#include <linux/io.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/input/samsung-keypad.h>
 
 struct samsung_keypad {
        struct input_dev *input_dev;
+       struct platform_device *pdev;
        struct clk *clk;
        void __iomem *base;
        wait_queue_head_t wait;
        bool stopped;
+       bool wake_enabled;
        int irq;
        unsigned int row_shift;
        unsigned int rows;
        unsigned int val;
        bool key_down;
 
+       pm_runtime_get_sync(&keypad->pdev->dev);
+
        do {
                val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR);
                /* Clear interrupt. */
 
        } while (key_down && !keypad->stopped);
 
+       pm_runtime_put_sync(&keypad->pdev->dev);
+
        return IRQ_HANDLED;
 }
 
 {
        unsigned int val;
 
+       pm_runtime_get_sync(&keypad->pdev->dev);
+
        /* Tell IRQ thread that it may poll the device. */
        keypad->stopped = false;
 
 
        /* KEYIFCOL reg clear. */
        writel(0, keypad->base + SAMSUNG_KEYIFCOL);
+
+       pm_runtime_put_sync(&keypad->pdev->dev);
 }
 
 static void samsung_keypad_stop(struct samsung_keypad *keypad)
 {
        unsigned int val;
 
+       pm_runtime_get_sync(&keypad->pdev->dev);
+
        /* Signal IRQ thread to stop polling and disable the handler. */
        keypad->stopped = true;
        wake_up(&keypad->wait);
         * re-enable the handler.
         */
        enable_irq(keypad->irq);
+
+       pm_runtime_put_sync(&keypad->pdev->dev);
 }
 
 static int samsung_keypad_open(struct input_dev *input_dev)
        }
 
        keypad->input_dev = input_dev;
+       keypad->pdev = pdev;
        keypad->row_shift = row_shift;
        keypad->rows = pdata->rows;
        keypad->cols = pdata->cols;
+       keypad->stopped = true;
        init_waitqueue_head(&keypad->wait);
 
        input_dev->name = pdev->name;
                goto err_put_clk;
        }
 
+       device_init_wakeup(&pdev->dev, pdata->wakeup);
+       platform_set_drvdata(pdev, keypad);
+       pm_runtime_enable(&pdev->dev);
+
        error = input_register_device(keypad->input_dev);
        if (error)
                goto err_free_irq;
 
-       device_init_wakeup(&pdev->dev, pdata->wakeup);
-       platform_set_drvdata(pdev, keypad);
        return 0;
 
 err_free_irq:
        free_irq(keypad->irq, keypad);
+       pm_runtime_disable(&pdev->dev);
+       device_init_wakeup(&pdev->dev, 0);
+       platform_set_drvdata(pdev, NULL);
 err_put_clk:
        clk_put(keypad->clk);
 err_unmap_base:
 {
        struct samsung_keypad *keypad = platform_get_drvdata(pdev);
 
+       pm_runtime_disable(&pdev->dev);
        device_init_wakeup(&pdev->dev, 0);
        platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+static int samsung_keypad_runtime_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct samsung_keypad *keypad = platform_get_drvdata(pdev);
+       unsigned int val;
+       int error;
+
+       if (keypad->stopped)
+               return 0;
+
+       /* This may fail on some SoCs due to lack of controller support */
+       error = enable_irq_wake(keypad->irq);
+       if (!error)
+               keypad->wake_enabled = true;
+
+       val = readl(keypad->base + SAMSUNG_KEYIFCON);
+       val |= SAMSUNG_KEYIFCON_WAKEUPEN;
+       writel(val, keypad->base + SAMSUNG_KEYIFCON);
+
+       clk_disable(keypad->clk);
+
+       return 0;
+}
+
+static int samsung_keypad_runtime_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct samsung_keypad *keypad = platform_get_drvdata(pdev);
+       unsigned int val;
+
+       if (keypad->stopped)
+               return 0;
+
+       clk_enable(keypad->clk);
+
+       val = readl(keypad->base + SAMSUNG_KEYIFCON);
+       val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
+       writel(val, keypad->base + SAMSUNG_KEYIFCON);
+
+       if (keypad->wake_enabled)
+               disable_irq_wake(keypad->irq);
+
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad,
                                         bool enable)
 {
-       struct device *dev = keypad->input_dev->dev.parent;
        unsigned int val;
 
        clk_enable(keypad->clk);
        val = readl(keypad->base + SAMSUNG_KEYIFCON);
        if (enable) {
                val |= SAMSUNG_KEYIFCON_WAKEUPEN;
-               if (device_may_wakeup(dev))
+               if (device_may_wakeup(&keypad->pdev->dev))
                        enable_irq_wake(keypad->irq);
        } else {
                val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
-               if (device_may_wakeup(dev))
+               if (device_may_wakeup(&keypad->pdev->dev))
                        disable_irq_wake(keypad->irq);
        }
        writel(val, keypad->base + SAMSUNG_KEYIFCON);
 }
 #endif
 
-static SIMPLE_DEV_PM_OPS(samsung_keypad_pm_ops,
-                        samsung_keypad_suspend, samsung_keypad_resume);
+static const struct dev_pm_ops samsung_keypad_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume)
+       SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend,
+                          samsung_keypad_runtime_resume, NULL)
+};
 
 static struct platform_device_id samsung_keypad_driver_ids[] = {
        {