ARM: OMAP: Port dmtimers to OMAP2 and implement PWM support
authorTimo Teras <timo.teras@solidboot.com>
Mon, 26 Jun 2006 23:16:12 +0000 (16:16 -0700)
committerTony Lindgren <tony@atomide.com>
Mon, 26 Jun 2006 23:16:12 +0000 (16:16 -0700)
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>
arch/arm/mach-omap2/Kconfig
arch/arm/mach-omap2/timer-gp.c
arch/arm/plat-omap/Kconfig
arch/arm/plat-omap/dmtimer.c
arch/arm/plat-omap/timer32k.c
include/asm-arm/arch-omap/dmtimer.h
include/asm-arm/arch-omap/irqs.h

index 537dd2e..aab97cc 100644 (file)
@@ -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
index 1d2f5ac..3358c0d 100644 (file)
@@ -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:
  *
 #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,
 };
-
index ec49495..ec752e1 100644 (file)
@@ -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.
 
index eba3cb5..c25a1a6 100644 (file)
@@ -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
  */
 
 #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 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);
index 3461a6c..f028e18 100644 (file)
@@ -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();
 }
 
index e6522e6..5b58e3d 100644 (file)
@@ -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
  * 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 */
index 42098d9..46f2f06 100644 (file)
 #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