aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Teras <timo.teras@solidboot.com>2006-06-26 16:16:12 -0700
committerTony Lindgren <tony@atomide.com>2006-06-26 16:16:12 -0700
commit77900a2fc3bfb1eb6eaa6d43eef4591e1f7c600d (patch)
tree6d6278f833a6057b1b951e4e9bded234aa6459e2
parent6e711ec6d249c0d3f8bc225066738d064ed90032 (diff)
ARM: OMAP: Port dmtimers to OMAP2 and implement PWM support
Port dmtimer framework to OMAP2. Modify the dmtimers API to support setting of PWM configuration and prescaler. Convert 32 kHz timer and GP timer to use the dmtimer framework. Signed-off-by: Timo Teras <timo.teras@solidboot.com> Signed-off-by: Juha Yrjola <juha.yrjola@solidboot.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
-rw-r--r--arch/arm/mach-omap2/Kconfig1
-rw-r--r--arch/arm/mach-omap2/timer-gp.c86
-rw-r--r--arch/arm/plat-omap/Kconfig2
-rw-r--r--arch/arm/plat-omap/dmtimer.c413
-rw-r--r--arch/arm/plat-omap/timer32k.c121
-rw-r--r--include/asm-arm/arch-omap/dmtimer.h79
-rw-r--r--include/asm-arm/arch-omap/irqs.h12
7 files changed, 412 insertions, 302 deletions
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 537dd2e6d38..aab97ccf1e6 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -8,6 +8,7 @@ config ARCH_OMAP24XX
config ARCH_OMAP2420
bool "OMAP2420 support"
depends on ARCH_OMAP24XX
+ select OMAP_DM_TIMER
comment "OMAP Board Type"
depends on ARCH_OMAP2
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac2f69..3358c0d47b1 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -6,6 +6,7 @@
* Copyright (C) 2005 Nokia Corporation
* Author: Paul Mundt <paul.mundt@nokia.com>
* Juha Yrjölä <juha.yrjola@nokia.com>
+ * OMAP Dual-mode timer framework support by Timo Teras
*
* Some parts based off of TI's 24xx code:
*
@@ -22,54 +23,18 @@
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#include <asm/mach/time.h>
-#include <asm/delay.h>
-#include <asm/io.h>
+#include <asm/arch/dmtimer.h>
-#define OMAP2_GP_TIMER1_BASE 0x48028000
-#define OMAP2_GP_TIMER2_BASE 0x4802a000
-#define OMAP2_GP_TIMER3_BASE 0x48078000
-#define OMAP2_GP_TIMER4_BASE 0x4807a000
+static struct omap_dm_timer *gptimer;
-#define GP_TIMER_TIDR 0x00
-#define GP_TIMER_TISR 0x18
-#define GP_TIMER_TIER 0x1c
-#define GP_TIMER_TCLR 0x24
-#define GP_TIMER_TCRR 0x28
-#define GP_TIMER_TLDR 0x2c
-#define GP_TIMER_TSICR 0x40
-
-#define OS_TIMER_NR 1 /* GP timer 2 */
-
-static unsigned long timer_base[] = {
- IO_ADDRESS(OMAP2_GP_TIMER1_BASE),
- IO_ADDRESS(OMAP2_GP_TIMER2_BASE),
- IO_ADDRESS(OMAP2_GP_TIMER3_BASE),
- IO_ADDRESS(OMAP2_GP_TIMER4_BASE),
-};
-
-static inline unsigned int timer_read_reg(int nr, unsigned int reg)
-{
- return __raw_readl(timer_base[nr] + reg);
-}
-
-static inline void timer_write_reg(int nr, unsigned int reg, unsigned int val)
-{
- __raw_writel(val, timer_base[nr] + reg);
-}
-
-/* Note that we always enable the clock prescale divider bit */
-static inline void omap2_gp_timer_start(int nr, unsigned long load_val)
+static inline void omap2_gp_timer_start(unsigned long load_val)
{
- unsigned int tmp;
-
- tmp = 0xffffffff - load_val;
-
- timer_write_reg(nr, GP_TIMER_TLDR, tmp);
- timer_write_reg(nr, GP_TIMER_TCRR, tmp);
- timer_write_reg(nr, GP_TIMER_TIER, 1 << 1);
- timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1);
+ omap_dm_timer_set_load(gptimer, 1, 0xffffffff - load_val);
+ omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_OVERFLOW);
+ omap_dm_timer_start(gptimer);
}
static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
@@ -77,7 +42,7 @@ static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
{
write_seqlock(&xtime_lock);
- timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1);
+ omap_dm_timer_write_status(gptimer, OMAP_TIMER_INT_OVERFLOW);
timer_tick(regs);
write_sequnlock(&xtime_lock);
@@ -87,41 +52,26 @@ static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
static struct irqaction omap2_gp_timer_irq = {
.name = "gp timer",
- .flags = SA_INTERRUPT,
+ .flags = SA_INTERRUPT | SA_TIMER,
.handler = omap2_gp_timer_interrupt,
};
static void __init omap2_gp_timer_init(void)
{
- struct clk * sys_ck;
- u32 tick_period = 120000;
- u32 l;
+ u32 tick_period;
- /* Reset clock and prescale value */
- timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+ omap_dm_timer_init();
+ gptimer = omap_dm_timer_request_specific(2);
+ BUG_ON(gptimer == NULL);
- sys_ck = clk_get(NULL, "sys_ck");
- if (IS_ERR(sys_ck))
- printk(KERN_ERR "Could not get sys_ck\n");
- else {
- clk_enable(sys_ck);
- tick_period = clk_get_rate(sys_ck) / 100;
- clk_put(sys_ck);
- }
-
- tick_period /= 2; /* Minimum prescale divider is 2 */
+ omap_dm_timer_set_source(gptimer, OMAP_TIMER_SRC_SYS_CLK);
+ tick_period = clk_get_rate(omap_dm_timer_get_fclk(gptimer)) / 100;
tick_period -= 1;
- l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
- printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
- (l >> 4) & 0x0f, l & 0x0f);
-
- setup_irq(38, &omap2_gp_timer_irq);
-
- omap2_gp_timer_start(OS_TIMER_NR, tick_period);
+ setup_irq(omap_dm_timer_get_irq(gptimer), &omap2_gp_timer_irq);
+ omap2_gp_timer_start(tick_period);
}
struct sys_timer omap_timer = {
.init = omap2_gp_timer_init,
};
-
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index ec49495e651..ec752e16d61 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -91,7 +91,7 @@ config OMAP_32K_TIMER_HZ
config OMAP_DM_TIMER
bool "Use dual-mode timer"
- depends on ARCH_OMAP16XX
+ depends on ARCH_OMAP16XX || ARCH_OMAP24XX
help
Select this option if you want to use OMAP Dual-Mode timers.
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index eba3cb52ad8..c25a1a6d2b0 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -4,7 +4,8 @@
* OMAP Dual-Mode Timers
*
* Copyright (C) 2005 Nokia Corporation
- * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ * OMAP2 support by Juha Yrjola
+ * API improvements and OMAP2 clock framework support by Timo Teras
*
* 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
@@ -26,15 +27,17 @@
*/
#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
@@ -50,52 +53,184 @@
#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 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 },
+};
-static struct dmtimer_info_struct {
- struct list_head unused_timers;
- struct list_head reserved_timers;
-} dm_timer_info;
+#elif defined(CONFIG_ARCH_OMAP2)
static struct omap_dm_timer dm_timers[] = {
- { .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 },
+ { .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 },
};
+#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);
+}
-inline void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value)
+static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value)
{
- omap_writel(value, timer->base + reg);
+ writel(value, timer->io_base + reg);
while (omap_dm_timer_read_reg(timer, OMAP_TIMER_WRITE_PEND_REG))
;
}
-u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg)
+static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
{
- return omap_readl(timer->base + 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;
+ }
+ }
}
-int omap_dm_timers_active(void)
+static void omap_dm_timer_reset(struct omap_dm_timer *timer)
+{
+ u32 l;
+
+ 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);
+
+ /* 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);
+}
+
+static void omap_dm_timer_reserve(struct omap_dm_timer *timer)
+{
+ timer->reserved = 1;
+#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)
+{
+ struct omap_dm_timer *timer = NULL;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&dm_timer_lock, flags);
+ for (i = 0; i < dm_timer_count; i++) {
+ if (dm_timers[i].reserved)
+ continue;
+
+ timer = &dm_timers[i];
+ omap_dm_timer_reserve(timer);
+ break;
+ }
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
+
+ return timer;
+}
+
+struct omap_dm_timer *omap_dm_timer_request_specific(int id)
{
struct omap_dm_timer *timer;
+ unsigned long flags;
- 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;
+ 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;
+ }
- return 0;
+ timer = &dm_timers[id-1];
+ omap_dm_timer_reserve(timer);
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
+
+ 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
@@ -103,184 +238,226 @@ int omap_dm_timers_active(void)
*/
__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
{
- int n;
+ 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 (n = 0; dm_timers[n].base; ++n)
- if (omap_dm_timer_read_reg(&dm_timers[n], OMAP_TIMER_CTRL_REG)&
- OMAP_TIMER_CTRL_ST) {
- if (((omap_readl(MOD_CONF_CTRL_1)>>(n*2)) & 0x03) == 0)
+ for (i = 0; i < dm_timer_count; i++) {
+ u32 l;
+
+ l = omap_dm_timer_read_reg(&dm_timers[n], 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;
}
+#elif defined(CONFIG_ARCH_OMAP2)
-void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
+struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
{
- int n = (timer - dm_timers) << 1;
- u32 l;
+ return timer->fclk;
+}
- l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n);
- l |= source << n;
- omap_writel(l, MOD_CONF_CTRL_1);
+__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
+{
+ BUG();
}
+#endif
-static void omap_dm_timer_reset(struct omap_dm_timer *timer)
+void omap_dm_timer_trigger(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);
-
- omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_ARMXOR);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
}
+void omap_dm_timer_start(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 |= OMAP_TIMER_CTRL_ST;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
+ }
+}
-struct omap_dm_timer * omap_dm_timer_request(void)
+void omap_dm_timer_stop(struct omap_dm_timer *timer)
{
- struct omap_dm_timer *timer = NULL;
- unsigned long flags;
+ u32 l;
- spin_lock_irqsave(&dm_timer_lock, flags);
- 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);
+ 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);
}
- spin_unlock_irqrestore(&dm_timer_lock, flags);
-
- return timer;
}
+#ifdef CONFIG_ARCH_OMAP1
-void omap_dm_timer_free(struct omap_dm_timer *timer)
+void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
{
- unsigned long flags;
-
- omap_dm_timer_reset(timer);
+ int n = (timer - dm_timers) << 1;
+ u32 l;
- 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);
+ l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n);
+ l |= source << n;
+ omap_writel(l, MOD_CONF_CTRL_1);
}
-void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
- unsigned int value)
-{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value);
-}
+#else
-unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
+void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
{
- return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
+ static const char *source_timers[] = {
+ "sys_ck",
+ "func_32k_ck",
+ "alt_ck"
+ };
+ struct clk *parent;
+
+ if (source < 0 || source >= 3)
+ return;
+
+ parent = clk_get(NULL, source_timers[source]);
+ clk_disable(timer->fclk);
+ clk_set_parent(timer->fclk, parent);
+ clk_enable(timer->fclk);
+ clk_put(parent);
+
+ /* When the functional clock disappears, too quick writes seem to
+ * cause an abort. */
+ udelay(50);
}
-void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
-{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value);
-}
+#endif
-void omap_dm_timer_enable_autoreload(struct omap_dm_timer *timer)
+void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
+ unsigned int load)
{
u32 l;
+
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- l |= OMAP_TIMER_CTRL_AR;
+ if (autoreload)
+ l |= OMAP_TIMER_CTRL_AR;
+ else
+ l &= ~OMAP_TIMER_CTRL_AR;
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_trigger(struct omap_dm_timer *timer)
-{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 1);
-}
-
-void omap_dm_timer_set_trigger(struct omap_dm_timer *timer, unsigned int value)
+void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
+ unsigned int match)
{
u32 l;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- l |= value & 0x3;
+ if (enable)
+ l |= OMAP_TIMER_CTRL_CE;
+ else
+ l &= ~OMAP_TIMER_CTRL_CE;
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_start(struct omap_dm_timer *timer)
+
+void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
+ int toggle, int trigger)
{
u32 l;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- l |= OMAP_TIMER_CTRL_ST;
+ 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;
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
}
-void omap_dm_timer_stop(struct omap_dm_timer *timer)
+void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
{
u32 l;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- l &= ~0x1;
+ 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);
}
-unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
+void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
+ unsigned int value)
{
- return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value);
}
-void omap_dm_timer_reset_counter(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_COUNTER_REG, 0);
+ return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
}
-void omap_dm_timer_set_load(struct omap_dm_timer *timer, unsigned int load)
+void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value);
}
-void omap_dm_timer_set_match(struct omap_dm_timer *timer, unsigned int match)
+unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
+ return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG);
}
-void omap_dm_timer_enable_compare(struct omap_dm_timer *timer)
+int omap_dm_timers_active(void)
{
- u32 l;
+ int i;
- 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);
-}
+ 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;
+}
-static inline void __dm_timer_init(void)
+int omap_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);
- 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++;
+ 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
}
-}
-static int __init omap_dm_timer_init(void)
-{
- if (cpu_is_omap16xx())
- __dm_timer_init();
return 0;
}
-
-arch_initcall(omap_dm_timer_init);
diff --git a/arch/arm/plat-omap/timer32k.c b/arch/arm/plat-omap/timer32k.c
index 3461a6c9665..f028e182215 100644
--- a/arch/arm/plat-omap/timer32k.c
+++ b/arch/arm/plat-omap/timer32k.c
@@ -7,6 +7,7 @@
* Partial timer rewrite and additional dynamic tick timer support by
* Tony Lindgen <tony@atomide.com> and
* Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
+ * OMAP Dual-mode timer framework support by Timo Teras
*
* MPU timer code based on the older MPU timer code for OMAP
* Copyright (C) 2000 RidgeRun, Inc.
@@ -79,18 +80,6 @@ struct sys_timer omap_timer;
#define OMAP1_32K_TIMER_TVR 0x00
#define OMAP1_32K_TIMER_TCR 0x04
-/* 24xx specific defines */
-#define OMAP2_GP_TIMER_BASE 0x48028000
-#define CM_CLKSEL_WKUP 0x48008440
-#define GP_TIMER_TIDR 0x00
-#define GP_TIMER_TISR 0x18
-#define GP_TIMER_TIER 0x1c
-#define GP_TIMER_TCLR 0x24
-#define GP_TIMER_TCRR 0x28
-#define GP_TIMER_TLDR 0x2c
-#define GP_TIMER_TTGR 0x30
-#define GP_TIMER_TSICR 0x40
-
#define OMAP_32K_TICKS_PER_HZ (32768 / HZ)
/*
@@ -102,54 +91,64 @@ struct sys_timer omap_timer;
#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \
(((nr_jiffies) * (clock_rate)) / HZ)
+#if defined(CONFIG_ARCH_OMAP1)
+
static inline void omap_32k_timer_write(int val, int reg)
{
- if (cpu_class_is_omap1())
- omap_writew(val, OMAP1_32K_TIMER_BASE + reg);
-
- if (cpu_is_omap24xx())
- omap_writel(val, OMAP2_GP_TIMER_BASE + reg);
+ omap_writew(val, OMAP1_32K_TIMER_BASE + reg);
}
static inline unsigned long omap_32k_timer_read(int reg)
{
- if (cpu_class_is_omap1())
- return omap_readl(OMAP1_32K_TIMER_BASE + reg) & 0xffffff;
+ return omap_readl(OMAP1_32K_TIMER_BASE + reg) & 0xffffff;
+}
- if (cpu_is_omap24xx())
- return omap_readl(OMAP2_GP_TIMER_BASE + reg);
+static inline void omap_32k_timer_start(unsigned long load_val)
+{
+ omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR);
+ omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR);
}
-/*
- * The 32KHz synchronized timer is an additional timer on 16xx.
- * It is always running.
- */
-static inline unsigned long omap_32k_sync_timer_read(void)
+static inline void omap_32k_timer_stop(void)
{
- return omap_readl(TIMER_32K_SYNCHRONIZED);
+ omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR);
}
+#define omap_32k_timer_ack_irq()
+
+#elif defined(CONFIG_ARCH_OMAP2)
+
+#include <asm/arch/dmtimer.h>
+
+static struct omap_dm_timer *gptimer;
+
static inline void omap_32k_timer_start(unsigned long load_val)
{
- if (cpu_class_is_omap1()) {
- omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR);
- omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR);
- }
-
- if (cpu_is_omap24xx()) {
- omap_32k_timer_write(0xffffffff - load_val, GP_TIMER_TCRR);
- omap_32k_timer_write((1 << 1), GP_TIMER_TIER);
- omap_32k_timer_write((1 << 1) | 1, GP_TIMER_TCLR);
- }
+ omap_dm_timer_set_load(gptimer, 1, 0xffffffff - load_val);
+ omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_OVERFLOW);
+ omap_dm_timer_start(gptimer);
}
static inline void omap_32k_timer_stop(void)
{
- if (cpu_class_is_omap1())
- omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR);
+ omap_dm_timer_stop(gptimer);
+}
- if (cpu_is_omap24xx())
- omap_32k_timer_write(0x0, GP_TIMER_TCLR);
+static inline void omap_32k_timer_ack_irq(void)
+{
+ u32 status = omap_dm_timer_read_status(gptimer);
+ omap_dm_timer_write_status(gptimer, status);
+}
+
+#endif
+
+/*
+ * The 32KHz synchronized timer is an additional timer on 16xx.
+ * It is always running.
+ */
+static inline unsigned long omap_32k_sync_timer_read(void)
+{
+ return omap_readl(TIMER_32K_SYNCHRONIZED);
}
/*
@@ -203,11 +202,7 @@ static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id,
write_seqlock_irqsave(&xtime_lock, flags);
- if (cpu_is_omap24xx()) {
- u32 status = omap_32k_timer_read(GP_TIMER_TISR);
- omap_32k_timer_write(status, GP_TIMER_TISR);
- }
-
+ omap_32k_timer_ack_irq();
now = omap_32k_sync_timer_read();
while ((signed long)(now - omap_32k_last_tick)
@@ -269,9 +264,6 @@ static struct irqaction omap_32k_timer_irq = {
.handler = omap_32k_timer_interrupt,
};
-static struct clk * gpt1_ick;
-static struct clk * gpt1_fck;
-
static __init void omap_init_32k_timer(void)
{
#ifdef CONFIG_NO_IDLE_HZ
@@ -280,31 +272,19 @@ static __init void omap_init_32k_timer(void)
if (cpu_class_is_omap1())
setup_irq(INT_OS_TIMER, &omap_32k_timer_irq);
- if (cpu_is_omap24xx())
- setup_irq(37, &omap_32k_timer_irq);
omap_timer.offset = omap_32k_timer_gettimeoffset;
omap_32k_last_tick = omap_32k_sync_timer_read();
/* REVISIT: Check 24xx TIOCP_CFG settings after idle works */
if (cpu_is_omap24xx()) {
- omap_32k_timer_write(0, GP_TIMER_TCLR);
- omap_writel(0, CM_CLKSEL_WKUP); /* 32KHz clock source */
-
- gpt1_ick = clk_get(NULL, "gpt1_ick");
- if (IS_ERR(gpt1_ick))
- printk(KERN_ERR "Could not get gpt1_ick\n");
- else
- clk_enable(gpt1_ick);
-
- gpt1_fck = clk_get(NULL, "gpt1_fck");
- if (IS_ERR(gpt1_fck))
- printk(KERN_ERR "Could not get gpt1_fck\n");
- else
- clk_enable(gpt1_fck);
-
- mdelay(100); /* Wait for clocks to stabilize */
-
- omap_32k_timer_write(0x7, GP_TIMER_TISR);
+ gptimer = omap_dm_timer_request_specific(1);
+ BUG_ON(gptimer == NULL);
+
+ omap_dm_timer_set_source(gptimer, OMAP_TIMER_SRC_32_KHZ);
+ setup_irq(omap_dm_timer_get_irq(gptimer), &omap_32k_timer_irq);
+ omap_dm_timer_set_int_enable(gptimer,
+ OMAP_TIMER_INT_CAPTURE | OMAP_TIMER_INT_OVERFLOW |
+ OMAP_TIMER_INT_MATCH);
}
omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD);
@@ -317,6 +297,9 @@ static __init void omap_init_32k_timer(void)
*/
static void __init omap_timer_init(void)
{
+#ifdef CONFIG_OMAP_DM_TIMER
+ omap_dm_timer_init();
+#endif
omap_init_32k_timer();
}
diff --git a/include/asm-arm/arch-omap/dmtimer.h b/include/asm-arm/arch-omap/dmtimer.h
index e6522e6a383..5b58e3d6274 100644
--- a/include/asm-arm/arch-omap/dmtimer.h
+++ b/include/asm-arm/arch-omap/dmtimer.h
@@ -5,6 +5,7 @@
*
* Copyright (C) 2005 Nokia Corporation
* Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ * PWM and clock framwork support by Timo Teras.
*
* 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
@@ -25,69 +26,55 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifndef __ASM_ARCH_TIMER_H
-#define __ASM_ARCH_TIMER_H
-
-#include <linux/list.h>
-
-#define OMAP_TIMER_SRC_ARMXOR 0x00
-#define OMAP_TIMER_SRC_32_KHZ 0x01
-#define OMAP_TIMER_SRC_EXT_CLK 0x02
-
-/* timer control reg bits */
-#define OMAP_TIMER_CTRL_CAPTMODE (1 << 13)
-#define OMAP_TIMER_CTRL_PT (1 << 12)
-#define OMAP_TIMER_CTRL_TRG_OVERFLOW (0x1 << 10)
-#define OMAP_TIMER_CTRL_TRG_OFANDMATCH (0x2 << 10)
-#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 */
+#ifndef __ASM_ARCH_DMTIMER_H
+#define __ASM_ARCH_DMTIMER_H
-/* timer interrupt enable bits */
-#define OMAP_TIMER_INT_CAPTURE (1 << 2)
-#define OMAP_TIMER_INT_OVERFLOW (1 << 1)
-#define OMAP_TIMER_INT_MATCH (1 << 0)
+/* clock sources */
+#define OMAP_TIMER_SRC_SYS_CLK 0x00
+#define OMAP_TIMER_SRC_32_KHZ 0x01
+#define OMAP_TIMER_SRC_EXT_CLK 0x02
+/* timer interrupt enable bits */
+#define OMAP_TIMER_INT_CAPTURE (1 << 2)
+#define OMAP_TIMER_INT_OVERFLOW (1 << 1)
+#define OMAP_TIMER_INT_MATCH (1 << 0)
-struct omap_dm_timer {
- struct list_head timer_list;
+/* trigger types */
+#define OMAP_TIMER_TRIGGER_NONE 0x00
+#define OMAP_TIMER_TRIGGER_OVERFLOW 0x01
+#define OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE 0x02
- u32 base;
- unsigned int irq;
-};
+struct omap_dm_timer;
+struct clk;
-u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg);
-void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value);
+int omap_dm_timer_init(void);
-struct omap_dm_timer * omap_dm_timer_request(void);
+struct omap_dm_timer *omap_dm_timer_request(void);
+struct omap_dm_timer *omap_dm_timer_request_specific(int timer_id);
void omap_dm_timer_free(struct omap_dm_timer *timer);
-void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source);
-void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, unsigned int value);
-void omap_dm_timer_set_trigger(struct omap_dm_timer *timer, unsigned int value);
-void omap_dm_timer_enable_compare(struct omap_dm_timer *timer);
-void omap_dm_timer_enable_autoreload(struct omap_dm_timer *timer);
+int omap_dm_timer_get_irq(struct omap_dm_timer *timer);
+
+u32 omap_dm_timer_modify_idlect_mask(u32 inputmask);
+struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer);
void omap_dm_timer_trigger(struct omap_dm_timer *timer);
void omap_dm_timer_start(struct omap_dm_timer *timer);
void omap_dm_timer_stop(struct omap_dm_timer *timer);
-void omap_dm_timer_set_load(struct omap_dm_timer *timer, unsigned int load);
-void omap_dm_timer_set_match(struct omap_dm_timer *timer, unsigned int match);
+void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source);
+void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, unsigned int value);
+void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, unsigned int match);
+void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, int toggle, int trigger);
+void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler);
+
+void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, unsigned int value);
unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer);
void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value);
-
unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer);
-void omap_dm_timer_reset_counter(struct omap_dm_timer *timer);
int omap_dm_timers_active(void);
-u32 omap_dm_timer_modify_idlect_mask(u32 inputmask);
-#endif /* __ASM_ARCH_TIMER_H */
+
+#endif /* __ASM_ARCH_DMTIMER_H */
diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h
index 42098d99f30..46f2f06eae0 100644
--- a/include/asm-arm/arch-omap/irqs.h
+++ b/include/asm-arm/arch-omap/irqs.h
@@ -242,6 +242,18 @@
#define INT_24XX_GPIO_BANK2 30
#define INT_24XX_GPIO_BANK3 31
#define INT_24XX_GPIO_BANK4 32
+#define INT_24XX_GPTIMER1 37
+#define INT_24XX_GPTIMER2 38
+#define INT_24XX_GPTIMER3 39
+#define INT_24XX_GPTIMER4 40
+#define INT_24XX_GPTIMER5 41
+#define INT_24XX_GPTIMER6 42
+#define INT_24XX_GPTIMER7 43
+#define INT_24XX_GPTIMER8 44
+#define INT_24XX_GPTIMER9 45
+#define INT_24XX_GPTIMER10 46
+#define INT_24XX_GPTIMER11 47
+#define INT_24XX_GPTIMER12 48
#define INT_24XX_MCBSP1_IRQ_TX 59
#define INT_24XX_MCBSP1_IRQ_RX 60
#define INT_24XX_MCBSP2_IRQ_TX 62