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); |