* OMAP Dual-Mode Timers
*
* Copyright (C) 2005 Nokia Corporation
- * OMAP2 support by Juha Yrjola
- * API improvements and OMAP2 clock framework support by Timo Teras
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
*/
#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/list.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
#include <asm/hardware.h>
#include <asm/arch/dmtimer.h>
#include <asm/io.h>
#include <asm/arch/irqs.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#define OMAP_TIMER_COUNT 8
-/* register offsets */
#define OMAP_TIMER_ID_REG 0x00
#define OMAP_TIMER_OCP_CFG_REG 0x10
#define OMAP_TIMER_SYS_STAT_REG 0x14
#define OMAP_TIMER_CAPTURE_REG 0x3c
#define OMAP_TIMER_IF_CTRL_REG 0x40
-/* timer control reg bits */
-#define OMAP_TIMER_CTRL_GPOCFG (1 << 14)
-#define OMAP_TIMER_CTRL_CAPTMODE (1 << 13)
-#define OMAP_TIMER_CTRL_PT (1 << 12)
-#define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8)
-#define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8)
-#define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8)
-#define OMAP_TIMER_CTRL_SCPWM (1 << 7)
-#define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */
-#define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */
-#define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* how much to shift the prescaler value */
-#define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */
-#define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */
-
-struct omap_dm_timer {
- unsigned long phys_base;
- int irq;
-#ifdef CONFIG_ARCH_OMAP2
- struct clk *iclk, *fclk;
-#endif
- void __iomem *io_base;
- unsigned reserved:1;
-};
-#ifdef CONFIG_ARCH_OMAP1
+static struct dmtimer_info_struct {
+ struct list_head unused_timers;
+ struct list_head reserved_timers;
+} dm_timer_info;
static struct omap_dm_timer dm_timers[] = {
- { .phys_base = 0xfffb1400, .irq = INT_1610_GPTIMER1 },
- { .phys_base = 0xfffb1c00, .irq = INT_1610_GPTIMER2 },
- { .phys_base = 0xfffb2400, .irq = INT_1610_GPTIMER3 },
- { .phys_base = 0xfffb2c00, .irq = INT_1610_GPTIMER4 },
- { .phys_base = 0xfffb3400, .irq = INT_1610_GPTIMER5 },
- { .phys_base = 0xfffb3c00, .irq = INT_1610_GPTIMER6 },
- { .phys_base = 0xfffb4400, .irq = INT_1610_GPTIMER7 },
- { .phys_base = 0xfffb4c00, .irq = INT_1610_GPTIMER8 },
+ { .base=0xfffb1400, .irq=INT_1610_GPTIMER1 },
+ { .base=0xfffb1c00, .irq=INT_1610_GPTIMER2 },
+ { .base=0xfffb2400, .irq=INT_1610_GPTIMER3 },
+ { .base=0xfffb2c00, .irq=INT_1610_GPTIMER4 },
+ { .base=0xfffb3400, .irq=INT_1610_GPTIMER5 },
+ { .base=0xfffb3c00, .irq=INT_1610_GPTIMER6 },
+ { .base=0xfffb4400, .irq=INT_1610_GPTIMER7 },
+ { .base=0xfffb4c00, .irq=INT_1610_GPTIMER8 },
+ { .base=0x0 },
};
-#elif defined(CONFIG_ARCH_OMAP2)
-static struct omap_dm_timer dm_timers[] = {
- { .phys_base = 0x48028000, .irq = INT_24XX_GPTIMER1 },
- { .phys_base = 0x4802a000, .irq = INT_24XX_GPTIMER2 },
- { .phys_base = 0x48078000, .irq = INT_24XX_GPTIMER3 },
- { .phys_base = 0x4807a000, .irq = INT_24XX_GPTIMER4 },
- { .phys_base = 0x4807c000, .irq = INT_24XX_GPTIMER5 },
- { .phys_base = 0x4807e000, .irq = INT_24XX_GPTIMER6 },
- { .phys_base = 0x48080000, .irq = INT_24XX_GPTIMER7 },
- { .phys_base = 0x48082000, .irq = INT_24XX_GPTIMER8 },
- { .phys_base = 0x48084000, .irq = INT_24XX_GPTIMER9 },
- { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 },
- { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 },
- { .phys_base = 0x4808a000, .irq = INT_24XX_GPTIMER12 },
-};
-
-static const char *dm_source_names[] = {
- "sys_ck",
- "func_32k_ck",
- "alt_ck"
-};
-
-static struct clk *dm_source_clocks[3];
-
-#else
-
-#error OMAP architecture not supported!
-
-#endif
-
-static const int dm_timer_count = ARRAY_SIZE(dm_timers);
static spinlock_t dm_timer_lock;
-static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg)
-{
- return readl(timer->io_base + reg);
-}
-static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value)
+inline void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value)
{
- writel(value, timer->io_base + reg);
+ omap_writel(value, timer->base + reg);
while (omap_dm_timer_read_reg(timer, OMAP_TIMER_WRITE_PEND_REG))
;
}
-static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
+u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg)
{
- int c;
-
- c = 0;
- while (!(omap_dm_timer_read_reg(timer, OMAP_TIMER_SYS_STAT_REG) & 1)) {
- c++;
- if (c > 100000) {
- printk(KERN_ERR "Timer failed to reset\n");
- return;
- }
- }
+ return omap_readl(timer->base + reg);
}
-static void omap_dm_timer_reset(struct omap_dm_timer *timer)
+int omap_dm_timers_active(void)
{
- u32 l;
+ struct omap_dm_timer *timer;
- if (timer != &dm_timers[0]) {
- omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
- omap_dm_timer_wait_for_reset(timer);
- }
- omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_SYS_CLK);
+ for (timer = &dm_timers[0]; timer->base; ++timer)
+ if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
+ OMAP_TIMER_CTRL_ST)
+ return 1;
- /* Set to smart-idle mode */
- l = omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG);
- l |= 0x02 << 3;
- omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l);
+ return 0;
}
-static void omap_dm_timer_prepare(struct omap_dm_timer *timer)
-{
-#ifdef CONFIG_ARCH_OMAP2
- clk_enable(timer->iclk);
- clk_enable(timer->fclk);
-#endif
- omap_dm_timer_reset(timer);
-}
-struct omap_dm_timer *omap_dm_timer_request(void)
+void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
{
- struct omap_dm_timer *timer = NULL;
- unsigned long flags;
- int i;
+ int n = (timer - dm_timers) << 1;
+ u32 l;
- spin_lock_irqsave(&dm_timer_lock, flags);
- for (i = 0; i < dm_timer_count; i++) {
- if (dm_timers[i].reserved)
- continue;
+ l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n);
+ l |= source << n;
+ omap_writel(l, MOD_CONF_CTRL_1);
+}
- timer = &dm_timers[i];
- timer->reserved = 1;
- break;
- }
- spin_unlock_irqrestore(&dm_timer_lock, flags);
- if (timer != NULL)
- omap_dm_timer_prepare(timer);
+static void omap_dm_timer_reset(struct omap_dm_timer *timer)
+{
+ /* Reset and set posted mode */
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, 0x02);
- return timer;
+ omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_ARMXOR);
}
-struct omap_dm_timer *omap_dm_timer_request_specific(int id)
+
+
+struct omap_dm_timer * omap_dm_timer_request(void)
{
- struct omap_dm_timer *timer;
+ struct omap_dm_timer *timer = NULL;
unsigned long flags;
spin_lock_irqsave(&dm_timer_lock, flags);
- if (id <= 0 || id > dm_timer_count || dm_timers[id-1].reserved) {
- spin_unlock_irqrestore(&dm_timer_lock, flags);
- printk("BUG: warning at %s:%d/%s(): unable to get timer %d\n",
- __FILE__, __LINE__, __FUNCTION__, id);
- dump_stack();
- return NULL;
+ if (!list_empty(&dm_timer_info.unused_timers)) {
+ timer = (struct omap_dm_timer *)
+ dm_timer_info.unused_timers.next;
+ list_move_tail((struct list_head *)timer,
+ &dm_timer_info.reserved_timers);
}
-
- timer = &dm_timers[id-1];
- timer->reserved = 1;
spin_unlock_irqrestore(&dm_timer_lock, flags);
- omap_dm_timer_prepare(timer);
-
return timer;
}
-void omap_dm_timer_free(struct omap_dm_timer *timer)
-{
- omap_dm_timer_reset(timer);
-#ifdef CONFIG_ARCH_OMAP2
- clk_disable(timer->iclk);
- clk_disable(timer->fclk);
-#endif
- WARN_ON(!timer->reserved);
- timer->reserved = 0;
-}
-int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
-{
- return timer->irq;
-}
-
-#if defined(CONFIG_ARCH_OMAP1)
-
-struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
-{
- BUG();
-}
-
-/**
- * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
- * @inputmask: current value of idlect mask
- */
-__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
+void omap_dm_timer_free(struct omap_dm_timer *timer)
{
- int i;
-
- /* If ARMXOR cannot be idled this function call is unnecessary */
- if (!(inputmask & (1 << 1)))
- return inputmask;
-
- /* If any active timer is using ARMXOR return modified mask */
- for (i = 0; i < dm_timer_count; i++) {
- u32 l;
-
- l = omap_dm_timer_read_reg(&dm_timers[i], OMAP_TIMER_CTRL_REG);
- if (l & OMAP_TIMER_CTRL_ST) {
- if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
- inputmask &= ~(1 << 1);
- else
- inputmask &= ~(1 << 2);
- }
- }
-
- return inputmask;
-}
+ unsigned long flags;
-#elif defined(CONFIG_ARCH_OMAP2)
+ omap_dm_timer_reset(timer);
-struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
-{
- return timer->fclk;
+ spin_lock_irqsave(&dm_timer_lock, flags);
+ list_move_tail((struct list_head *)timer, &dm_timer_info.unused_timers);
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
}
-__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
+void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
+ unsigned int value)
{
- BUG();
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value);
}
-#endif
-
-void omap_dm_timer_trigger(struct omap_dm_timer *timer)
+unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
+ return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
}
-void omap_dm_timer_start(struct omap_dm_timer *timer)
+void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
{
- u32 l;
-
- l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- if (!(l & OMAP_TIMER_CTRL_ST)) {
- l |= OMAP_TIMER_CTRL_ST;
- omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
- }
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value);
}
-void omap_dm_timer_stop(struct omap_dm_timer *timer)
+void omap_dm_timer_enable_autoreload(struct omap_dm_timer *timer)
{
u32 l;
-
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- if (l & OMAP_TIMER_CTRL_ST) {
- l &= ~0x1;
- omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
- }
-}
-
-#ifdef CONFIG_ARCH_OMAP1
-
-void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
-{
- int n = (timer - dm_timers) << 1;
- u32 l;
-
- l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n);
- l |= source << n;
- omap_writel(l, MOD_CONF_CTRL_1);
+ l |= OMAP_TIMER_CTRL_AR;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
}
-#else
-
-void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
+void omap_dm_timer_trigger(struct omap_dm_timer *timer)
{
- if (source < 0 || source >= 3)
- return;
-
- clk_disable(timer->fclk);
- clk_set_parent(timer->fclk, dm_source_clocks[source]);
- clk_enable(timer->fclk);
-
- /* When the functional clock disappears, too quick writes seem to
- * cause an abort. */
- __delay(15000);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 1);
}
-#endif
-
-void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
- unsigned int load)
+void omap_dm_timer_set_trigger(struct omap_dm_timer *timer, unsigned int value)
{
u32 l;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- if (autoreload)
- l |= OMAP_TIMER_CTRL_AR;
- else
- l &= ~OMAP_TIMER_CTRL_AR;
+ l |= value & 0x3;
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
}
-void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
- unsigned int match)
+void omap_dm_timer_start(struct omap_dm_timer *timer)
{
u32 l;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- if (enable)
- l |= OMAP_TIMER_CTRL_CE;
- else
- l &= ~OMAP_TIMER_CTRL_CE;
+ l |= OMAP_TIMER_CTRL_ST;
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
}
-
-void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
- int toggle, int trigger)
+void omap_dm_timer_stop(struct omap_dm_timer *timer)
{
u32 l;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
- OMAP_TIMER_CTRL_PT | (0x03 << 10));
- if (def_on)
- l |= OMAP_TIMER_CTRL_SCPWM;
- if (toggle)
- l |= OMAP_TIMER_CTRL_PT;
- l |= trigger << 10;
+ l &= ~0x1;
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
}
-void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
+unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
{
- u32 l;
-
- l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
- if (prescaler >= 0x00 && prescaler <= 0x07) {
- l |= OMAP_TIMER_CTRL_PRE;
- l |= prescaler << 2;
- }
- omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
+ return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG);
}
-void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
- unsigned int value)
+void omap_dm_timer_reset_counter(struct omap_dm_timer *timer)
{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, 0);
}
-unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
+void omap_dm_timer_set_load(struct omap_dm_timer *timer, unsigned int load)
{
- return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
}
-void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
+void omap_dm_timer_set_match(struct omap_dm_timer *timer, unsigned int match)
{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
}
-unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
+void omap_dm_timer_enable_compare(struct omap_dm_timer *timer)
{
- return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG);
-}
+ u32 l;
-void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
-{
- return omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ l |= OMAP_TIMER_CTRL_CE;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
}
-int omap_dm_timers_active(void)
-{
- int i;
-
- for (i = 0; i < dm_timer_count; i++) {
- struct omap_dm_timer *timer;
-
- timer = &dm_timers[i];
- if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
- OMAP_TIMER_CTRL_ST)
- return 1;
- }
- return 0;
-}
-int omap_dm_timer_init(void)
+static inline void __dm_timer_init(void)
{
struct omap_dm_timer *timer;
- int i;
-
- if (!(cpu_is_omap16xx() || cpu_is_omap24xx()))
- return -ENODEV;
spin_lock_init(&dm_timer_lock);
-#ifdef CONFIG_ARCH_OMAP2
- for (i = 0; i < ARRAY_SIZE(dm_source_names); i++) {
- dm_source_clocks[i] = clk_get(NULL, dm_source_names[i]);
- BUG_ON(dm_source_clocks[i] == NULL);
- }
-#endif
-
- for (i = 0; i < dm_timer_count; i++) {
-#ifdef CONFIG_ARCH_OMAP2
- char clk_name[16];
-#endif
-
- timer = &dm_timers[i];
- timer->io_base = (void __iomem *) io_p2v(timer->phys_base);
-#ifdef CONFIG_ARCH_OMAP2
- sprintf(clk_name, "gpt%d_ick", i + 1);
- timer->iclk = clk_get(NULL, clk_name);
- sprintf(clk_name, "gpt%d_fck", i + 1);
- timer->fclk = clk_get(NULL, clk_name);
-#endif
+ INIT_LIST_HEAD(&dm_timer_info.unused_timers);
+ INIT_LIST_HEAD(&dm_timer_info.reserved_timers);
+
+ timer = &dm_timers[0];
+ while (timer->base) {
+ list_add_tail((struct list_head *)timer, &dm_timer_info.unused_timers);
+ omap_dm_timer_reset(timer);
+ timer++;
}
+}
+static int __init omap_dm_timer_init(void)
+{
+ if (cpu_is_omap16xx())
+ __dm_timer_init();
return 0;
}
+
+arch_initcall(omap_dm_timer_init);