Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6
[safe/jmp/linux-2.6] / drivers / input / keyboard / sh_keysc.c
index 8486abc..887af79 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/input.h>
+#include <linux/clk.h>
 #include <linux/io.h>
 #include <asm/sh_keysc.h>
 
@@ -39,6 +40,7 @@ static const struct {
 
 struct sh_keysc_priv {
        void __iomem *iomem_base;
+       struct clk *clk;
        unsigned long last_keys;
        struct input_dev *input;
        struct sh_keysc_info pdata;
@@ -78,6 +80,9 @@ static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
                iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8),
                          priv->iomem_base + KYCR2_OFFS);
 
+               if (pdata->kycr2_delay)
+                       udelay(pdata->kycr2_delay);
+
                keys ^= ~0;
                keys &= (1 << (sh_keysc_mode[pdata->mode].keyin *
                               sh_keysc_mode[pdata->mode].keyout)) - 1;
@@ -125,7 +130,8 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
        struct sh_keysc_info *pdata;
        struct resource *res;
        struct input_dev *input;
-       int i, k;
+       char clk_name[8];
+       int i;
        int irq, error;
 
        if (!pdev->dev.platform_data) {
@@ -158,17 +164,18 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
        memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata));
        pdata = &priv->pdata;
 
-       res = request_mem_region(res->start, res_size(res), pdev->name);
-       if (res == NULL) {
-               dev_err(&pdev->dev, "failed to request I/O memory\n");
-               error = -EBUSY;
-               goto err1;
-       }
-
        priv->iomem_base = ioremap_nocache(res->start, res_size(res));
        if (priv->iomem_base == NULL) {
                dev_err(&pdev->dev, "failed to remap I/O memory\n");
                error = -ENXIO;
+               goto err1;
+       }
+
+       snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id);
+       priv->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(priv->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               error = PTR_ERR(priv->clk);
                goto err2;
        }
 
@@ -191,17 +198,19 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
        input->id.product = 0x0001;
        input->id.version = 0x0100;
 
+       input->keycode = pdata->keycodes;
+       input->keycodesize = sizeof(pdata->keycodes[0]);
+       input->keycodemax = ARRAY_SIZE(pdata->keycodes);
+
        error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
        if (error) {
                dev_err(&pdev->dev, "failed to request IRQ\n");
                goto err4;
        }
 
-       for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
-               k = pdata->keycodes[i];
-               if (k)
-                       input_set_capability(input, EV_KEY, k);
-       }
+       for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
+               __set_bit(pdata->keycodes[i], input->keybit);
+       __clear_bit(KEY_RESERVED, input->keybit);
 
        error = input_register_device(input);
        if (error) {
@@ -209,19 +218,25 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
                goto err5;
        }
 
+       clk_enable(priv->clk);
+
        iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) |
                  pdata->scan_timing, priv->iomem_base + KYCR1_OFFS);
        iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
        iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS);
+
+       device_init_wakeup(&pdev->dev, 1);
+
        return 0;
+
  err5:
        free_irq(irq, pdev);
  err4:
        input_free_device(input);
  err3:
-       iounmap(priv->iomem_base);
+       clk_put(priv->clk);
  err2:
-       release_mem_region(res->start, res_size(res));
+       iounmap(priv->iomem_base);
  err1:
        platform_set_drvdata(pdev, NULL);
        kfree(priv);
@@ -232,7 +247,6 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
 static int __devexit sh_keysc_remove(struct platform_device *pdev)
 {
        struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
-       struct resource *res;
 
        iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS);
 
@@ -240,25 +254,58 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
        free_irq(platform_get_irq(pdev, 0), pdev);
        iounmap(priv->iomem_base);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, res_size(res));
+       clk_disable(priv->clk);
+       clk_put(priv->clk);
 
        platform_set_drvdata(pdev, NULL);
        kfree(priv);
+
        return 0;
 }
 
+static int sh_keysc_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+       int irq = platform_get_irq(pdev, 0);
+       unsigned short value;
+
+       value = ioread16(priv->iomem_base + KYCR1_OFFS);
+
+       if (device_may_wakeup(dev)) {
+               value |= 0x80;
+               enable_irq_wake(irq);
+       } else {
+               value &= ~0x80;
+       }
 
-#define sh_keysc_suspend NULL
-#define sh_keysc_resume NULL
+       iowrite16(value, priv->iomem_base + KYCR1_OFFS);
+
+       return 0;
+}
+
+static int sh_keysc_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       int irq = platform_get_irq(pdev, 0);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(irq);
+
+       return 0;
+}
+
+static struct dev_pm_ops sh_keysc_dev_pm_ops = {
+       .suspend = sh_keysc_suspend,
+       .resume = sh_keysc_resume,
+};
 
 struct platform_driver sh_keysc_device_driver = {
        .probe          = sh_keysc_probe,
        .remove         = __devexit_p(sh_keysc_remove),
-       .suspend        = sh_keysc_suspend,
-       .resume         = sh_keysc_resume,
        .driver         = {
                .name   = "sh_keysc",
+               .pm     = &sh_keysc_dev_pm_ops,
        }
 };