Commit | Line | Data |
---|---|---|
2b91c28f HHW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Copyright (c) 2020 MediaTek Inc. | |
4 | ||
5 | #include <linux/interrupt.h> | |
738654be FP |
6 | #include <linux/mfd/mt6357/core.h> |
7 | #include <linux/mfd/mt6357/registers.h> | |
2b91c28f HHW |
8 | #include <linux/mfd/mt6358/core.h> |
9 | #include <linux/mfd/mt6358/registers.h> | |
e545b8f3 HHW |
10 | #include <linux/mfd/mt6359/core.h> |
11 | #include <linux/mfd/mt6359/registers.h> | |
2b91c28f HHW |
12 | #include <linux/mfd/mt6397/core.h> |
13 | #include <linux/module.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_device.h> | |
16 | #include <linux/of_irq.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/regmap.h> | |
19 | ||
d8570c18 HHW |
20 | #define MTK_PMIC_REG_WIDTH 16 |
21 | ||
738654be FP |
22 | static const struct irq_top_t mt6357_ints[] = { |
23 | MT6357_TOP_GEN(BUCK), | |
24 | MT6357_TOP_GEN(LDO), | |
25 | MT6357_TOP_GEN(PSC), | |
26 | MT6357_TOP_GEN(SCK), | |
27 | MT6357_TOP_GEN(BM), | |
28 | MT6357_TOP_GEN(HK), | |
29 | MT6357_TOP_GEN(AUD), | |
30 | MT6357_TOP_GEN(MISC), | |
31 | }; | |
32 | ||
d8570c18 | 33 | static const struct irq_top_t mt6358_ints[] = { |
2b91c28f HHW |
34 | MT6358_TOP_GEN(BUCK), |
35 | MT6358_TOP_GEN(LDO), | |
36 | MT6358_TOP_GEN(PSC), | |
37 | MT6358_TOP_GEN(SCK), | |
38 | MT6358_TOP_GEN(BM), | |
39 | MT6358_TOP_GEN(HK), | |
40 | MT6358_TOP_GEN(AUD), | |
41 | MT6358_TOP_GEN(MISC), | |
42 | }; | |
43 | ||
e545b8f3 HHW |
44 | static const struct irq_top_t mt6359_ints[] = { |
45 | MT6359_TOP_GEN(BUCK), | |
46 | MT6359_TOP_GEN(LDO), | |
47 | MT6359_TOP_GEN(PSC), | |
48 | MT6359_TOP_GEN(SCK), | |
49 | MT6359_TOP_GEN(BM), | |
50 | MT6359_TOP_GEN(HK), | |
51 | MT6359_TOP_GEN(AUD), | |
52 | MT6359_TOP_GEN(MISC), | |
53 | }; | |
54 | ||
738654be FP |
55 | static struct pmic_irq_data mt6357_irqd = { |
56 | .num_top = ARRAY_SIZE(mt6357_ints), | |
57 | .num_pmic_irqs = MT6357_IRQ_NR, | |
58 | .top_int_status_reg = MT6357_TOP_INT_STATUS0, | |
59 | .pmic_ints = mt6357_ints, | |
60 | }; | |
61 | ||
d8570c18 HHW |
62 | static struct pmic_irq_data mt6358_irqd = { |
63 | .num_top = ARRAY_SIZE(mt6358_ints), | |
64 | .num_pmic_irqs = MT6358_IRQ_NR, | |
65 | .top_int_status_reg = MT6358_TOP_INT_STATUS0, | |
66 | .pmic_ints = mt6358_ints, | |
67 | }; | |
68 | ||
e545b8f3 HHW |
69 | static struct pmic_irq_data mt6359_irqd = { |
70 | .num_top = ARRAY_SIZE(mt6359_ints), | |
71 | .num_pmic_irqs = MT6359_IRQ_NR, | |
72 | .top_int_status_reg = MT6359_TOP_INT_STATUS0, | |
73 | .pmic_ints = mt6359_ints, | |
74 | }; | |
75 | ||
2b91c28f HHW |
76 | static void pmic_irq_enable(struct irq_data *data) |
77 | { | |
78 | unsigned int hwirq = irqd_to_hwirq(data); | |
79 | struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); | |
80 | struct pmic_irq_data *irqd = chip->irq_data; | |
81 | ||
82 | irqd->enable_hwirq[hwirq] = true; | |
83 | } | |
84 | ||
85 | static void pmic_irq_disable(struct irq_data *data) | |
86 | { | |
87 | unsigned int hwirq = irqd_to_hwirq(data); | |
88 | struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); | |
89 | struct pmic_irq_data *irqd = chip->irq_data; | |
90 | ||
91 | irqd->enable_hwirq[hwirq] = false; | |
92 | } | |
93 | ||
94 | static void pmic_irq_lock(struct irq_data *data) | |
95 | { | |
96 | struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); | |
97 | ||
98 | mutex_lock(&chip->irqlock); | |
99 | } | |
100 | ||
101 | static void pmic_irq_sync_unlock(struct irq_data *data) | |
102 | { | |
103 | unsigned int i, top_gp, gp_offset, en_reg, int_regs, shift; | |
104 | struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); | |
105 | struct pmic_irq_data *irqd = chip->irq_data; | |
106 | ||
107 | for (i = 0; i < irqd->num_pmic_irqs; i++) { | |
108 | if (irqd->enable_hwirq[i] == irqd->cache_hwirq[i]) | |
109 | continue; | |
110 | ||
111 | /* Find out the IRQ group */ | |
112 | top_gp = 0; | |
113 | while ((top_gp + 1) < irqd->num_top && | |
d8570c18 | 114 | i >= irqd->pmic_ints[top_gp + 1].hwirq_base) |
2b91c28f HHW |
115 | top_gp++; |
116 | ||
117 | /* Find the IRQ registers */ | |
d8570c18 HHW |
118 | gp_offset = i - irqd->pmic_ints[top_gp].hwirq_base; |
119 | int_regs = gp_offset / MTK_PMIC_REG_WIDTH; | |
120 | shift = gp_offset % MTK_PMIC_REG_WIDTH; | |
121 | en_reg = irqd->pmic_ints[top_gp].en_reg + | |
122 | (irqd->pmic_ints[top_gp].en_reg_shift * int_regs); | |
2b91c28f HHW |
123 | |
124 | regmap_update_bits(chip->regmap, en_reg, BIT(shift), | |
125 | irqd->enable_hwirq[i] << shift); | |
126 | ||
127 | irqd->cache_hwirq[i] = irqd->enable_hwirq[i]; | |
128 | } | |
129 | mutex_unlock(&chip->irqlock); | |
130 | } | |
131 | ||
132 | static struct irq_chip mt6358_irq_chip = { | |
133 | .name = "mt6358-irq", | |
134 | .flags = IRQCHIP_SKIP_SET_WAKE, | |
135 | .irq_enable = pmic_irq_enable, | |
136 | .irq_disable = pmic_irq_disable, | |
137 | .irq_bus_lock = pmic_irq_lock, | |
138 | .irq_bus_sync_unlock = pmic_irq_sync_unlock, | |
139 | }; | |
140 | ||
141 | static void mt6358_irq_sp_handler(struct mt6397_chip *chip, | |
142 | unsigned int top_gp) | |
143 | { | |
144 | unsigned int irq_status, sta_reg, status; | |
145 | unsigned int hwirq, virq; | |
146 | int i, j, ret; | |
d8570c18 | 147 | struct pmic_irq_data *irqd = chip->irq_data; |
2b91c28f | 148 | |
d8570c18 HHW |
149 | for (i = 0; i < irqd->pmic_ints[top_gp].num_int_regs; i++) { |
150 | sta_reg = irqd->pmic_ints[top_gp].sta_reg + | |
151 | irqd->pmic_ints[top_gp].sta_reg_shift * i; | |
2b91c28f HHW |
152 | |
153 | ret = regmap_read(chip->regmap, sta_reg, &irq_status); | |
154 | if (ret) { | |
155 | dev_err(chip->dev, | |
156 | "Failed to read IRQ status, ret=%d\n", ret); | |
157 | return; | |
158 | } | |
159 | ||
160 | if (!irq_status) | |
161 | continue; | |
162 | ||
163 | status = irq_status; | |
164 | do { | |
165 | j = __ffs(status); | |
166 | ||
d8570c18 HHW |
167 | hwirq = irqd->pmic_ints[top_gp].hwirq_base + |
168 | MTK_PMIC_REG_WIDTH * i + j; | |
2b91c28f HHW |
169 | |
170 | virq = irq_find_mapping(chip->irq_domain, hwirq); | |
171 | if (virq) | |
172 | handle_nested_irq(virq); | |
173 | ||
174 | status &= ~BIT(j); | |
175 | } while (status); | |
176 | ||
177 | regmap_write(chip->regmap, sta_reg, irq_status); | |
178 | } | |
179 | } | |
180 | ||
181 | static irqreturn_t mt6358_irq_handler(int irq, void *data) | |
182 | { | |
183 | struct mt6397_chip *chip = data; | |
d8570c18 | 184 | struct pmic_irq_data *irqd = chip->irq_data; |
2b91c28f HHW |
185 | unsigned int bit, i, top_irq_status = 0; |
186 | int ret; | |
187 | ||
188 | ret = regmap_read(chip->regmap, | |
d8570c18 | 189 | irqd->top_int_status_reg, |
2b91c28f HHW |
190 | &top_irq_status); |
191 | if (ret) { | |
192 | dev_err(chip->dev, | |
193 | "Failed to read status from the device, ret=%d\n", ret); | |
194 | return IRQ_NONE; | |
195 | } | |
196 | ||
d8570c18 HHW |
197 | for (i = 0; i < irqd->num_top; i++) { |
198 | bit = BIT(irqd->pmic_ints[i].top_offset); | |
2b91c28f HHW |
199 | if (top_irq_status & bit) { |
200 | mt6358_irq_sp_handler(chip, i); | |
201 | top_irq_status &= ~bit; | |
202 | if (!top_irq_status) | |
203 | break; | |
204 | } | |
205 | } | |
206 | ||
207 | return IRQ_HANDLED; | |
208 | } | |
209 | ||
210 | static int pmic_irq_domain_map(struct irq_domain *d, unsigned int irq, | |
211 | irq_hw_number_t hw) | |
212 | { | |
213 | struct mt6397_chip *mt6397 = d->host_data; | |
214 | ||
215 | irq_set_chip_data(irq, mt6397); | |
216 | irq_set_chip_and_handler(irq, &mt6358_irq_chip, handle_level_irq); | |
217 | irq_set_nested_thread(irq, 1); | |
218 | irq_set_noprobe(irq); | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | static const struct irq_domain_ops mt6358_irq_domain_ops = { | |
224 | .map = pmic_irq_domain_map, | |
225 | .xlate = irq_domain_xlate_twocell, | |
226 | }; | |
227 | ||
228 | int mt6358_irq_init(struct mt6397_chip *chip) | |
229 | { | |
230 | int i, j, ret; | |
231 | struct pmic_irq_data *irqd; | |
232 | ||
d8570c18 | 233 | switch (chip->chip_id) { |
738654be FP |
234 | case MT6357_CHIP_ID: |
235 | chip->irq_data = &mt6357_irqd; | |
236 | break; | |
237 | ||
d8570c18 | 238 | case MT6358_CHIP_ID: |
c47383f8 | 239 | case MT6366_CHIP_ID: |
d8570c18 HHW |
240 | chip->irq_data = &mt6358_irqd; |
241 | break; | |
2b91c28f | 242 | |
e545b8f3 HHW |
243 | case MT6359_CHIP_ID: |
244 | chip->irq_data = &mt6359_irqd; | |
245 | break; | |
246 | ||
d8570c18 HHW |
247 | default: |
248 | dev_err(chip->dev, "unsupported chip: 0x%x\n", chip->chip_id); | |
249 | return -ENODEV; | |
250 | } | |
2b91c28f HHW |
251 | |
252 | mutex_init(&chip->irqlock); | |
d8570c18 | 253 | irqd = chip->irq_data; |
2b91c28f HHW |
254 | irqd->enable_hwirq = devm_kcalloc(chip->dev, |
255 | irqd->num_pmic_irqs, | |
256 | sizeof(*irqd->enable_hwirq), | |
257 | GFP_KERNEL); | |
258 | if (!irqd->enable_hwirq) | |
259 | return -ENOMEM; | |
260 | ||
261 | irqd->cache_hwirq = devm_kcalloc(chip->dev, | |
262 | irqd->num_pmic_irqs, | |
263 | sizeof(*irqd->cache_hwirq), | |
264 | GFP_KERNEL); | |
265 | if (!irqd->cache_hwirq) | |
266 | return -ENOMEM; | |
267 | ||
268 | /* Disable all interrupts for initializing */ | |
269 | for (i = 0; i < irqd->num_top; i++) { | |
d8570c18 | 270 | for (j = 0; j < irqd->pmic_ints[i].num_int_regs; j++) |
2b91c28f | 271 | regmap_write(chip->regmap, |
d8570c18 HHW |
272 | irqd->pmic_ints[i].en_reg + |
273 | irqd->pmic_ints[i].en_reg_shift * j, 0); | |
2b91c28f HHW |
274 | } |
275 | ||
276 | chip->irq_domain = irq_domain_add_linear(chip->dev->of_node, | |
277 | irqd->num_pmic_irqs, | |
278 | &mt6358_irq_domain_ops, chip); | |
279 | if (!chip->irq_domain) { | |
280 | dev_err(chip->dev, "Could not create IRQ domain\n"); | |
281 | return -ENODEV; | |
282 | } | |
283 | ||
284 | ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, | |
285 | mt6358_irq_handler, IRQF_ONESHOT, | |
286 | mt6358_irq_chip.name, chip); | |
287 | if (ret) { | |
288 | dev_err(chip->dev, "Failed to register IRQ=%d, ret=%d\n", | |
289 | chip->irq, ret); | |
290 | return ret; | |
291 | } | |
292 | ||
293 | enable_irq_wake(chip->irq); | |
294 | return ret; | |
295 | } |