ARM: shmobile: Rework the PMIC IRQ line quirk
authorMarek Vasut <marek.vasut@gmail.com>
Tue, 18 Sep 2018 12:23:40 +0000 (14:23 +0200)
committerSimon Horman <horms+renesas@verge.net.au>
Wed, 19 Sep 2018 09:11:46 +0000 (11:11 +0200)
Rather than hard-coding the quirk topology, which stopped scaling,
parse the information from DT. The code looks for all compatible
PMICs -- da9063 and da9210 -- and checks if their IRQ line is tied
to the same pin. If so, the code sends a matching sequence to the
PMIC to deassert the IRQ.

Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> (on Koelsch)
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c

index 21ebc76..8e50daa 100644 (file)
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/list.h>
 #include <linux/notifier.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
 #include <linux/mfd/da9063/registers.h>
 
-
 #define IRQC_BASE              0xe61c0000
 #define IRQC_MONITOR           0x104   /* IRQn Signal Level Monitor Register */
 
 /* start of DA9210 System Control and Event Registers */
 #define DA9210_REG_MASK_A              0x54
 
+struct regulator_quirk {
+       struct list_head                list;
+       const struct of_device_id       *id;
+       struct of_phandle_args          irq_args;
+       struct i2c_msg                  i2c_msg;
+       bool                            shared; /* IRQ line is shared */
+};
+
+static LIST_HEAD(quirk_list);
 static void __iomem *irqc;
 
 /* first byte sets the memory pointer, following are consecutive reg values */
 static u8 da9063_irq_clr[] = { DA9063_REG_IRQ_MASK_A, 0xff, 0xff, 0xff, 0xff };
 static u8 da9210_irq_clr[] = { DA9210_REG_MASK_A, 0xff, 0xff };
 
-static struct i2c_msg da9xxx_msgs[3] = {
-       {
-               .addr = 0x58,
-               .len = ARRAY_SIZE(da9063_irq_clr),
-               .buf = da9063_irq_clr,
-       }, {
-               .addr = 0x68,
-               .len = ARRAY_SIZE(da9210_irq_clr),
-               .buf = da9210_irq_clr,
-       }, {
-               .addr = 0x70,
-               .len = ARRAY_SIZE(da9210_irq_clr),
-               .buf = da9210_irq_clr,
-       },
+static struct i2c_msg da9063_msg = {
+       .len = ARRAY_SIZE(da9063_irq_clr),
+       .buf = da9063_irq_clr,
+};
+
+static struct i2c_msg da9210_msg = {
+       .len = ARRAY_SIZE(da9210_irq_clr),
+       .buf = da9210_irq_clr,
+};
+
+static const struct of_device_id rcar_gen2_quirk_match[] = {
+       { .compatible = "dlg,da9063", .data = &da9063_msg },
+       { .compatible = "dlg,da9210", .data = &da9210_msg },
+       {},
 };
 
 static int regulator_quirk_notify(struct notifier_block *nb,
                                  unsigned long action, void *data)
 {
+       struct regulator_quirk *pos, *tmp;
        struct device *dev = data;
        struct i2c_client *client;
        static bool done;
+       int ret;
        u32 mon;
 
        if (done)
@@ -80,17 +92,20 @@ static int regulator_quirk_notify(struct notifier_block *nb,
        client = to_i2c_client(dev);
        dev_dbg(dev, "Detected %s\n", client->name);
 
-       if ((client->addr == 0x58 && !strcmp(client->name, "da9063")) ||
-           (client->addr == 0x68 && !strcmp(client->name, "da9210")) ||
-           (client->addr == 0x70 && !strcmp(client->name, "da9210"))) {
-               int ret, len;
+       /*
+        * Send message to all PMICs that share an IRQ line to deassert it.
+        *
+        * WARNING: This works only if all the PMICs are on the same I2C bus.
+        */
+       list_for_each_entry(pos, &quirk_list, list) {
+               if (!pos->shared)
+                       continue;
 
-               /* There are two DA9210 on Stout, one on the other boards. */
-               len = of_machine_is_compatible("renesas,stout") ? 3 : 2;
+               dev_info(&client->dev, "clearing %s@0x%02x interrupts\n",
+                        pos->id->compatible, pos->i2c_msg.addr);
 
-               dev_info(&client->dev, "clearing da9063/da9210 interrupts\n");
-               ret = i2c_transfer(client->adapter, da9xxx_msgs, len);
-               if (ret != len)
+               ret = i2c_transfer(client->adapter, &pos->i2c_msg, 1);
+               if (ret != 1)
                        dev_err(&client->dev, "i2c error %d\n", ret);
        }
 
@@ -103,6 +118,11 @@ static int regulator_quirk_notify(struct notifier_block *nb,
 remove:
        dev_info(dev, "IRQ2 is not asserted, removing quirk\n");
 
+       list_for_each_entry_safe(pos, tmp, &quirk_list, list) {
+               list_del(&pos->list);
+               kfree(pos);
+       }
+
        done = true;
        iounmap(irqc);
        return 0;
@@ -114,7 +134,12 @@ static struct notifier_block regulator_quirk_nb = {
 
 static int __init rcar_gen2_regulator_quirk(void)
 {
-       u32 mon;
+       struct regulator_quirk *quirk, *pos, *tmp;
+       struct of_phandle_args *argsa, *argsb;
+       const struct of_device_id *id;
+       struct device_node *np;
+       u32 mon, addr;
+       int ret;
 
        if (!of_machine_is_compatible("renesas,koelsch") &&
            !of_machine_is_compatible("renesas,lager") &&
@@ -122,22 +147,78 @@ static int __init rcar_gen2_regulator_quirk(void)
            !of_machine_is_compatible("renesas,gose"))
                return -ENODEV;
 
+       for_each_matching_node_and_match(np, rcar_gen2_quirk_match, &id) {
+               if (!of_device_is_available(np))
+                       break;
+
+               ret = of_property_read_u32(np, "reg", &addr);
+               if (ret)        /* Skip invalid entry and continue */
+                       continue;
+
+               quirk = kzalloc(sizeof(*quirk), GFP_KERNEL);
+               if (!quirk) {
+                       ret = -ENOMEM;
+                       goto err_mem;
+               }
+
+               argsa = &quirk->irq_args;
+               memcpy(&quirk->i2c_msg, id->data, sizeof(quirk->i2c_msg));
+
+               quirk->id = id;
+               quirk->i2c_msg.addr = addr;
+
+               ret = of_irq_parse_one(np, 0, argsa);
+               if (ret) {      /* Skip invalid entry and continue */
+                       kfree(quirk);
+                       continue;
+               }
+
+               list_for_each_entry(pos, &quirk_list, list) {
+                       argsb = &pos->irq_args;
+
+                       if (argsa->args_count != argsb->args_count)
+                               continue;
+
+                       ret = memcmp(argsa->args, argsb->args,
+                                    argsa->args_count *
+                                    sizeof(argsa->args[0]));
+                       if (!ret) {
+                               pos->shared = true;
+                               quirk->shared = true;
+                       }
+               }
+
+               list_add_tail(&quirk->list, &quirk_list);
+       }
+
        irqc = ioremap(IRQC_BASE, PAGE_SIZE);
-       if (!irqc)
-               return -ENOMEM;
+       if (!irqc) {
+               ret = -ENOMEM;
+               goto err_mem;
+       }
 
        mon = ioread32(irqc + IRQC_MONITOR);
        if (mon & REGULATOR_IRQ_MASK) {
                pr_debug("%s: IRQ2 is not asserted, not installing quirk\n",
                         __func__);
-               iounmap(irqc);
-               return 0;
+               ret = 0;
+               goto err_free;
        }
 
        pr_info("IRQ2 is asserted, installing da9063/da9210 regulator quirk\n");
 
        bus_register_notifier(&i2c_bus_type, &regulator_quirk_nb);
        return 0;
+
+err_free:
+       iounmap(irqc);
+err_mem:
+       list_for_each_entry_safe(pos, tmp, &quirk_list, list) {
+               list_del(&pos->list);
+               kfree(pos);
+       }
+
+       return ret;
 }
 
 arch_initcall(rcar_gen2_regulator_quirk);