Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
[safe/jmp/linux-2.6] / drivers / clocksource / sh_cmt.c
index d607ac2..744f748 100644 (file)
@@ -18,7 +18,6 @@
  */
 
 #include <linux/init.h>
-#include <linux/bootmem.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
@@ -29,7 +28,8 @@
 #include <linux/err.h>
 #include <linux/clocksource.h>
 #include <linux/clockchips.h>
-#include <linux/sh_cmt.h>
+#include <linux/sh_timer.h>
+#include <linux/slab.h>
 
 struct sh_cmt_priv {
        void __iomem *mapbase;
@@ -60,7 +60,7 @@ static DEFINE_SPINLOCK(sh_cmt_lock);
 
 static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr)
 {
-       struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+       struct sh_timer_config *cfg = p->pdev->dev.platform_data;
        void __iomem *base = p->mapbase;
        unsigned long offs;
 
@@ -84,7 +84,7 @@ static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr)
 static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr,
                                unsigned long value)
 {
-       struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+       struct sh_timer_config *cfg = p->pdev->dev.platform_data;
        void __iomem *base = p->mapbase;
        unsigned long offs;
 
@@ -132,7 +132,7 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p,
 
 static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)
 {
-       struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+       struct sh_timer_config *cfg = p->pdev->dev.platform_data;
        unsigned long flags, value;
 
        /* start stop register shared by multiple timer channels */
@@ -150,7 +150,7 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)
 
 static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
 {
-       struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+       struct sh_timer_config *cfg = p->pdev->dev.platform_data;
        int ret;
 
        /* enable clock */
@@ -159,16 +159,18 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
                pr_err("sh_cmt: cannot enable clock \"%s\"\n", cfg->clk);
                return ret;
        }
-       *rate = clk_get_rate(p->clk) / 8;
 
        /* make sure channel is disabled */
        sh_cmt_start_stop_ch(p, 0);
 
        /* configure channel, periodic mode and maximum timeout */
-       if (p->width == 16)
-               sh_cmt_write(p, CMCSR, 0);
-       else
+       if (p->width == 16) {
+               *rate = clk_get_rate(p->clk) / 512;
+               sh_cmt_write(p, CMCSR, 0x43);
+       } else {
+               *rate = clk_get_rate(p->clk) / 8;
                sh_cmt_write(p, CMCSR, 0x01a4);
+       }
 
        sh_cmt_write(p, CMCOR, 0xffffffff);
        sh_cmt_write(p, CMCNT, 0);
@@ -183,6 +185,9 @@ static void sh_cmt_disable(struct sh_cmt_priv *p)
        /* disable channel */
        sh_cmt_start_stop_ch(p, 0);
 
+       /* disable interrupts in CMT block */
+       sh_cmt_write(p, CMCSR, 0);
+
        /* stop clock */
        clk_disable(p->clk);
 }
@@ -427,6 +432,11 @@ static void sh_cmt_clocksource_disable(struct clocksource *cs)
        sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
 }
 
+static void sh_cmt_clocksource_resume(struct clocksource *cs)
+{
+       sh_cmt_start(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
+}
+
 static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
                                       char *name, unsigned long rating)
 {
@@ -437,6 +447,8 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
        cs->read = sh_cmt_clocksource_read;
        cs->enable = sh_cmt_clocksource_enable;
        cs->disable = sh_cmt_clocksource_disable;
+       cs->suspend = sh_cmt_clocksource_disable;
+       cs->resume = sh_cmt_clocksource_resume;
        cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
        cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
        pr_info("sh_cmt: %s used as clock source\n", cs->name);
@@ -536,9 +548,9 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p,
        clockevents_register_device(ced);
 }
 
-int sh_cmt_register(struct sh_cmt_priv *p, char *name,
-                   unsigned long clockevent_rating,
-                   unsigned long clocksource_rating)
+static int sh_cmt_register(struct sh_cmt_priv *p, char *name,
+                          unsigned long clockevent_rating,
+                          unsigned long clocksource_rating)
 {
        if (p->width == (sizeof(p->max_match_value) * 8))
                p->max_match_value = ~0;
@@ -559,7 +571,7 @@ int sh_cmt_register(struct sh_cmt_priv *p, char *name,
 
 static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
 {
-       struct sh_cmt_config *cfg = pdev->dev.platform_data;
+       struct sh_timer_config *cfg = pdev->dev.platform_data;
        struct resource *res;
        int irq, ret;
        ret = -ENXIO;
@@ -598,46 +610,51 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
        p->irqaction.handler = sh_cmt_interrupt;
        p->irqaction.dev_id = p;
        p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
-       p->irqaction.mask = CPU_MASK_NONE;
-       ret = setup_irq(irq, &p->irqaction);
-       if (ret) {
-               pr_err("sh_cmt: failed to request irq %d\n", irq);
-               goto err1;
-       }
 
        /* get hold of clock */
        p->clk = clk_get(&p->pdev->dev, cfg->clk);
        if (IS_ERR(p->clk)) {
                pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk);
                ret = PTR_ERR(p->clk);
-               goto err2;
+               goto err1;
        }
 
        if (resource_size(res) == 6) {
                p->width = 16;
                p->overflow_bit = 0x80;
-               p->clear_bits = ~0xc0;
+               p->clear_bits = ~0x80;
        } else {
                p->width = 32;
                p->overflow_bit = 0x8000;
                p->clear_bits = ~0xc000;
        }
 
-       return sh_cmt_register(p, cfg->name,
-                              cfg->clockevent_rating,
-                              cfg->clocksource_rating);
- err2:
-       remove_irq(irq, &p->irqaction);
- err1:
+       ret = sh_cmt_register(p, cfg->name,
+                             cfg->clockevent_rating,
+                             cfg->clocksource_rating);
+       if (ret) {
+               pr_err("sh_cmt: registration failed\n");
+               goto err1;
+       }
+
+       ret = setup_irq(irq, &p->irqaction);
+       if (ret) {
+               pr_err("sh_cmt: failed to request irq %d\n", irq);
+               goto err1;
+       }
+
+       return 0;
+
+err1:
        iounmap(p->mapbase);
- err0:
+err0:
        return ret;
 }
 
 static int __devinit sh_cmt_probe(struct platform_device *pdev)
 {
        struct sh_cmt_priv *p = platform_get_drvdata(pdev);
-       struct sh_cmt_config *cfg = pdev->dev.platform_data;
+       struct sh_timer_config *cfg = pdev->dev.platform_data;
        int ret;
 
        if (p) {
@@ -645,11 +662,7 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev)
                return 0;
        }
 
-       if (is_early_platform_device(pdev))
-               p = alloc_bootmem(sizeof(*p));
-       else
-               p = kmalloc(sizeof(*p), GFP_KERNEL);
-
+       p = kmalloc(sizeof(*p), GFP_KERNEL);
        if (p == NULL) {
                dev_err(&pdev->dev, "failed to allocate driver data\n");
                return -ENOMEM;
@@ -657,11 +670,7 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev)
 
        ret = sh_cmt_setup(p, pdev);
        if (ret) {
-               if (is_early_platform_device(pdev))
-                       free_bootmem(__pa(p), sizeof(*p));
-               else
-                       kfree(p);
-
+               kfree(p);
                platform_set_drvdata(pdev, NULL);
        }
        return ret;