1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // Exynos specific support for Samsung pinctrl/gpiolib driver with eint support. |
4 | // |
5 | // Copyright (c) 2012 Samsung Electronics Co., Ltd. |
6 | // http://www.samsung.com |
7 | // Copyright (c) 2012 Linaro Ltd |
8 | // http://www.linaro.org |
9 | // |
10 | // Author: Thomas Abraham <thomas.ab@samsung.com> |
11 | // |
12 | // This file contains the Samsung Exynos specific information required by the |
13 | // the Samsung pinctrl/gpiolib driver. It also includes the implementation of |
14 | // external gpio and wakeup interrupt support. |
15 | |
16 | #include <linux/device.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/irqdomain.h> |
19 | #include <linux/irq.h> |
20 | #include <linux/irqchip/chained_irq.h> |
21 | #include <linux/of.h> |
22 | #include <linux/of_irq.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/spinlock.h> |
25 | #include <linux/regmap.h> |
26 | #include <linux/err.h> |
27 | #include <linux/soc/samsung/exynos-pmu.h> |
28 | #include <linux/soc/samsung/exynos-regs-pmu.h> |
29 | |
30 | #include "pinctrl-samsung.h" |
31 | #include "pinctrl-exynos.h" |
32 | |
33 | struct exynos_irq_chip { |
34 | struct irq_chip chip; |
35 | |
36 | u32 eint_con; |
37 | u32 eint_mask; |
38 | u32 eint_pend; |
39 | u32 *eint_wake_mask_value; |
40 | u32 eint_wake_mask_reg; |
41 | void (*set_eint_wakeup_mask)(struct samsung_pinctrl_drv_data *drvdata, |
42 | struct exynos_irq_chip *irq_chip); |
43 | }; |
44 | |
45 | static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip) |
46 | { |
47 | return container_of(chip, struct exynos_irq_chip, chip); |
48 | } |
49 | |
50 | static void exynos_irq_mask(struct irq_data *irqd) |
51 | { |
52 | struct irq_chip *chip = irq_data_get_irq_chip(d: irqd); |
53 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); |
54 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
55 | unsigned long reg_mask; |
56 | unsigned int mask; |
57 | unsigned long flags; |
58 | |
59 | if (bank->eint_mask_offset) |
60 | reg_mask = bank->pctl_offset + bank->eint_mask_offset; |
61 | else |
62 | reg_mask = our_chip->eint_mask + bank->eint_offset; |
63 | |
64 | raw_spin_lock_irqsave(&bank->slock, flags); |
65 | |
66 | mask = readl(addr: bank->eint_base + reg_mask); |
67 | mask |= 1 << irqd->hwirq; |
68 | writel(val: mask, addr: bank->eint_base + reg_mask); |
69 | |
70 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
71 | } |
72 | |
73 | static void exynos_irq_ack(struct irq_data *irqd) |
74 | { |
75 | struct irq_chip *chip = irq_data_get_irq_chip(d: irqd); |
76 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); |
77 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
78 | unsigned long reg_pend; |
79 | |
80 | if (bank->eint_pend_offset) |
81 | reg_pend = bank->pctl_offset + bank->eint_pend_offset; |
82 | else |
83 | reg_pend = our_chip->eint_pend + bank->eint_offset; |
84 | |
85 | writel(val: 1 << irqd->hwirq, addr: bank->eint_base + reg_pend); |
86 | } |
87 | |
88 | static void exynos_irq_unmask(struct irq_data *irqd) |
89 | { |
90 | struct irq_chip *chip = irq_data_get_irq_chip(d: irqd); |
91 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); |
92 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
93 | unsigned long reg_mask; |
94 | unsigned int mask; |
95 | unsigned long flags; |
96 | |
97 | /* |
98 | * Ack level interrupts right before unmask |
99 | * |
100 | * If we don't do this we'll get a double-interrupt. Level triggered |
101 | * interrupts must not fire an interrupt if the level is not |
102 | * _currently_ active, even if it was active while the interrupt was |
103 | * masked. |
104 | */ |
105 | if (irqd_get_trigger_type(d: irqd) & IRQ_TYPE_LEVEL_MASK) |
106 | exynos_irq_ack(irqd); |
107 | |
108 | if (bank->eint_mask_offset) |
109 | reg_mask = bank->pctl_offset + bank->eint_mask_offset; |
110 | else |
111 | reg_mask = our_chip->eint_mask + bank->eint_offset; |
112 | |
113 | raw_spin_lock_irqsave(&bank->slock, flags); |
114 | |
115 | mask = readl(addr: bank->eint_base + reg_mask); |
116 | mask &= ~(1 << irqd->hwirq); |
117 | writel(val: mask, addr: bank->eint_base + reg_mask); |
118 | |
119 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
120 | } |
121 | |
122 | static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type) |
123 | { |
124 | struct irq_chip *chip = irq_data_get_irq_chip(d: irqd); |
125 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); |
126 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
127 | unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq; |
128 | unsigned int con, trig_type; |
129 | unsigned long reg_con; |
130 | |
131 | switch (type) { |
132 | case IRQ_TYPE_EDGE_RISING: |
133 | trig_type = EXYNOS_EINT_EDGE_RISING; |
134 | break; |
135 | case IRQ_TYPE_EDGE_FALLING: |
136 | trig_type = EXYNOS_EINT_EDGE_FALLING; |
137 | break; |
138 | case IRQ_TYPE_EDGE_BOTH: |
139 | trig_type = EXYNOS_EINT_EDGE_BOTH; |
140 | break; |
141 | case IRQ_TYPE_LEVEL_HIGH: |
142 | trig_type = EXYNOS_EINT_LEVEL_HIGH; |
143 | break; |
144 | case IRQ_TYPE_LEVEL_LOW: |
145 | trig_type = EXYNOS_EINT_LEVEL_LOW; |
146 | break; |
147 | default: |
148 | pr_err("unsupported external interrupt type\n" ); |
149 | return -EINVAL; |
150 | } |
151 | |
152 | if (type & IRQ_TYPE_EDGE_BOTH) |
153 | irq_set_handler_locked(data: irqd, handler: handle_edge_irq); |
154 | else |
155 | irq_set_handler_locked(data: irqd, handler: handle_level_irq); |
156 | |
157 | if (bank->eint_con_offset) |
158 | reg_con = bank->pctl_offset + bank->eint_con_offset; |
159 | else |
160 | reg_con = our_chip->eint_con + bank->eint_offset; |
161 | |
162 | con = readl(addr: bank->eint_base + reg_con); |
163 | con &= ~(EXYNOS_EINT_CON_MASK << shift); |
164 | con |= trig_type << shift; |
165 | writel(val: con, addr: bank->eint_base + reg_con); |
166 | |
167 | return 0; |
168 | } |
169 | |
170 | static int exynos_irq_set_affinity(struct irq_data *irqd, |
171 | const struct cpumask *dest, bool force) |
172 | { |
173 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
174 | struct samsung_pinctrl_drv_data *d = bank->drvdata; |
175 | struct irq_data *parent = irq_get_irq_data(irq: d->irq); |
176 | |
177 | if (parent) |
178 | return parent->chip->irq_set_affinity(parent, dest, force); |
179 | |
180 | return -EINVAL; |
181 | } |
182 | |
183 | static int exynos_irq_request_resources(struct irq_data *irqd) |
184 | { |
185 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
186 | const struct samsung_pin_bank_type *bank_type = bank->type; |
187 | unsigned long reg_con, flags; |
188 | unsigned int shift, mask, con; |
189 | int ret; |
190 | |
191 | ret = gpiochip_lock_as_irq(gc: &bank->gpio_chip, offset: irqd->hwirq); |
192 | if (ret) { |
193 | dev_err(bank->gpio_chip.parent, |
194 | "unable to lock pin %s-%lu IRQ\n" , |
195 | bank->name, irqd->hwirq); |
196 | return ret; |
197 | } |
198 | |
199 | reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC]; |
200 | shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC]; |
201 | mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; |
202 | |
203 | raw_spin_lock_irqsave(&bank->slock, flags); |
204 | |
205 | con = readl(addr: bank->pctl_base + reg_con); |
206 | con &= ~(mask << shift); |
207 | con |= EXYNOS_PIN_CON_FUNC_EINT << shift; |
208 | writel(val: con, addr: bank->pctl_base + reg_con); |
209 | |
210 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | static void exynos_irq_release_resources(struct irq_data *irqd) |
216 | { |
217 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
218 | const struct samsung_pin_bank_type *bank_type = bank->type; |
219 | unsigned long reg_con, flags; |
220 | unsigned int shift, mask, con; |
221 | |
222 | reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC]; |
223 | shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC]; |
224 | mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; |
225 | |
226 | raw_spin_lock_irqsave(&bank->slock, flags); |
227 | |
228 | con = readl(addr: bank->pctl_base + reg_con); |
229 | con &= ~(mask << shift); |
230 | con |= PIN_CON_FUNC_INPUT << shift; |
231 | writel(val: con, addr: bank->pctl_base + reg_con); |
232 | |
233 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
234 | |
235 | gpiochip_unlock_as_irq(gc: &bank->gpio_chip, offset: irqd->hwirq); |
236 | } |
237 | |
238 | /* |
239 | * irq_chip for gpio interrupts. |
240 | */ |
241 | static const struct exynos_irq_chip exynos_gpio_irq_chip __initconst = { |
242 | .chip = { |
243 | .name = "exynos_gpio_irq_chip" , |
244 | .irq_unmask = exynos_irq_unmask, |
245 | .irq_mask = exynos_irq_mask, |
246 | .irq_ack = exynos_irq_ack, |
247 | .irq_set_type = exynos_irq_set_type, |
248 | .irq_set_affinity = exynos_irq_set_affinity, |
249 | .irq_request_resources = exynos_irq_request_resources, |
250 | .irq_release_resources = exynos_irq_release_resources, |
251 | }, |
252 | .eint_con = EXYNOS_GPIO_ECON_OFFSET, |
253 | .eint_mask = EXYNOS_GPIO_EMASK_OFFSET, |
254 | .eint_pend = EXYNOS_GPIO_EPEND_OFFSET, |
255 | /* eint_wake_mask_value not used */ |
256 | }; |
257 | |
258 | static int exynos_eint_irq_map(struct irq_domain *h, unsigned int virq, |
259 | irq_hw_number_t hw) |
260 | { |
261 | struct samsung_pin_bank *b = h->host_data; |
262 | |
263 | irq_set_chip_data(irq: virq, data: b); |
264 | irq_set_chip_and_handler(irq: virq, chip: &b->irq_chip->chip, |
265 | handle: handle_level_irq); |
266 | return 0; |
267 | } |
268 | |
269 | /* |
270 | * irq domain callbacks for external gpio and wakeup interrupt controllers. |
271 | */ |
272 | static const struct irq_domain_ops exynos_eint_irqd_ops = { |
273 | .map = exynos_eint_irq_map, |
274 | .xlate = irq_domain_xlate_twocell, |
275 | }; |
276 | |
277 | static irqreturn_t exynos_eint_gpio_irq(int irq, void *data) |
278 | { |
279 | struct samsung_pinctrl_drv_data *d = data; |
280 | struct samsung_pin_bank *bank = d->pin_banks; |
281 | unsigned int svc, group, pin; |
282 | int ret; |
283 | |
284 | if (bank->eint_con_offset) |
285 | svc = readl(addr: bank->eint_base + EXYNOSAUTO_SVC_OFFSET); |
286 | else |
287 | svc = readl(addr: bank->eint_base + EXYNOS_SVC_OFFSET); |
288 | group = EXYNOS_SVC_GROUP(svc); |
289 | pin = svc & EXYNOS_SVC_NUM_MASK; |
290 | |
291 | if (!group) |
292 | return IRQ_HANDLED; |
293 | bank += (group - 1); |
294 | |
295 | ret = generic_handle_domain_irq(domain: bank->irq_domain, hwirq: pin); |
296 | if (ret) |
297 | return IRQ_NONE; |
298 | |
299 | return IRQ_HANDLED; |
300 | } |
301 | |
302 | struct exynos_eint_gpio_save { |
303 | u32 eint_con; |
304 | u32 eint_fltcon0; |
305 | u32 eint_fltcon1; |
306 | u32 eint_mask; |
307 | }; |
308 | |
309 | /* |
310 | * exynos_eint_gpio_init() - setup handling of external gpio interrupts. |
311 | * @d: driver data of samsung pinctrl driver. |
312 | */ |
313 | __init int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) |
314 | { |
315 | struct samsung_pin_bank *bank; |
316 | struct device *dev = d->dev; |
317 | int ret; |
318 | int i; |
319 | |
320 | if (!d->irq) { |
321 | dev_err(dev, "irq number not available\n" ); |
322 | return -EINVAL; |
323 | } |
324 | |
325 | ret = devm_request_irq(dev, irq: d->irq, handler: exynos_eint_gpio_irq, |
326 | irqflags: 0, devname: dev_name(dev), dev_id: d); |
327 | if (ret) { |
328 | dev_err(dev, "irq request failed\n" ); |
329 | return -ENXIO; |
330 | } |
331 | |
332 | bank = d->pin_banks; |
333 | for (i = 0; i < d->nr_banks; ++i, ++bank) { |
334 | if (bank->eint_type != EINT_TYPE_GPIO) |
335 | continue; |
336 | |
337 | bank->irq_chip = devm_kmemdup(dev, src: &exynos_gpio_irq_chip, |
338 | len: sizeof(*bank->irq_chip), GFP_KERNEL); |
339 | if (!bank->irq_chip) { |
340 | ret = -ENOMEM; |
341 | goto err_domains; |
342 | } |
343 | bank->irq_chip->chip.name = bank->name; |
344 | |
345 | bank->irq_domain = irq_domain_create_linear(fwnode: bank->fwnode, |
346 | size: bank->nr_pins, ops: &exynos_eint_irqd_ops, host_data: bank); |
347 | if (!bank->irq_domain) { |
348 | dev_err(dev, "gpio irq domain add failed\n" ); |
349 | ret = -ENXIO; |
350 | goto err_domains; |
351 | } |
352 | |
353 | bank->soc_priv = devm_kzalloc(dev: d->dev, |
354 | size: sizeof(struct exynos_eint_gpio_save), GFP_KERNEL); |
355 | if (!bank->soc_priv) { |
356 | irq_domain_remove(host: bank->irq_domain); |
357 | ret = -ENOMEM; |
358 | goto err_domains; |
359 | } |
360 | |
361 | } |
362 | |
363 | return 0; |
364 | |
365 | err_domains: |
366 | for (--i, --bank; i >= 0; --i, --bank) { |
367 | if (bank->eint_type != EINT_TYPE_GPIO) |
368 | continue; |
369 | irq_domain_remove(host: bank->irq_domain); |
370 | } |
371 | |
372 | return ret; |
373 | } |
374 | |
375 | static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on) |
376 | { |
377 | struct irq_chip *chip = irq_data_get_irq_chip(d: irqd); |
378 | struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); |
379 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
380 | unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq); |
381 | |
382 | pr_info("wake %s for irq %u (%s-%lu)\n" , on ? "enabled" : "disabled" , |
383 | irqd->irq, bank->name, irqd->hwirq); |
384 | |
385 | if (!on) |
386 | *our_chip->eint_wake_mask_value |= bit; |
387 | else |
388 | *our_chip->eint_wake_mask_value &= ~bit; |
389 | |
390 | return 0; |
391 | } |
392 | |
393 | static void |
394 | exynos_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata, |
395 | struct exynos_irq_chip *irq_chip) |
396 | { |
397 | struct regmap *pmu_regs; |
398 | |
399 | if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) { |
400 | dev_warn(drvdata->dev, |
401 | "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n" ); |
402 | return; |
403 | } |
404 | |
405 | pmu_regs = drvdata->retention_ctrl->priv; |
406 | dev_info(drvdata->dev, |
407 | "Setting external wakeup interrupt mask: 0x%x\n" , |
408 | *irq_chip->eint_wake_mask_value); |
409 | |
410 | regmap_write(map: pmu_regs, reg: irq_chip->eint_wake_mask_reg, |
411 | val: *irq_chip->eint_wake_mask_value); |
412 | } |
413 | |
414 | static void |
415 | s5pv210_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata, |
416 | struct exynos_irq_chip *irq_chip) |
417 | |
418 | { |
419 | void __iomem *clk_base; |
420 | |
421 | if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) { |
422 | dev_warn(drvdata->dev, |
423 | "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n" ); |
424 | return; |
425 | } |
426 | |
427 | |
428 | clk_base = (void __iomem *) drvdata->retention_ctrl->priv; |
429 | |
430 | __raw_writel(val: *irq_chip->eint_wake_mask_value, |
431 | addr: clk_base + irq_chip->eint_wake_mask_reg); |
432 | } |
433 | |
434 | static u32 eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED; |
435 | /* |
436 | * irq_chip for wakeup interrupts |
437 | */ |
438 | static const struct exynos_irq_chip s5pv210_wkup_irq_chip __initconst = { |
439 | .chip = { |
440 | .name = "s5pv210_wkup_irq_chip" , |
441 | .irq_unmask = exynos_irq_unmask, |
442 | .irq_mask = exynos_irq_mask, |
443 | .irq_ack = exynos_irq_ack, |
444 | .irq_set_type = exynos_irq_set_type, |
445 | .irq_set_wake = exynos_wkup_irq_set_wake, |
446 | .irq_request_resources = exynos_irq_request_resources, |
447 | .irq_release_resources = exynos_irq_release_resources, |
448 | }, |
449 | .eint_con = EXYNOS_WKUP_ECON_OFFSET, |
450 | .eint_mask = EXYNOS_WKUP_EMASK_OFFSET, |
451 | .eint_pend = EXYNOS_WKUP_EPEND_OFFSET, |
452 | .eint_wake_mask_value = &eint_wake_mask_value, |
453 | /* Only differences with exynos4210_wkup_irq_chip: */ |
454 | .eint_wake_mask_reg = S5PV210_EINT_WAKEUP_MASK, |
455 | .set_eint_wakeup_mask = s5pv210_pinctrl_set_eint_wakeup_mask, |
456 | }; |
457 | |
458 | static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = { |
459 | .chip = { |
460 | .name = "exynos4210_wkup_irq_chip" , |
461 | .irq_unmask = exynos_irq_unmask, |
462 | .irq_mask = exynos_irq_mask, |
463 | .irq_ack = exynos_irq_ack, |
464 | .irq_set_type = exynos_irq_set_type, |
465 | .irq_set_wake = exynos_wkup_irq_set_wake, |
466 | .irq_request_resources = exynos_irq_request_resources, |
467 | .irq_release_resources = exynos_irq_release_resources, |
468 | }, |
469 | .eint_con = EXYNOS_WKUP_ECON_OFFSET, |
470 | .eint_mask = EXYNOS_WKUP_EMASK_OFFSET, |
471 | .eint_pend = EXYNOS_WKUP_EPEND_OFFSET, |
472 | .eint_wake_mask_value = &eint_wake_mask_value, |
473 | .eint_wake_mask_reg = EXYNOS_EINT_WAKEUP_MASK, |
474 | .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask, |
475 | }; |
476 | |
477 | static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = { |
478 | .chip = { |
479 | .name = "exynos7_wkup_irq_chip" , |
480 | .irq_unmask = exynos_irq_unmask, |
481 | .irq_mask = exynos_irq_mask, |
482 | .irq_ack = exynos_irq_ack, |
483 | .irq_set_type = exynos_irq_set_type, |
484 | .irq_set_wake = exynos_wkup_irq_set_wake, |
485 | .irq_request_resources = exynos_irq_request_resources, |
486 | .irq_release_resources = exynos_irq_release_resources, |
487 | }, |
488 | .eint_con = EXYNOS7_WKUP_ECON_OFFSET, |
489 | .eint_mask = EXYNOS7_WKUP_EMASK_OFFSET, |
490 | .eint_pend = EXYNOS7_WKUP_EPEND_OFFSET, |
491 | .eint_wake_mask_value = &eint_wake_mask_value, |
492 | .eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK, |
493 | .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask, |
494 | }; |
495 | |
496 | static const struct exynos_irq_chip exynosautov920_wkup_irq_chip __initconst = { |
497 | .chip = { |
498 | .name = "exynosautov920_wkup_irq_chip" , |
499 | .irq_unmask = exynos_irq_unmask, |
500 | .irq_mask = exynos_irq_mask, |
501 | .irq_ack = exynos_irq_ack, |
502 | .irq_set_type = exynos_irq_set_type, |
503 | .irq_set_wake = exynos_wkup_irq_set_wake, |
504 | .irq_request_resources = exynos_irq_request_resources, |
505 | .irq_release_resources = exynos_irq_release_resources, |
506 | }, |
507 | .eint_wake_mask_value = &eint_wake_mask_value, |
508 | .eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK, |
509 | .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask, |
510 | }; |
511 | |
512 | /* list of external wakeup controllers supported */ |
513 | static const struct of_device_id exynos_wkup_irq_ids[] = { |
514 | { .compatible = "samsung,s5pv210-wakeup-eint" , |
515 | .data = &s5pv210_wkup_irq_chip }, |
516 | { .compatible = "samsung,exynos4210-wakeup-eint" , |
517 | .data = &exynos4210_wkup_irq_chip }, |
518 | { .compatible = "samsung,exynos7-wakeup-eint" , |
519 | .data = &exynos7_wkup_irq_chip }, |
520 | { .compatible = "samsung,exynos850-wakeup-eint" , |
521 | .data = &exynos7_wkup_irq_chip }, |
522 | { .compatible = "samsung,exynosautov9-wakeup-eint" , |
523 | .data = &exynos7_wkup_irq_chip }, |
524 | { .compatible = "samsung,exynosautov920-wakeup-eint" , |
525 | .data = &exynosautov920_wkup_irq_chip }, |
526 | { } |
527 | }; |
528 | |
529 | /* interrupt handler for wakeup interrupts 0..15 */ |
530 | static void exynos_irq_eint0_15(struct irq_desc *desc) |
531 | { |
532 | struct exynos_weint_data *eintd = irq_desc_get_handler_data(desc); |
533 | struct samsung_pin_bank *bank = eintd->bank; |
534 | struct irq_chip *chip = irq_desc_get_chip(desc); |
535 | |
536 | chained_irq_enter(chip, desc); |
537 | |
538 | generic_handle_domain_irq(domain: bank->irq_domain, hwirq: eintd->irq); |
539 | |
540 | chained_irq_exit(chip, desc); |
541 | } |
542 | |
543 | static inline void exynos_irq_demux_eint(unsigned int pend, |
544 | struct irq_domain *domain) |
545 | { |
546 | unsigned int irq; |
547 | |
548 | while (pend) { |
549 | irq = fls(x: pend) - 1; |
550 | generic_handle_domain_irq(domain, hwirq: irq); |
551 | pend &= ~(1 << irq); |
552 | } |
553 | } |
554 | |
555 | /* interrupt handler for wakeup interrupt 16 */ |
556 | static void exynos_irq_demux_eint16_31(struct irq_desc *desc) |
557 | { |
558 | struct irq_chip *chip = irq_desc_get_chip(desc); |
559 | struct exynos_muxed_weint_data *eintd = irq_desc_get_handler_data(desc); |
560 | unsigned int pend; |
561 | unsigned int mask; |
562 | int i; |
563 | |
564 | chained_irq_enter(chip, desc); |
565 | |
566 | for (i = 0; i < eintd->nr_banks; ++i) { |
567 | struct samsung_pin_bank *b = eintd->banks[i]; |
568 | pend = readl(addr: b->eint_base + b->irq_chip->eint_pend |
569 | + b->eint_offset); |
570 | mask = readl(addr: b->eint_base + b->irq_chip->eint_mask |
571 | + b->eint_offset); |
572 | exynos_irq_demux_eint(pend: pend & ~mask, domain: b->irq_domain); |
573 | } |
574 | |
575 | chained_irq_exit(chip, desc); |
576 | } |
577 | |
578 | /* |
579 | * exynos_eint_wkup_init() - setup handling of external wakeup interrupts. |
580 | * @d: driver data of samsung pinctrl driver. |
581 | */ |
582 | __init int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d) |
583 | { |
584 | struct device *dev = d->dev; |
585 | struct device_node *wkup_np = NULL; |
586 | struct device_node *np; |
587 | struct samsung_pin_bank *bank; |
588 | struct exynos_weint_data *weint_data; |
589 | struct exynos_muxed_weint_data *muxed_data; |
590 | const struct exynos_irq_chip *irq_chip; |
591 | unsigned int muxed_banks = 0; |
592 | unsigned int i; |
593 | int idx, irq; |
594 | |
595 | for_each_child_of_node(dev->of_node, np) { |
596 | const struct of_device_id *match; |
597 | |
598 | match = of_match_node(matches: exynos_wkup_irq_ids, node: np); |
599 | if (match) { |
600 | irq_chip = match->data; |
601 | wkup_np = np; |
602 | break; |
603 | } |
604 | } |
605 | if (!wkup_np) |
606 | return -ENODEV; |
607 | |
608 | bank = d->pin_banks; |
609 | for (i = 0; i < d->nr_banks; ++i, ++bank) { |
610 | if (bank->eint_type != EINT_TYPE_WKUP) |
611 | continue; |
612 | |
613 | bank->irq_chip = devm_kmemdup(dev, src: irq_chip, len: sizeof(*irq_chip), |
614 | GFP_KERNEL); |
615 | if (!bank->irq_chip) { |
616 | of_node_put(node: wkup_np); |
617 | return -ENOMEM; |
618 | } |
619 | bank->irq_chip->chip.name = bank->name; |
620 | |
621 | bank->irq_domain = irq_domain_create_linear(fwnode: bank->fwnode, |
622 | size: bank->nr_pins, ops: &exynos_eint_irqd_ops, host_data: bank); |
623 | if (!bank->irq_domain) { |
624 | dev_err(dev, "wkup irq domain add failed\n" ); |
625 | of_node_put(node: wkup_np); |
626 | return -ENXIO; |
627 | } |
628 | |
629 | if (!fwnode_property_present(fwnode: bank->fwnode, propname: "interrupts" )) { |
630 | bank->eint_type = EINT_TYPE_WKUP_MUX; |
631 | ++muxed_banks; |
632 | continue; |
633 | } |
634 | |
635 | weint_data = devm_kcalloc(dev, |
636 | n: bank->nr_pins, size: sizeof(*weint_data), |
637 | GFP_KERNEL); |
638 | if (!weint_data) { |
639 | of_node_put(node: wkup_np); |
640 | return -ENOMEM; |
641 | } |
642 | |
643 | for (idx = 0; idx < bank->nr_pins; ++idx) { |
644 | irq = irq_of_parse_and_map(to_of_node(bank->fwnode), index: idx); |
645 | if (!irq) { |
646 | dev_err(dev, "irq number for eint-%s-%d not found\n" , |
647 | bank->name, idx); |
648 | continue; |
649 | } |
650 | weint_data[idx].irq = idx; |
651 | weint_data[idx].bank = bank; |
652 | irq_set_chained_handler_and_data(irq, |
653 | handle: exynos_irq_eint0_15, |
654 | data: &weint_data[idx]); |
655 | } |
656 | } |
657 | |
658 | if (!muxed_banks) { |
659 | of_node_put(node: wkup_np); |
660 | return 0; |
661 | } |
662 | |
663 | irq = irq_of_parse_and_map(node: wkup_np, index: 0); |
664 | of_node_put(node: wkup_np); |
665 | if (!irq) { |
666 | dev_err(dev, "irq number for muxed EINTs not found\n" ); |
667 | return 0; |
668 | } |
669 | |
670 | muxed_data = devm_kzalloc(dev, size: sizeof(*muxed_data) |
671 | + muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL); |
672 | if (!muxed_data) |
673 | return -ENOMEM; |
674 | muxed_data->nr_banks = muxed_banks; |
675 | |
676 | irq_set_chained_handler_and_data(irq, handle: exynos_irq_demux_eint16_31, |
677 | data: muxed_data); |
678 | |
679 | bank = d->pin_banks; |
680 | idx = 0; |
681 | for (i = 0; i < d->nr_banks; ++i, ++bank) { |
682 | if (bank->eint_type != EINT_TYPE_WKUP_MUX) |
683 | continue; |
684 | |
685 | muxed_data->banks[idx++] = bank; |
686 | } |
687 | |
688 | return 0; |
689 | } |
690 | |
691 | static void exynos_pinctrl_suspend_bank( |
692 | struct samsung_pinctrl_drv_data *drvdata, |
693 | struct samsung_pin_bank *bank) |
694 | { |
695 | struct exynos_eint_gpio_save *save = bank->soc_priv; |
696 | const void __iomem *regs = bank->eint_base; |
697 | |
698 | save->eint_con = readl(addr: regs + EXYNOS_GPIO_ECON_OFFSET |
699 | + bank->eint_offset); |
700 | save->eint_fltcon0 = readl(addr: regs + EXYNOS_GPIO_EFLTCON_OFFSET |
701 | + 2 * bank->eint_offset); |
702 | save->eint_fltcon1 = readl(addr: regs + EXYNOS_GPIO_EFLTCON_OFFSET |
703 | + 2 * bank->eint_offset + 4); |
704 | save->eint_mask = readl(addr: regs + bank->irq_chip->eint_mask |
705 | + bank->eint_offset); |
706 | |
707 | pr_debug("%s: save con %#010x\n" , bank->name, save->eint_con); |
708 | pr_debug("%s: save fltcon0 %#010x\n" , bank->name, save->eint_fltcon0); |
709 | pr_debug("%s: save fltcon1 %#010x\n" , bank->name, save->eint_fltcon1); |
710 | pr_debug("%s: save mask %#010x\n" , bank->name, save->eint_mask); |
711 | } |
712 | |
713 | static void exynosauto_pinctrl_suspend_bank(struct samsung_pinctrl_drv_data *drvdata, |
714 | struct samsung_pin_bank *bank) |
715 | { |
716 | struct exynos_eint_gpio_save *save = bank->soc_priv; |
717 | const void __iomem *regs = bank->eint_base; |
718 | |
719 | save->eint_con = readl(addr: regs + bank->pctl_offset + bank->eint_con_offset); |
720 | save->eint_mask = readl(addr: regs + bank->pctl_offset + bank->eint_mask_offset); |
721 | |
722 | pr_debug("%s: save con %#010x\n" , bank->name, save->eint_con); |
723 | pr_debug("%s: save mask %#010x\n" , bank->name, save->eint_mask); |
724 | } |
725 | |
726 | void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata) |
727 | { |
728 | struct samsung_pin_bank *bank = drvdata->pin_banks; |
729 | struct exynos_irq_chip *irq_chip = NULL; |
730 | int i; |
731 | |
732 | for (i = 0; i < drvdata->nr_banks; ++i, ++bank) { |
733 | if (bank->eint_type == EINT_TYPE_GPIO) { |
734 | if (bank->eint_con_offset) |
735 | exynosauto_pinctrl_suspend_bank(drvdata, bank); |
736 | else |
737 | exynos_pinctrl_suspend_bank(drvdata, bank); |
738 | } |
739 | else if (bank->eint_type == EINT_TYPE_WKUP) { |
740 | if (!irq_chip) { |
741 | irq_chip = bank->irq_chip; |
742 | irq_chip->set_eint_wakeup_mask(drvdata, |
743 | irq_chip); |
744 | } |
745 | } |
746 | } |
747 | } |
748 | |
749 | static void exynos_pinctrl_resume_bank( |
750 | struct samsung_pinctrl_drv_data *drvdata, |
751 | struct samsung_pin_bank *bank) |
752 | { |
753 | struct exynos_eint_gpio_save *save = bank->soc_priv; |
754 | void __iomem *regs = bank->eint_base; |
755 | |
756 | pr_debug("%s: con %#010x => %#010x\n" , bank->name, |
757 | readl(regs + EXYNOS_GPIO_ECON_OFFSET |
758 | + bank->eint_offset), save->eint_con); |
759 | pr_debug("%s: fltcon0 %#010x => %#010x\n" , bank->name, |
760 | readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET |
761 | + 2 * bank->eint_offset), save->eint_fltcon0); |
762 | pr_debug("%s: fltcon1 %#010x => %#010x\n" , bank->name, |
763 | readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET |
764 | + 2 * bank->eint_offset + 4), save->eint_fltcon1); |
765 | pr_debug("%s: mask %#010x => %#010x\n" , bank->name, |
766 | readl(regs + bank->irq_chip->eint_mask |
767 | + bank->eint_offset), save->eint_mask); |
768 | |
769 | writel(val: save->eint_con, addr: regs + EXYNOS_GPIO_ECON_OFFSET |
770 | + bank->eint_offset); |
771 | writel(val: save->eint_fltcon0, addr: regs + EXYNOS_GPIO_EFLTCON_OFFSET |
772 | + 2 * bank->eint_offset); |
773 | writel(val: save->eint_fltcon1, addr: regs + EXYNOS_GPIO_EFLTCON_OFFSET |
774 | + 2 * bank->eint_offset + 4); |
775 | writel(val: save->eint_mask, addr: regs + bank->irq_chip->eint_mask |
776 | + bank->eint_offset); |
777 | } |
778 | |
779 | static void exynosauto_pinctrl_resume_bank(struct samsung_pinctrl_drv_data *drvdata, |
780 | struct samsung_pin_bank *bank) |
781 | { |
782 | struct exynos_eint_gpio_save *save = bank->soc_priv; |
783 | void __iomem *regs = bank->eint_base; |
784 | |
785 | pr_debug("%s: con %#010x => %#010x\n" , bank->name, |
786 | readl(regs + bank->pctl_offset + bank->eint_con_offset), save->eint_con); |
787 | pr_debug("%s: mask %#010x => %#010x\n" , bank->name, |
788 | readl(regs + bank->pctl_offset + bank->eint_mask_offset), save->eint_mask); |
789 | |
790 | writel(val: save->eint_con, addr: regs + bank->pctl_offset + bank->eint_con_offset); |
791 | writel(val: save->eint_mask, addr: regs + bank->pctl_offset + bank->eint_mask_offset); |
792 | } |
793 | |
794 | void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata) |
795 | { |
796 | struct samsung_pin_bank *bank = drvdata->pin_banks; |
797 | int i; |
798 | |
799 | for (i = 0; i < drvdata->nr_banks; ++i, ++bank) |
800 | if (bank->eint_type == EINT_TYPE_GPIO) { |
801 | if (bank->eint_con_offset) |
802 | exynosauto_pinctrl_resume_bank(drvdata, bank); |
803 | else |
804 | exynos_pinctrl_resume_bank(drvdata, bank); |
805 | } |
806 | } |
807 | |
808 | static void exynos_retention_enable(struct samsung_pinctrl_drv_data *drvdata) |
809 | { |
810 | if (drvdata->retention_ctrl->refcnt) |
811 | atomic_inc(v: drvdata->retention_ctrl->refcnt); |
812 | } |
813 | |
814 | static void exynos_retention_disable(struct samsung_pinctrl_drv_data *drvdata) |
815 | { |
816 | struct samsung_retention_ctrl *ctrl = drvdata->retention_ctrl; |
817 | struct regmap *pmu_regs = ctrl->priv; |
818 | int i; |
819 | |
820 | if (ctrl->refcnt && !atomic_dec_and_test(v: ctrl->refcnt)) |
821 | return; |
822 | |
823 | for (i = 0; i < ctrl->nr_regs; i++) |
824 | regmap_write(map: pmu_regs, reg: ctrl->regs[i], val: ctrl->value); |
825 | } |
826 | |
827 | struct samsung_retention_ctrl * |
828 | exynos_retention_init(struct samsung_pinctrl_drv_data *drvdata, |
829 | const struct samsung_retention_data *data) |
830 | { |
831 | struct samsung_retention_ctrl *ctrl; |
832 | struct regmap *pmu_regs; |
833 | int i; |
834 | |
835 | ctrl = devm_kzalloc(dev: drvdata->dev, size: sizeof(*ctrl), GFP_KERNEL); |
836 | if (!ctrl) |
837 | return ERR_PTR(error: -ENOMEM); |
838 | |
839 | pmu_regs = exynos_get_pmu_regmap(); |
840 | if (IS_ERR(ptr: pmu_regs)) |
841 | return ERR_CAST(ptr: pmu_regs); |
842 | |
843 | ctrl->priv = pmu_regs; |
844 | ctrl->regs = data->regs; |
845 | ctrl->nr_regs = data->nr_regs; |
846 | ctrl->value = data->value; |
847 | ctrl->refcnt = data->refcnt; |
848 | ctrl->enable = exynos_retention_enable; |
849 | ctrl->disable = exynos_retention_disable; |
850 | |
851 | /* Ensure that retention is disabled on driver init */ |
852 | for (i = 0; i < ctrl->nr_regs; i++) |
853 | regmap_write(map: pmu_regs, reg: ctrl->regs[i], val: ctrl->value); |
854 | |
855 | return ctrl; |
856 | } |
857 | |