diff options
| author | Marek Vasut <marek.vasut@gmail.com> | 2018-09-18 08:23:40 -0400 |
|---|---|---|
| committer | Simon Horman <horms+renesas@verge.net.au> | 2018-09-19 05:11:46 -0400 |
| commit | 6d14d4d313d09dabdb06b4a9f4e5fe4305051e9b (patch) | |
| tree | 13d45c09227e338e766ed50c6e661d3b95df11ed | |
| parent | abf3bf537d298db18ad929ceb71c0353d6794f42 (diff) | |
ARM: shmobile: Rework the PMIC IRQ line quirk
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>
| -rw-r--r-- | arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c | 139 |
1 files changed, 110 insertions, 29 deletions
diff --git a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c index 21ebc7678ffd..8e50daa99151 100644 --- a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c +++ b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c | |||
| @@ -23,11 +23,12 @@ | |||
| 23 | #include <linux/i2c.h> | 23 | #include <linux/i2c.h> |
| 24 | #include <linux/init.h> | 24 | #include <linux/init.h> |
| 25 | #include <linux/io.h> | 25 | #include <linux/io.h> |
| 26 | #include <linux/list.h> | ||
| 26 | #include <linux/notifier.h> | 27 | #include <linux/notifier.h> |
| 27 | #include <linux/of.h> | 28 | #include <linux/of.h> |
| 29 | #include <linux/of_irq.h> | ||
| 28 | #include <linux/mfd/da9063/registers.h> | 30 | #include <linux/mfd/da9063/registers.h> |
| 29 | 31 | ||
| 30 | |||
| 31 | #define IRQC_BASE 0xe61c0000 | 32 | #define IRQC_BASE 0xe61c0000 |
| 32 | #define IRQC_MONITOR 0x104 /* IRQn Signal Level Monitor Register */ | 33 | #define IRQC_MONITOR 0x104 /* IRQn Signal Level Monitor Register */ |
| 33 | 34 | ||
| @@ -36,34 +37,45 @@ | |||
| 36 | /* start of DA9210 System Control and Event Registers */ | 37 | /* start of DA9210 System Control and Event Registers */ |
| 37 | #define DA9210_REG_MASK_A 0x54 | 38 | #define DA9210_REG_MASK_A 0x54 |
| 38 | 39 | ||
| 40 | struct regulator_quirk { | ||
| 41 | struct list_head list; | ||
| 42 | const struct of_device_id *id; | ||
| 43 | struct of_phandle_args irq_args; | ||
| 44 | struct i2c_msg i2c_msg; | ||
| 45 | bool shared; /* IRQ line is shared */ | ||
| 46 | }; | ||
| 47 | |||
| 48 | static LIST_HEAD(quirk_list); | ||
| 39 | static void __iomem *irqc; | 49 | static void __iomem *irqc; |
| 40 | 50 | ||
| 41 | /* first byte sets the memory pointer, following are consecutive reg values */ | 51 | /* first byte sets the memory pointer, following are consecutive reg values */ |
| 42 | static u8 da9063_irq_clr[] = { DA9063_REG_IRQ_MASK_A, 0xff, 0xff, 0xff, 0xff }; | 52 | static u8 da9063_irq_clr[] = { DA9063_REG_IRQ_MASK_A, 0xff, 0xff, 0xff, 0xff }; |
| 43 | static u8 da9210_irq_clr[] = { DA9210_REG_MASK_A, 0xff, 0xff }; | 53 | static u8 da9210_irq_clr[] = { DA9210_REG_MASK_A, 0xff, 0xff }; |
| 44 | 54 | ||
| 45 | static struct i2c_msg da9xxx_msgs[3] = { | 55 | static struct i2c_msg da9063_msg = { |
| 46 | { | 56 | .len = ARRAY_SIZE(da9063_irq_clr), |
| 47 | .addr = 0x58, | 57 | .buf = da9063_irq_clr, |
| 48 | .len = ARRAY_SIZE(da9063_irq_clr), | 58 | }; |
| 49 | .buf = da9063_irq_clr, | 59 | |
| 50 | }, { | 60 | static struct i2c_msg da9210_msg = { |
| 51 | .addr = 0x68, | 61 | .len = ARRAY_SIZE(da9210_irq_clr), |
| 52 | .len = ARRAY_SIZE(da9210_irq_clr), | 62 | .buf = da9210_irq_clr, |
| 53 | .buf = da9210_irq_clr, | 63 | }; |
| 54 | }, { | 64 | |
| 55 | .addr = 0x70, | 65 | static const struct of_device_id rcar_gen2_quirk_match[] = { |
| 56 | .len = ARRAY_SIZE(da9210_irq_clr), | 66 | { .compatible = "dlg,da9063", .data = &da9063_msg }, |
| 57 | .buf = da9210_irq_clr, | 67 | { .compatible = "dlg,da9210", .data = &da9210_msg }, |
| 58 | }, | 68 | {}, |
| 59 | }; | 69 | }; |
| 60 | 70 | ||
| 61 | static int regulator_quirk_notify(struct notifier_block *nb, | 71 | static int regulator_quirk_notify(struct notifier_block *nb, |
| 62 | unsigned long action, void *data) | 72 | unsigned long action, void *data) |
| 63 | { | 73 | { |
| 74 | struct regulator_quirk *pos, *tmp; | ||
| 64 | struct device *dev = data; | 75 | struct device *dev = data; |
| 65 | struct i2c_client *client; | 76 | struct i2c_client *client; |
| 66 | static bool done; | 77 | static bool done; |
| 78 | int ret; | ||
| 67 | u32 mon; | 79 | u32 mon; |
| 68 | 80 | ||
| 69 | if (done) | 81 | if (done) |
| @@ -80,17 +92,20 @@ static int regulator_quirk_notify(struct notifier_block *nb, | |||
| 80 | client = to_i2c_client(dev); | 92 | client = to_i2c_client(dev); |
| 81 | dev_dbg(dev, "Detected %s\n", client->name); | 93 | dev_dbg(dev, "Detected %s\n", client->name); |
| 82 | 94 | ||
| 83 | if ((client->addr == 0x58 && !strcmp(client->name, "da9063")) || | 95 | /* |
| 84 | (client->addr == 0x68 && !strcmp(client->name, "da9210")) || | 96 | * Send message to all PMICs that share an IRQ line to deassert it. |
| 85 | (client->addr == 0x70 && !strcmp(client->name, "da9210"))) { | 97 | * |
| 86 | int ret, len; | 98 | * WARNING: This works only if all the PMICs are on the same I2C bus. |
| 99 | */ | ||
| 100 | list_for_each_entry(pos, &quirk_list, list) { | ||
| 101 | if (!pos->shared) | ||
| 102 | continue; | ||
| 87 | 103 | ||
| 88 | /* There are two DA9210 on Stout, one on the other boards. */ | 104 | dev_info(&client->dev, "clearing %s@0x%02x interrupts\n", |
| 89 | len = of_machine_is_compatible("renesas,stout") ? 3 : 2; | 105 | pos->id->compatible, pos->i2c_msg.addr); |
| 90 | 106 | ||
| 91 | dev_info(&client->dev, "clearing da9063/da9210 interrupts\n"); | 107 | ret = i2c_transfer(client->adapter, &pos->i2c_msg, 1); |
| 92 | ret = i2c_transfer(client->adapter, da9xxx_msgs, len); | 108 | if (ret != 1) |
| 93 | if (ret != len) | ||
| 94 | dev_err(&client->dev, "i2c error %d\n", ret); | 109 | dev_err(&client->dev, "i2c error %d\n", ret); |
| 95 | } | 110 | } |
| 96 | 111 | ||
| @@ -103,6 +118,11 @@ static int regulator_quirk_notify(struct notifier_block *nb, | |||
| 103 | remove: | 118 | remove: |
| 104 | dev_info(dev, "IRQ2 is not asserted, removing quirk\n"); | 119 | dev_info(dev, "IRQ2 is not asserted, removing quirk\n"); |
| 105 | 120 | ||
| 121 | list_for_each_entry_safe(pos, tmp, &quirk_list, list) { | ||
| 122 | list_del(&pos->list); | ||
| 123 | kfree(pos); | ||
| 124 | } | ||
| 125 | |||
| 106 | done = true; | 126 | done = true; |
| 107 | iounmap(irqc); | 127 | iounmap(irqc); |
| 108 | return 0; | 128 | return 0; |
| @@ -114,7 +134,12 @@ static struct notifier_block regulator_quirk_nb = { | |||
| 114 | 134 | ||
| 115 | static int __init rcar_gen2_regulator_quirk(void) | 135 | static int __init rcar_gen2_regulator_quirk(void) |
| 116 | { | 136 | { |
| 117 | u32 mon; | 137 | struct regulator_quirk *quirk, *pos, *tmp; |
| 138 | struct of_phandle_args *argsa, *argsb; | ||
| 139 | const struct of_device_id *id; | ||
| 140 | struct device_node *np; | ||
| 141 | u32 mon, addr; | ||
| 142 | int ret; | ||
| 118 | 143 | ||
| 119 | if (!of_machine_is_compatible("renesas,koelsch") && | 144 | if (!of_machine_is_compatible("renesas,koelsch") && |
| 120 | !of_machine_is_compatible("renesas,lager") && | 145 | !of_machine_is_compatible("renesas,lager") && |
| @@ -122,22 +147,78 @@ static int __init rcar_gen2_regulator_quirk(void) | |||
| 122 | !of_machine_is_compatible("renesas,gose")) | 147 | !of_machine_is_compatible("renesas,gose")) |
| 123 | return -ENODEV; | 148 | return -ENODEV; |
| 124 | 149 | ||
| 150 | for_each_matching_node_and_match(np, rcar_gen2_quirk_match, &id) { | ||
| 151 | if (!of_device_is_available(np)) | ||
| 152 | break; | ||
| 153 | |||
| 154 | ret = of_property_read_u32(np, "reg", &addr); | ||
| 155 | if (ret) /* Skip invalid entry and continue */ | ||
| 156 | continue; | ||
| 157 | |||
| 158 | quirk = kzalloc(sizeof(*quirk), GFP_KERNEL); | ||
| 159 | if (!quirk) { | ||
| 160 | ret = -ENOMEM; | ||
| 161 | goto err_mem; | ||
| 162 | } | ||
| 163 | |||
| 164 | argsa = &quirk->irq_args; | ||
| 165 | memcpy(&quirk->i2c_msg, id->data, sizeof(quirk->i2c_msg)); | ||
| 166 | |||
| 167 | quirk->id = id; | ||
| 168 | quirk->i2c_msg.addr = addr; | ||
| 169 | |||
| 170 | ret = of_irq_parse_one(np, 0, argsa); | ||
| 171 | if (ret) { /* Skip invalid entry and continue */ | ||
| 172 | kfree(quirk); | ||
| 173 | continue; | ||
| 174 | } | ||
| 175 | |||
| 176 | list_for_each_entry(pos, &quirk_list, list) { | ||
| 177 | argsb = &pos->irq_args; | ||
| 178 | |||
| 179 | if (argsa->args_count != argsb->args_count) | ||
| 180 | continue; | ||
| 181 | |||
| 182 | ret = memcmp(argsa->args, argsb->args, | ||
| 183 | argsa->args_count * | ||
| 184 | sizeof(argsa->args[0])); | ||
| 185 | if (!ret) { | ||
| 186 | pos->shared = true; | ||
| 187 | quirk->shared = true; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | list_add_tail(&quirk->list, &quirk_list); | ||
| 192 | } | ||
| 193 | |||
| 125 | irqc = ioremap(IRQC_BASE, PAGE_SIZE); | 194 | irqc = ioremap(IRQC_BASE, PAGE_SIZE); |
| 126 | if (!irqc) | 195 | if (!irqc) { |
| 127 | return -ENOMEM; | 196 | ret = -ENOMEM; |
| 197 | goto err_mem; | ||
| 198 | } | ||
| 128 | 199 | ||
| 129 | mon = ioread32(irqc + IRQC_MONITOR); | 200 | mon = ioread32(irqc + IRQC_MONITOR); |
| 130 | if (mon & REGULATOR_IRQ_MASK) { | 201 | if (mon & REGULATOR_IRQ_MASK) { |
| 131 | pr_debug("%s: IRQ2 is not asserted, not installing quirk\n", | 202 | pr_debug("%s: IRQ2 is not asserted, not installing quirk\n", |
| 132 | __func__); | 203 | __func__); |
| 133 | iounmap(irqc); | 204 | ret = 0; |
| 134 | return 0; | 205 | goto err_free; |
| 135 | } | 206 | } |
| 136 | 207 | ||
| 137 | pr_info("IRQ2 is asserted, installing da9063/da9210 regulator quirk\n"); | 208 | pr_info("IRQ2 is asserted, installing da9063/da9210 regulator quirk\n"); |
| 138 | 209 | ||
| 139 | bus_register_notifier(&i2c_bus_type, ®ulator_quirk_nb); | 210 | bus_register_notifier(&i2c_bus_type, ®ulator_quirk_nb); |
| 140 | return 0; | 211 | return 0; |
| 212 | |||
| 213 | err_free: | ||
| 214 | iounmap(irqc); | ||
| 215 | err_mem: | ||
| 216 | list_for_each_entry_safe(pos, tmp, &quirk_list, list) { | ||
| 217 | list_del(&pos->list); | ||
| 218 | kfree(pos); | ||
| 219 | } | ||
| 220 | |||
| 221 | return ret; | ||
| 141 | } | 222 | } |
| 142 | 223 | ||
| 143 | arch_initcall(rcar_gen2_regulator_quirk); | 224 | arch_initcall(rcar_gen2_regulator_quirk); |
