1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // S3C64xx specific support for pinctrl-samsung driver. |
4 | // |
5 | // Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> |
6 | // |
7 | // Based on pinctrl-exynos.c, please see the file for original copyrights. |
8 | // |
9 | // This file contains the Samsung S3C64xx specific information required by the |
10 | // the Samsung pinctrl/gpiolib driver. It also includes the implementation of |
11 | // external gpio and wakeup interrupt support. |
12 | |
13 | #include <linux/init.h> |
14 | #include <linux/device.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/irqdomain.h> |
17 | #include <linux/irq.h> |
18 | #include <linux/of_irq.h> |
19 | #include <linux/io.h> |
20 | #include <linux/irqchip/chained_irq.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/err.h> |
23 | |
24 | #include "pinctrl-samsung.h" |
25 | |
26 | #define NUM_EINT0 28 |
27 | #define NUM_EINT0_IRQ 4 |
28 | #define EINT_MAX_PER_REG 16 |
29 | #define EINT_MAX_PER_GROUP 16 |
30 | |
31 | /* External GPIO and wakeup interrupt related definitions */ |
32 | #define SVC_GROUP_SHIFT 4 |
33 | #define SVC_GROUP_MASK 0xf |
34 | #define SVC_NUM_MASK 0xf |
35 | #define SVC_GROUP(x) ((x >> SVC_GROUP_SHIFT) & \ |
36 | SVC_GROUP_MASK) |
37 | |
38 | #define EINT12CON_REG 0x200 |
39 | #define EINT12MASK_REG 0x240 |
40 | #define EINT12PEND_REG 0x260 |
41 | |
42 | #define EINT_OFFS(i) ((i) % (2 * EINT_MAX_PER_GROUP)) |
43 | #define EINT_GROUP(i) ((i) / EINT_MAX_PER_GROUP) |
44 | #define EINT_REG(g) (4 * ((g) / 2)) |
45 | |
46 | #define EINTCON_REG(i) (EINT12CON_REG + EINT_REG(EINT_GROUP(i))) |
47 | #define EINTMASK_REG(i) (EINT12MASK_REG + EINT_REG(EINT_GROUP(i))) |
48 | #define EINTPEND_REG(i) (EINT12PEND_REG + EINT_REG(EINT_GROUP(i))) |
49 | |
50 | #define SERVICE_REG 0x284 |
51 | #define SERVICEPEND_REG 0x288 |
52 | |
53 | #define EINT0CON0_REG 0x900 |
54 | #define EINT0MASK_REG 0x920 |
55 | #define EINT0PEND_REG 0x924 |
56 | |
57 | /* S3C64xx specific external interrupt trigger types */ |
58 | #define EINT_LEVEL_LOW 0 |
59 | #define EINT_LEVEL_HIGH 1 |
60 | #define EINT_EDGE_FALLING 2 |
61 | #define EINT_EDGE_RISING 4 |
62 | #define EINT_EDGE_BOTH 6 |
63 | #define EINT_CON_MASK 0xF |
64 | #define EINT_CON_LEN 4 |
65 | |
66 | static const struct samsung_pin_bank_type bank_type_4bit_off = { |
67 | .fld_width = { 4, 1, 2, 0, 2, 2, }, |
68 | .reg_offset = { 0x00, 0x04, 0x08, 0, 0x0c, 0x10, }, |
69 | }; |
70 | |
71 | static const struct samsung_pin_bank_type bank_type_4bit_alive = { |
72 | .fld_width = { 4, 1, 2, }, |
73 | .reg_offset = { 0x00, 0x04, 0x08, }, |
74 | }; |
75 | |
76 | static const struct samsung_pin_bank_type bank_type_4bit2_off = { |
77 | .fld_width = { 4, 1, 2, 0, 2, 2, }, |
78 | .reg_offset = { 0x00, 0x08, 0x0c, 0, 0x10, 0x14, }, |
79 | }; |
80 | |
81 | static const struct samsung_pin_bank_type bank_type_4bit2_alive = { |
82 | .fld_width = { 4, 1, 2, }, |
83 | .reg_offset = { 0x00, 0x08, 0x0c, }, |
84 | }; |
85 | |
86 | static const struct samsung_pin_bank_type bank_type_2bit_off = { |
87 | .fld_width = { 2, 1, 2, 0, 2, 2, }, |
88 | .reg_offset = { 0x00, 0x04, 0x08, 0, 0x0c, 0x10, }, |
89 | }; |
90 | |
91 | static const struct samsung_pin_bank_type bank_type_2bit_alive = { |
92 | .fld_width = { 2, 1, 2, }, |
93 | .reg_offset = { 0x00, 0x04, 0x08, }, |
94 | }; |
95 | |
96 | #define PIN_BANK_4BIT(pins, reg, id) \ |
97 | { \ |
98 | .type = &bank_type_4bit_off, \ |
99 | .pctl_offset = reg, \ |
100 | .nr_pins = pins, \ |
101 | .eint_type = EINT_TYPE_NONE, \ |
102 | .name = id \ |
103 | } |
104 | |
105 | #define PIN_BANK_4BIT_EINTG(pins, reg, id, eoffs) \ |
106 | { \ |
107 | .type = &bank_type_4bit_off, \ |
108 | .pctl_offset = reg, \ |
109 | .nr_pins = pins, \ |
110 | .eint_type = EINT_TYPE_GPIO, \ |
111 | .eint_func = 7, \ |
112 | .eint_mask = (1 << (pins)) - 1, \ |
113 | .eint_offset = eoffs, \ |
114 | .name = id \ |
115 | } |
116 | |
117 | #define PIN_BANK_4BIT_EINTW(pins, reg, id, eoffs, emask) \ |
118 | { \ |
119 | .type = &bank_type_4bit_alive,\ |
120 | .pctl_offset = reg, \ |
121 | .nr_pins = pins, \ |
122 | .eint_type = EINT_TYPE_WKUP, \ |
123 | .eint_func = 3, \ |
124 | .eint_mask = emask, \ |
125 | .eint_offset = eoffs, \ |
126 | .name = id \ |
127 | } |
128 | |
129 | #define PIN_BANK_4BIT2_EINTG(pins, reg, id, eoffs) \ |
130 | { \ |
131 | .type = &bank_type_4bit2_off, \ |
132 | .pctl_offset = reg, \ |
133 | .nr_pins = pins, \ |
134 | .eint_type = EINT_TYPE_GPIO, \ |
135 | .eint_func = 7, \ |
136 | .eint_mask = (1 << (pins)) - 1, \ |
137 | .eint_offset = eoffs, \ |
138 | .name = id \ |
139 | } |
140 | |
141 | #define PIN_BANK_4BIT2_EINTW(pins, reg, id, eoffs, emask) \ |
142 | { \ |
143 | .type = &bank_type_4bit2_alive,\ |
144 | .pctl_offset = reg, \ |
145 | .nr_pins = pins, \ |
146 | .eint_type = EINT_TYPE_WKUP, \ |
147 | .eint_func = 3, \ |
148 | .eint_mask = emask, \ |
149 | .eint_offset = eoffs, \ |
150 | .name = id \ |
151 | } |
152 | |
153 | #define PIN_BANK_4BIT2_ALIVE(pins, reg, id) \ |
154 | { \ |
155 | .type = &bank_type_4bit2_alive,\ |
156 | .pctl_offset = reg, \ |
157 | .nr_pins = pins, \ |
158 | .eint_type = EINT_TYPE_NONE, \ |
159 | .name = id \ |
160 | } |
161 | |
162 | #define PIN_BANK_2BIT(pins, reg, id) \ |
163 | { \ |
164 | .type = &bank_type_2bit_off, \ |
165 | .pctl_offset = reg, \ |
166 | .nr_pins = pins, \ |
167 | .eint_type = EINT_TYPE_NONE, \ |
168 | .name = id \ |
169 | } |
170 | |
171 | #define PIN_BANK_2BIT_EINTG(pins, reg, id, eoffs, emask) \ |
172 | { \ |
173 | .type = &bank_type_2bit_off, \ |
174 | .pctl_offset = reg, \ |
175 | .nr_pins = pins, \ |
176 | .eint_type = EINT_TYPE_GPIO, \ |
177 | .eint_func = 3, \ |
178 | .eint_mask = emask, \ |
179 | .eint_offset = eoffs, \ |
180 | .name = id \ |
181 | } |
182 | |
183 | #define PIN_BANK_2BIT_EINTW(pins, reg, id, eoffs) \ |
184 | { \ |
185 | .type = &bank_type_2bit_alive,\ |
186 | .pctl_offset = reg, \ |
187 | .nr_pins = pins, \ |
188 | .eint_type = EINT_TYPE_WKUP, \ |
189 | .eint_func = 2, \ |
190 | .eint_mask = (1 << (pins)) - 1, \ |
191 | .eint_offset = eoffs, \ |
192 | .name = id \ |
193 | } |
194 | |
195 | /** |
196 | * struct s3c64xx_eint0_data - EINT0 common data |
197 | * @drvdata: pin controller driver data |
198 | * @domains: IRQ domains of particular EINT0 interrupts |
199 | * @pins: pin offsets inside of banks of particular EINT0 interrupts |
200 | */ |
201 | struct s3c64xx_eint0_data { |
202 | struct samsung_pinctrl_drv_data *drvdata; |
203 | struct irq_domain *domains[NUM_EINT0]; |
204 | u8 pins[NUM_EINT0]; |
205 | }; |
206 | |
207 | /** |
208 | * struct s3c64xx_eint0_domain_data - EINT0 per-domain data |
209 | * @bank: pin bank related to the domain |
210 | * @eints: EINT0 interrupts related to the domain |
211 | */ |
212 | struct s3c64xx_eint0_domain_data { |
213 | struct samsung_pin_bank *bank; |
214 | u8 eints[]; |
215 | }; |
216 | |
217 | /** |
218 | * struct s3c64xx_eint_gpio_data - GPIO EINT data |
219 | * @drvdata: pin controller driver data |
220 | * @domains: array of domains related to EINT interrupt groups |
221 | */ |
222 | struct s3c64xx_eint_gpio_data { |
223 | struct samsung_pinctrl_drv_data *drvdata; |
224 | struct irq_domain *domains[]; |
225 | }; |
226 | |
227 | /* |
228 | * Common functions for S3C64xx EINT configuration |
229 | */ |
230 | |
231 | static int s3c64xx_irq_get_trigger(unsigned int type) |
232 | { |
233 | int trigger; |
234 | |
235 | switch (type) { |
236 | case IRQ_TYPE_EDGE_RISING: |
237 | trigger = EINT_EDGE_RISING; |
238 | break; |
239 | case IRQ_TYPE_EDGE_FALLING: |
240 | trigger = EINT_EDGE_FALLING; |
241 | break; |
242 | case IRQ_TYPE_EDGE_BOTH: |
243 | trigger = EINT_EDGE_BOTH; |
244 | break; |
245 | case IRQ_TYPE_LEVEL_HIGH: |
246 | trigger = EINT_LEVEL_HIGH; |
247 | break; |
248 | case IRQ_TYPE_LEVEL_LOW: |
249 | trigger = EINT_LEVEL_LOW; |
250 | break; |
251 | default: |
252 | return -EINVAL; |
253 | } |
254 | |
255 | return trigger; |
256 | } |
257 | |
258 | static void s3c64xx_irq_set_handler(struct irq_data *d, unsigned int type) |
259 | { |
260 | /* Edge- and level-triggered interrupts need different handlers */ |
261 | if (type & IRQ_TYPE_EDGE_BOTH) |
262 | irq_set_handler_locked(data: d, handler: handle_edge_irq); |
263 | else |
264 | irq_set_handler_locked(data: d, handler: handle_level_irq); |
265 | } |
266 | |
267 | static void s3c64xx_irq_set_function(struct samsung_pinctrl_drv_data *d, |
268 | struct samsung_pin_bank *bank, int pin) |
269 | { |
270 | const struct samsung_pin_bank_type *bank_type = bank->type; |
271 | unsigned long flags; |
272 | void __iomem *reg; |
273 | u8 shift; |
274 | u32 mask; |
275 | u32 val; |
276 | |
277 | /* Make sure that pin is configured as interrupt */ |
278 | reg = d->virt_base + bank->pctl_offset; |
279 | shift = pin; |
280 | if (bank_type->fld_width[PINCFG_TYPE_FUNC] * shift >= 32) { |
281 | /* 4-bit bank type with 2 con regs */ |
282 | reg += 4; |
283 | shift -= 8; |
284 | } |
285 | |
286 | shift = shift * bank_type->fld_width[PINCFG_TYPE_FUNC]; |
287 | mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1; |
288 | |
289 | raw_spin_lock_irqsave(&bank->slock, flags); |
290 | |
291 | val = readl(addr: reg); |
292 | val &= ~(mask << shift); |
293 | val |= bank->eint_func << shift; |
294 | writel(val, addr: reg); |
295 | |
296 | raw_spin_unlock_irqrestore(&bank->slock, flags); |
297 | } |
298 | |
299 | /* |
300 | * Functions for EINT GPIO configuration (EINT groups 1-9) |
301 | */ |
302 | |
303 | static inline void s3c64xx_gpio_irq_set_mask(struct irq_data *irqd, bool mask) |
304 | { |
305 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
306 | struct samsung_pinctrl_drv_data *d = bank->drvdata; |
307 | unsigned char index = EINT_OFFS(bank->eint_offset) + irqd->hwirq; |
308 | void __iomem *reg = d->virt_base + EINTMASK_REG(bank->eint_offset); |
309 | u32 val; |
310 | |
311 | val = readl(addr: reg); |
312 | if (mask) |
313 | val |= 1 << index; |
314 | else |
315 | val &= ~(1 << index); |
316 | writel(val, addr: reg); |
317 | } |
318 | |
319 | static void s3c64xx_gpio_irq_unmask(struct irq_data *irqd) |
320 | { |
321 | s3c64xx_gpio_irq_set_mask(irqd, mask: false); |
322 | } |
323 | |
324 | static void s3c64xx_gpio_irq_mask(struct irq_data *irqd) |
325 | { |
326 | s3c64xx_gpio_irq_set_mask(irqd, mask: true); |
327 | } |
328 | |
329 | static void s3c64xx_gpio_irq_ack(struct irq_data *irqd) |
330 | { |
331 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
332 | struct samsung_pinctrl_drv_data *d = bank->drvdata; |
333 | unsigned char index = EINT_OFFS(bank->eint_offset) + irqd->hwirq; |
334 | void __iomem *reg = d->virt_base + EINTPEND_REG(bank->eint_offset); |
335 | |
336 | writel(val: 1 << index, addr: reg); |
337 | } |
338 | |
339 | static int s3c64xx_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) |
340 | { |
341 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(d: irqd); |
342 | struct samsung_pinctrl_drv_data *d = bank->drvdata; |
343 | void __iomem *reg; |
344 | int trigger; |
345 | u8 shift; |
346 | u32 val; |
347 | |
348 | trigger = s3c64xx_irq_get_trigger(type); |
349 | if (trigger < 0) { |
350 | pr_err("unsupported external interrupt type\n" ); |
351 | return -EINVAL; |
352 | } |
353 | |
354 | s3c64xx_irq_set_handler(d: irqd, type); |
355 | |
356 | /* Set up interrupt trigger */ |
357 | reg = d->virt_base + EINTCON_REG(bank->eint_offset); |
358 | shift = EINT_OFFS(bank->eint_offset) + irqd->hwirq; |
359 | shift = 4 * (shift / 4); /* 4 EINTs per trigger selector */ |
360 | |
361 | val = readl(addr: reg); |
362 | val &= ~(EINT_CON_MASK << shift); |
363 | val |= trigger << shift; |
364 | writel(val, addr: reg); |
365 | |
366 | s3c64xx_irq_set_function(d, bank, pin: irqd->hwirq); |
367 | |
368 | return 0; |
369 | } |
370 | |
371 | /* |
372 | * irq_chip for gpio interrupts. |
373 | */ |
374 | static struct irq_chip s3c64xx_gpio_irq_chip = { |
375 | .name = "GPIO" , |
376 | .irq_unmask = s3c64xx_gpio_irq_unmask, |
377 | .irq_mask = s3c64xx_gpio_irq_mask, |
378 | .irq_ack = s3c64xx_gpio_irq_ack, |
379 | .irq_set_type = s3c64xx_gpio_irq_set_type, |
380 | }; |
381 | |
382 | static int s3c64xx_gpio_irq_map(struct irq_domain *h, unsigned int virq, |
383 | irq_hw_number_t hw) |
384 | { |
385 | struct samsung_pin_bank *bank = h->host_data; |
386 | |
387 | if (!(bank->eint_mask & (1 << hw))) |
388 | return -EINVAL; |
389 | |
390 | irq_set_chip_and_handler(irq: virq, |
391 | chip: &s3c64xx_gpio_irq_chip, handle: handle_level_irq); |
392 | irq_set_chip_data(irq: virq, data: bank); |
393 | |
394 | return 0; |
395 | } |
396 | |
397 | /* |
398 | * irq domain callbacks for external gpio interrupt controller. |
399 | */ |
400 | static const struct irq_domain_ops s3c64xx_gpio_irqd_ops = { |
401 | .map = s3c64xx_gpio_irq_map, |
402 | .xlate = irq_domain_xlate_twocell, |
403 | }; |
404 | |
405 | static void s3c64xx_eint_gpio_irq(struct irq_desc *desc) |
406 | { |
407 | struct irq_chip *chip = irq_desc_get_chip(desc); |
408 | struct s3c64xx_eint_gpio_data *data = irq_desc_get_handler_data(desc); |
409 | struct samsung_pinctrl_drv_data *drvdata = data->drvdata; |
410 | |
411 | chained_irq_enter(chip, desc); |
412 | |
413 | do { |
414 | unsigned int svc; |
415 | unsigned int group; |
416 | unsigned int pin; |
417 | int ret; |
418 | |
419 | svc = readl(addr: drvdata->virt_base + SERVICE_REG); |
420 | group = SVC_GROUP(svc); |
421 | pin = svc & SVC_NUM_MASK; |
422 | |
423 | if (!group) |
424 | break; |
425 | |
426 | /* Group 1 is used for two pin banks */ |
427 | if (group == 1) { |
428 | if (pin < 8) |
429 | group = 0; |
430 | else |
431 | pin -= 8; |
432 | } |
433 | |
434 | ret = generic_handle_domain_irq(domain: data->domains[group], hwirq: pin); |
435 | /* |
436 | * Something must be really wrong if an unmapped EINT |
437 | * was unmasked... |
438 | */ |
439 | BUG_ON(ret); |
440 | } while (1); |
441 | |
442 | chained_irq_exit(chip, desc); |
443 | } |
444 | |
445 | /** |
446 | * s3c64xx_eint_gpio_init() - setup handling of external gpio interrupts. |
447 | * @d: driver data of samsung pinctrl driver. |
448 | */ |
449 | static int s3c64xx_eint_gpio_init(struct samsung_pinctrl_drv_data *d) |
450 | { |
451 | struct s3c64xx_eint_gpio_data *data; |
452 | struct samsung_pin_bank *bank; |
453 | struct device *dev = d->dev; |
454 | unsigned int nr_domains; |
455 | unsigned int i; |
456 | |
457 | if (!d->irq) { |
458 | dev_err(dev, "irq number not available\n" ); |
459 | return -EINVAL; |
460 | } |
461 | |
462 | nr_domains = 0; |
463 | bank = d->pin_banks; |
464 | for (i = 0; i < d->nr_banks; ++i, ++bank) { |
465 | unsigned int nr_eints; |
466 | unsigned int mask; |
467 | |
468 | if (bank->eint_type != EINT_TYPE_GPIO) |
469 | continue; |
470 | |
471 | mask = bank->eint_mask; |
472 | nr_eints = fls(x: mask); |
473 | |
474 | bank->irq_domain = irq_domain_create_linear(fwnode: bank->fwnode, |
475 | size: nr_eints, ops: &s3c64xx_gpio_irqd_ops, host_data: bank); |
476 | if (!bank->irq_domain) { |
477 | dev_err(dev, "gpio irq domain add failed\n" ); |
478 | return -ENXIO; |
479 | } |
480 | |
481 | ++nr_domains; |
482 | } |
483 | |
484 | data = devm_kzalloc(dev, struct_size(data, domains, nr_domains), |
485 | GFP_KERNEL); |
486 | if (!data) |
487 | return -ENOMEM; |
488 | data->drvdata = d; |
489 | |
490 | bank = d->pin_banks; |
491 | nr_domains = 0; |
492 | for (i = 0; i < d->nr_banks; ++i, ++bank) { |
493 | if (bank->eint_type != EINT_TYPE_GPIO) |
494 | continue; |
495 | |
496 | data->domains[nr_domains++] = bank->irq_domain; |
497 | } |
498 | |
499 | irq_set_chained_handler_and_data(irq: d->irq, handle: s3c64xx_eint_gpio_irq, data); |
500 | |
501 | return 0; |
502 | } |
503 | |
504 | /* |
505 | * Functions for configuration of EINT0 wake-up interrupts |
506 | */ |
507 | |
508 | static inline void s3c64xx_eint0_irq_set_mask(struct irq_data *irqd, bool mask) |
509 | { |
510 | struct s3c64xx_eint0_domain_data *ddata = |
511 | irq_data_get_irq_chip_data(d: irqd); |
512 | struct samsung_pinctrl_drv_data *d = ddata->bank->drvdata; |
513 | u32 val; |
514 | |
515 | val = readl(addr: d->virt_base + EINT0MASK_REG); |
516 | if (mask) |
517 | val |= 1 << ddata->eints[irqd->hwirq]; |
518 | else |
519 | val &= ~(1 << ddata->eints[irqd->hwirq]); |
520 | writel(val, addr: d->virt_base + EINT0MASK_REG); |
521 | } |
522 | |
523 | static void s3c64xx_eint0_irq_unmask(struct irq_data *irqd) |
524 | { |
525 | s3c64xx_eint0_irq_set_mask(irqd, mask: false); |
526 | } |
527 | |
528 | static void s3c64xx_eint0_irq_mask(struct irq_data *irqd) |
529 | { |
530 | s3c64xx_eint0_irq_set_mask(irqd, mask: true); |
531 | } |
532 | |
533 | static void s3c64xx_eint0_irq_ack(struct irq_data *irqd) |
534 | { |
535 | struct s3c64xx_eint0_domain_data *ddata = |
536 | irq_data_get_irq_chip_data(d: irqd); |
537 | struct samsung_pinctrl_drv_data *d = ddata->bank->drvdata; |
538 | |
539 | writel(val: 1 << ddata->eints[irqd->hwirq], |
540 | addr: d->virt_base + EINT0PEND_REG); |
541 | } |
542 | |
543 | static int s3c64xx_eint0_irq_set_type(struct irq_data *irqd, unsigned int type) |
544 | { |
545 | struct s3c64xx_eint0_domain_data *ddata = |
546 | irq_data_get_irq_chip_data(d: irqd); |
547 | struct samsung_pin_bank *bank = ddata->bank; |
548 | struct samsung_pinctrl_drv_data *d = bank->drvdata; |
549 | void __iomem *reg; |
550 | int trigger; |
551 | u8 shift; |
552 | u32 val; |
553 | |
554 | trigger = s3c64xx_irq_get_trigger(type); |
555 | if (trigger < 0) { |
556 | pr_err("unsupported external interrupt type\n" ); |
557 | return -EINVAL; |
558 | } |
559 | |
560 | s3c64xx_irq_set_handler(d: irqd, type); |
561 | |
562 | /* Set up interrupt trigger */ |
563 | reg = d->virt_base + EINT0CON0_REG; |
564 | shift = ddata->eints[irqd->hwirq]; |
565 | if (shift >= EINT_MAX_PER_REG) { |
566 | reg += 4; |
567 | shift -= EINT_MAX_PER_REG; |
568 | } |
569 | shift = EINT_CON_LEN * (shift / 2); |
570 | |
571 | val = readl(addr: reg); |
572 | val &= ~(EINT_CON_MASK << shift); |
573 | val |= trigger << shift; |
574 | writel(val, addr: reg); |
575 | |
576 | s3c64xx_irq_set_function(d, bank, pin: irqd->hwirq); |
577 | |
578 | return 0; |
579 | } |
580 | |
581 | /* |
582 | * irq_chip for wakeup interrupts |
583 | */ |
584 | static struct irq_chip s3c64xx_eint0_irq_chip = { |
585 | .name = "EINT0" , |
586 | .irq_unmask = s3c64xx_eint0_irq_unmask, |
587 | .irq_mask = s3c64xx_eint0_irq_mask, |
588 | .irq_ack = s3c64xx_eint0_irq_ack, |
589 | .irq_set_type = s3c64xx_eint0_irq_set_type, |
590 | }; |
591 | |
592 | static inline void s3c64xx_irq_demux_eint(struct irq_desc *desc, u32 range) |
593 | { |
594 | struct irq_chip *chip = irq_desc_get_chip(desc); |
595 | struct s3c64xx_eint0_data *data = irq_desc_get_handler_data(desc); |
596 | struct samsung_pinctrl_drv_data *drvdata = data->drvdata; |
597 | unsigned int pend, mask; |
598 | |
599 | chained_irq_enter(chip, desc); |
600 | |
601 | pend = readl(addr: drvdata->virt_base + EINT0PEND_REG); |
602 | mask = readl(addr: drvdata->virt_base + EINT0MASK_REG); |
603 | |
604 | pend = pend & range & ~mask; |
605 | pend &= range; |
606 | |
607 | while (pend) { |
608 | unsigned int irq; |
609 | int ret; |
610 | |
611 | irq = fls(x: pend) - 1; |
612 | pend &= ~(1 << irq); |
613 | ret = generic_handle_domain_irq(domain: data->domains[irq], hwirq: data->pins[irq]); |
614 | /* |
615 | * Something must be really wrong if an unmapped EINT |
616 | * was unmasked... |
617 | */ |
618 | BUG_ON(ret); |
619 | } |
620 | |
621 | chained_irq_exit(chip, desc); |
622 | } |
623 | |
624 | static void s3c64xx_demux_eint0_3(struct irq_desc *desc) |
625 | { |
626 | s3c64xx_irq_demux_eint(desc, range: 0xf); |
627 | } |
628 | |
629 | static void s3c64xx_demux_eint4_11(struct irq_desc *desc) |
630 | { |
631 | s3c64xx_irq_demux_eint(desc, range: 0xff0); |
632 | } |
633 | |
634 | static void s3c64xx_demux_eint12_19(struct irq_desc *desc) |
635 | { |
636 | s3c64xx_irq_demux_eint(desc, range: 0xff000); |
637 | } |
638 | |
639 | static void s3c64xx_demux_eint20_27(struct irq_desc *desc) |
640 | { |
641 | s3c64xx_irq_demux_eint(desc, range: 0xff00000); |
642 | } |
643 | |
644 | static irq_flow_handler_t s3c64xx_eint0_handlers[NUM_EINT0_IRQ] = { |
645 | s3c64xx_demux_eint0_3, |
646 | s3c64xx_demux_eint4_11, |
647 | s3c64xx_demux_eint12_19, |
648 | s3c64xx_demux_eint20_27, |
649 | }; |
650 | |
651 | static int s3c64xx_eint0_irq_map(struct irq_domain *h, unsigned int virq, |
652 | irq_hw_number_t hw) |
653 | { |
654 | struct s3c64xx_eint0_domain_data *ddata = h->host_data; |
655 | struct samsung_pin_bank *bank = ddata->bank; |
656 | |
657 | if (!(bank->eint_mask & (1 << hw))) |
658 | return -EINVAL; |
659 | |
660 | irq_set_chip_and_handler(irq: virq, |
661 | chip: &s3c64xx_eint0_irq_chip, handle: handle_level_irq); |
662 | irq_set_chip_data(irq: virq, data: ddata); |
663 | |
664 | return 0; |
665 | } |
666 | |
667 | /* |
668 | * irq domain callbacks for external wakeup interrupt controller. |
669 | */ |
670 | static const struct irq_domain_ops s3c64xx_eint0_irqd_ops = { |
671 | .map = s3c64xx_eint0_irq_map, |
672 | .xlate = irq_domain_xlate_twocell, |
673 | }; |
674 | |
675 | /* list of external wakeup controllers supported */ |
676 | static const struct of_device_id s3c64xx_eint0_irq_ids[] = { |
677 | { .compatible = "samsung,s3c64xx-wakeup-eint" , }, |
678 | { } |
679 | }; |
680 | |
681 | /** |
682 | * s3c64xx_eint_eint0_init() - setup handling of external wakeup interrupts. |
683 | * @d: driver data of samsung pinctrl driver. |
684 | */ |
685 | static int s3c64xx_eint_eint0_init(struct samsung_pinctrl_drv_data *d) |
686 | { |
687 | struct device *dev = d->dev; |
688 | struct device_node *eint0_np = NULL; |
689 | struct device_node *np; |
690 | struct samsung_pin_bank *bank; |
691 | struct s3c64xx_eint0_data *data; |
692 | unsigned int i; |
693 | |
694 | for_each_child_of_node(dev->of_node, np) { |
695 | if (of_match_node(matches: s3c64xx_eint0_irq_ids, node: np)) { |
696 | eint0_np = np; |
697 | break; |
698 | } |
699 | } |
700 | if (!eint0_np) |
701 | return -ENODEV; |
702 | |
703 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
704 | if (!data) { |
705 | of_node_put(node: eint0_np); |
706 | return -ENOMEM; |
707 | } |
708 | data->drvdata = d; |
709 | |
710 | for (i = 0; i < NUM_EINT0_IRQ; ++i) { |
711 | unsigned int irq; |
712 | |
713 | irq = irq_of_parse_and_map(node: eint0_np, index: i); |
714 | if (!irq) { |
715 | dev_err(dev, "failed to get wakeup EINT IRQ %d\n" , i); |
716 | of_node_put(node: eint0_np); |
717 | return -ENXIO; |
718 | } |
719 | |
720 | irq_set_chained_handler_and_data(irq, |
721 | handle: s3c64xx_eint0_handlers[i], |
722 | data); |
723 | } |
724 | of_node_put(node: eint0_np); |
725 | |
726 | bank = d->pin_banks; |
727 | for (i = 0; i < d->nr_banks; ++i, ++bank) { |
728 | struct s3c64xx_eint0_domain_data *ddata; |
729 | unsigned int nr_eints; |
730 | unsigned int mask; |
731 | unsigned int irq; |
732 | unsigned int pin; |
733 | |
734 | if (bank->eint_type != EINT_TYPE_WKUP) |
735 | continue; |
736 | |
737 | mask = bank->eint_mask; |
738 | nr_eints = fls(x: mask); |
739 | |
740 | ddata = devm_kzalloc(dev, |
741 | size: sizeof(*ddata) + nr_eints, GFP_KERNEL); |
742 | if (!ddata) |
743 | return -ENOMEM; |
744 | ddata->bank = bank; |
745 | |
746 | bank->irq_domain = irq_domain_create_linear(fwnode: bank->fwnode, |
747 | size: nr_eints, ops: &s3c64xx_eint0_irqd_ops, host_data: ddata); |
748 | if (!bank->irq_domain) { |
749 | dev_err(dev, "wkup irq domain add failed\n" ); |
750 | return -ENXIO; |
751 | } |
752 | |
753 | irq = bank->eint_offset; |
754 | mask = bank->eint_mask; |
755 | for (pin = 0; mask; ++pin, mask >>= 1) { |
756 | if (!(mask & 1)) |
757 | continue; |
758 | data->domains[irq] = bank->irq_domain; |
759 | data->pins[irq] = pin; |
760 | ddata->eints[pin] = irq; |
761 | ++irq; |
762 | } |
763 | } |
764 | |
765 | return 0; |
766 | } |
767 | |
768 | /* pin banks of s3c64xx pin-controller 0 */ |
769 | static const struct samsung_pin_bank_data s3c64xx_pin_banks0[] __initconst = { |
770 | PIN_BANK_4BIT_EINTG(8, 0x000, "gpa" , 0), |
771 | PIN_BANK_4BIT_EINTG(7, 0x020, "gpb" , 8), |
772 | PIN_BANK_4BIT_EINTG(8, 0x040, "gpc" , 16), |
773 | PIN_BANK_4BIT_EINTG(5, 0x060, "gpd" , 32), |
774 | PIN_BANK_4BIT(5, 0x080, "gpe" ), |
775 | PIN_BANK_2BIT_EINTG(16, 0x0a0, "gpf" , 48, 0x3fff), |
776 | PIN_BANK_4BIT_EINTG(7, 0x0c0, "gpg" , 64), |
777 | PIN_BANK_4BIT2_EINTG(10, 0x0e0, "gph" , 80), |
778 | PIN_BANK_2BIT(16, 0x100, "gpi" ), |
779 | PIN_BANK_2BIT(12, 0x120, "gpj" ), |
780 | PIN_BANK_4BIT2_ALIVE(16, 0x800, "gpk" ), |
781 | PIN_BANK_4BIT2_EINTW(15, 0x810, "gpl" , 16, 0x7f00), |
782 | PIN_BANK_4BIT_EINTW(6, 0x820, "gpm" , 23, 0x1f), |
783 | PIN_BANK_2BIT_EINTW(16, 0x830, "gpn" , 0), |
784 | PIN_BANK_2BIT_EINTG(16, 0x140, "gpo" , 96, 0xffff), |
785 | PIN_BANK_2BIT_EINTG(15, 0x160, "gpp" , 112, 0x7fff), |
786 | PIN_BANK_2BIT_EINTG(9, 0x180, "gpq" , 128, 0x1ff), |
787 | }; |
788 | |
789 | /* |
790 | * Samsung pinctrl driver data for S3C64xx SoC. S3C64xx SoC includes |
791 | * one gpio/pin-mux/pinconfig controller. |
792 | */ |
793 | static const struct samsung_pin_ctrl s3c64xx_pin_ctrl[] __initconst = { |
794 | { |
795 | /* pin-controller instance 1 data */ |
796 | .pin_banks = s3c64xx_pin_banks0, |
797 | .nr_banks = ARRAY_SIZE(s3c64xx_pin_banks0), |
798 | .eint_gpio_init = s3c64xx_eint_gpio_init, |
799 | .eint_wkup_init = s3c64xx_eint_eint0_init, |
800 | }, |
801 | }; |
802 | |
803 | const struct samsung_pinctrl_of_match_data s3c64xx_of_data __initconst = { |
804 | .ctrl = s3c64xx_pin_ctrl, |
805 | .num_ctrl = ARRAY_SIZE(s3c64xx_pin_ctrl), |
806 | }; |
807 | |