rk312x:pmic:rt5036:support pmic rt5036 drivers
[firefly-linux-kernel-4.4.55.git] / drivers / mfd / rt5036-irq.c
diff --git a/drivers/mfd/rt5036-irq.c b/drivers/mfd/rt5036-irq.c
new file mode 100755 (executable)
index 0000000..68df9af
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ *  drivers/mfd/rt5036-irq.c
+ *  Driver for Richtek RT5036 PMIC IRQ driver
+ *
+ *  Copyright (C) 2014 Richtek Technology Corp.
+ *  cy_huang <cy_huang@richtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+
+#include <linux/mfd/rt5036/rt5036.h>
+#include <linux/mfd/rt5036/rt5036-irq.h>
+
+struct rt5036_irq_info {
+       struct i2c_client *i2c;
+       struct rt5036_chip *chip;
+       struct device *dev;
+       int irq;
+       unsigned char suspend:1;
+       struct delayed_work irq_delayed_work;
+};
+
+static irqreturn_t rt5036_irq_handler(int irqno, void *param)
+{
+       struct rt5036_irq_info *ii = param;
+       unsigned char regval[3];
+       unsigned int irq_event;
+       int ret = 0;
+
+       if (ii->suspend) {
+               schedule_delayed_work(&ii->irq_delayed_work,
+                                     msecs_to_jiffies(10));
+               goto irq_fin;
+       }
+
+       ret =
+           rt5036_reg_block_read(ii->i2c, RT5036_REG_CHGIRQ1,
+                                 ARRAY_SIZE(regval), regval);
+       if (ret < 0) {
+               dev_err(ii->dev, "read charger irq event fail\n");
+       } else {
+               irq_event = regval[0] << 16 | regval[1] << 8 | regval[2];
+               RTINFO("chg event %06x\n", irq_event);
+#ifdef CONFIG_CHARGER_RT5036
+               if (irq_event && ii->chip->chg_info)
+                       rt5036_charger_irq_handler(ii->chip->chg_info,
+                                                  irq_event);
+#endif /* #ifdef CONFIG_CHARGER_RT5036 */
+       }
+
+       ret =
+           rt5036_reg_block_read(ii->i2c, RT5036_REG_BUCKLDOIRQ,
+                                 ARRAY_SIZE(regval), regval);
+       if (ret < 0) {
+               dev_err(ii->dev, "read misc irq event fail\n");
+       } else {
+               irq_event = regval[0] << 16 | regval[1] << 8 | regval[2];
+               RTINFO("misc event %06x\n", irq_event);
+#ifdef CONFIG_MISC_RT5036
+               if (irq_event && ii->chip->misc_info)
+                       rt5036_misc_irq_handler(ii->chip->misc_info, irq_event);
+#endif /* #ifdef CONFIG_MISC_RT5036 */
+       }
+
+       ret = rt5036_reg_read(ii->i2c, RT5036_REG_STBWACKIRQ);
+       if (ret < 0) {
+               dev_err(ii->dev, "read rtc irq event fail\n");
+       } else {
+               irq_event = ret;
+               RTINFO("rtc event %02x\n", irq_event);
+#ifdef CONFIG_RTC_RT5036
+               if (irq_event && ii->chip->rtc_info)
+                       rt5036_rtc_irq_handler(ii->chip->rtc_info, irq_event);
+#endif /* #ifdef CONFIG_RTC_RT5036 */
+       }
+       rt5036_set_bits(ii->i2c, RT5036_REG_BUCKVN1, RT5036_IRQPREZ_MASK);
+irq_fin:
+       return IRQ_HANDLED;
+}
+
+static void rt5036_irq_delayed_work(struct work_struct *work)
+{
+       struct rt5036_irq_info *ii =
+           (struct rt5036_irq_info *)container_of(work, struct rt5036_irq_info,
+                                                  irq_delayed_work.work);
+       rt5036_irq_handler(ii->irq, ii);
+}
+
+static int rt_parse_dt(struct rt5036_irq_info *ii, struct device *dev)
+{
+#ifdef CONFIG_OF
+       struct device_node *np = dev->of_node;
+       int val;
+
+       val = of_get_named_gpio(np, "rt,irq-gpio", 0);
+       if (gpio_is_valid(val)) {
+               if (gpio_request(val, "rt5036_irq") >= 0) {
+                       gpio_direction_input(val);
+                       ii->irq = gpio_to_irq(val);
+               } else {
+                       ii->irq = -1;
+               }
+       } else {
+               ii->irq = -1;
+       }
+#endif /* #ifdef CONFIG_OF */
+       RTINFO("\n");
+       return 0;
+}
+
+static int rt_parse_pdata(struct rt5036_irq_info *ii,
+                                   struct device *dev)
+{
+       struct rt5036_irq_data *pdata = dev->platform_data;
+
+       if (gpio_is_valid(pdata->irq_gpio)) {
+               if (gpio_request(pdata->irq_gpio, "rt5036_irq") >= 0) {
+                       gpio_direction_input(pdata->irq_gpio);
+                       ii->irq = gpio_to_irq(pdata->irq_gpio);
+               } else {
+                       ii->irq = -1;
+               }
+       } else {
+               ii->irq = -1;
+       }
+       RTINFO("\n");
+       return 0;
+}
+
+static int rt5036_irq_probe(struct platform_device *pdev)
+{
+       struct rt5036_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       struct rt5036_platform_data *pdata = (pdev->dev.parent)->platform_data;
+       struct rt5036_irq_info *ii;
+       bool use_dt = pdev->dev.of_node;
+
+       ii = devm_kzalloc(&pdev->dev, sizeof(*ii), GFP_KERNEL);
+       if (!ii)
+               return -ENOMEM;
+
+       ii->i2c = chip->i2c;
+       ii->chip = chip;
+       ii->dev = &pdev->dev;
+       if (use_dt) {
+               rt_parse_dt(ii, &pdev->dev);
+       } else {
+               if (!pdata)
+                       goto out_dev;
+               pdev->dev.platform_data = pdata->irq_pdata;
+               rt_parse_pdata(ii, &pdev->dev);
+               dev_info(&pdev->dev, "ii->irq %d\n", ii->irq);
+       }
+       INIT_DELAYED_WORK(&ii->irq_delayed_work, rt5036_irq_delayed_work);
+
+       platform_set_drvdata(pdev, ii);
+       if (ii->irq >= 0) {
+               if (devm_request_irq
+                   (&pdev->dev, ii->irq, rt5036_irq_handler,
+                    IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND | IRQF_DISABLED,
+                    "rt5036_irq", ii)) {
+                       dev_err(&pdev->dev, "request threaded irq fail\n");
+                       goto out_dev;
+               }
+               enable_irq_wake(ii->irq);
+               schedule_delayed_work(&ii->irq_delayed_work,
+                                     msecs_to_jiffies(500));
+       }
+       dev_info(&pdev->dev, "driver successfully loaded\n");
+       return 0;
+out_dev:
+       return -EINVAL;
+}
+
+static int rt5036_irq_remove(struct platform_device *pdev)
+{
+       struct rt5036_irq_info *ii = platform_get_drvdata(pdev);
+
+       if (ii->irq >= 0)
+               devm_free_irq(&pdev->dev, ii->irq, ii);
+       return 0;
+}
+
+static int rt5036_irq_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct rt5036_irq_info *ii = platform_get_drvdata(pdev);
+
+       ii->suspend = 1;
+       return 0;
+}
+
+static int rt5036_irq_resume(struct platform_device *pdev)
+{
+       struct rt5036_irq_info *ii = platform_get_drvdata(pdev);
+
+       ii->suspend = 0;
+       return 0;
+}
+
+static const struct of_device_id rt_match_table[] = {
+       {.compatible = "rt,rt5036-irq",},
+       {},
+};
+
+static struct platform_driver rt5036_irq_driver = {
+       .driver = {
+                  .name = RT5036_DEV_NAME "-irq",
+                  .owner = THIS_MODULE,
+                  .of_match_table = rt_match_table,
+                  },
+       .probe = rt5036_irq_probe,
+       .remove = rt5036_irq_remove,
+       .suspend = rt5036_irq_suspend,
+       .resume = rt5036_irq_resume,
+};
+
+static int __init rt5036_irq_init(void)
+{
+       return platform_driver_register(&rt5036_irq_driver);
+}
+subsys_initcall_sync(rt5036_irq_init);
+
+static void __exit rt5036_irq_exit(void)
+{
+       platform_driver_unregister(&rt5036_irq_driver);
+}
+module_exit(rt5036_irq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("CY Huang <cy_huang@richtek.com");
+MODULE_DESCRIPTION("IRQ driver for RT5036");
+MODULE_ALIAS("platform:" RT5036_DEV_NAME "-irq");
+MODULE_VERSION(RT5036_DRV_VER);