linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / arm / plat-omap / dmtimer.c
index 5052443..38d7ebf 100644 (file)
@@ -4,8 +4,7 @@
  * 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);