aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl
diff options
context:
space:
mode:
authorTomasz Figa <t.figa@samsung.com>2012-10-11 04:11:18 -0400
committerLinus Walleij <linus.walleij@linaro.org>2012-10-15 03:10:12 -0400
commita04b07c0fc4d63e3fb9fea84d48a177ac5bd9164 (patch)
tree38504cfbceac9b3ed6359caded76a626b633fa12 /drivers/pinctrl
parentd3a7b9e3a168df881a0ae3bd0d582f44a5d5aca3 (diff)
pinctrl: samsung: Use per-bank IRQ domain for wake-up interrupts
This patch reworks wake-up interrupt handling in pinctrl-exynos driver, so each pin bank, which provides wake-up interrupts, has its own IRQ domain. Information about whether given pin bank provides wake-up interrupts, how many and whether they are separate or muxed are parsed from device tree. It gives following advantages: - interrupts can be specified in device tree in a more readable way, e.g. : device { /* ... */ interrupt-parent = <&gpx2>; interrupts = <4 0>; /* ... */ }; - the amount and layout of interrupts is not hardcoded in the code anymore, but defined in SoC-specific structure - bank and pin of each wake-up interrupt can be easily identified, to allow operations, such as setting the pin to EINT function, from irq_set_type() callback Signed-off-by: Tomasz Figa <t.figa@samsung.com> Reviewed-by: Kyungmin Park <kyungmin.park@samsung.com> Acked-by: Thomas Abraham <thomas.abraham@linaro.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r--drivers/pinctrl/pinctrl-exynos.c163
-rw-r--r--drivers/pinctrl/pinctrl-exynos.h29
-rw-r--r--drivers/pinctrl/pinctrl-samsung.h6
3 files changed, 129 insertions, 69 deletions
diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c
index be757b1d4fcd..4bf2fc40aca8 100644
--- a/drivers/pinctrl/pinctrl-exynos.c
+++ b/drivers/pinctrl/pinctrl-exynos.c
@@ -218,46 +218,43 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
218 218
219static void exynos_wkup_irq_unmask(struct irq_data *irqd) 219static void exynos_wkup_irq_unmask(struct irq_data *irqd)
220{ 220{
221 struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd); 221 struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd);
222 unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK; 222 struct samsung_pinctrl_drv_data *d = b->drvdata;
223 unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1); 223 unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset;
224 unsigned long reg_mask = d->ctrl->weint_mask + (bank << 2);
225 unsigned long mask; 224 unsigned long mask;
226 225
227 mask = readl(d->virt_base + reg_mask); 226 mask = readl(d->virt_base + reg_mask);
228 mask &= ~(1 << pin); 227 mask &= ~(1 << irqd->hwirq);
229 writel(mask, d->virt_base + reg_mask); 228 writel(mask, d->virt_base + reg_mask);
230} 229}
231 230
232static void exynos_wkup_irq_mask(struct irq_data *irqd) 231static void exynos_wkup_irq_mask(struct irq_data *irqd)
233{ 232{
234 struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd); 233 struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd);
235 unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK; 234 struct samsung_pinctrl_drv_data *d = b->drvdata;
236 unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1); 235 unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset;
237 unsigned long reg_mask = d->ctrl->weint_mask + (bank << 2);
238 unsigned long mask; 236 unsigned long mask;
239 237
240 mask = readl(d->virt_base + reg_mask); 238 mask = readl(d->virt_base + reg_mask);
241 mask |= 1 << pin; 239 mask |= 1 << irqd->hwirq;
242 writel(mask, d->virt_base + reg_mask); 240 writel(mask, d->virt_base + reg_mask);
243} 241}
244 242
245static void exynos_wkup_irq_ack(struct irq_data *irqd) 243static void exynos_wkup_irq_ack(struct irq_data *irqd)
246{ 244{
247 struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd); 245 struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd);
248 unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK; 246 struct samsung_pinctrl_drv_data *d = b->drvdata;
249 unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1); 247 unsigned long pend = d->ctrl->weint_pend + b->eint_offset;
250 unsigned long pend = d->ctrl->weint_pend + (bank << 2);
251 248
252 writel(1 << pin, d->virt_base + pend); 249 writel(1 << irqd->hwirq, d->virt_base + pend);
253} 250}
254 251
255static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type) 252static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type)
256{ 253{
257 struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd); 254 struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
258 unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK; 255 struct samsung_pinctrl_drv_data *d = bank->drvdata;
259 unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1); 256 unsigned int pin = irqd->hwirq;
260 unsigned long reg_con = d->ctrl->weint_con + (bank << 2); 257 unsigned long reg_con = d->ctrl->weint_con + bank->eint_offset;
261 unsigned long shift = EXYNOS_EINT_CON_LEN * pin; 258 unsigned long shift = EXYNOS_EINT_CON_LEN * pin;
262 unsigned long con, trig_type; 259 unsigned long con, trig_type;
263 260
@@ -309,6 +306,7 @@ static struct irq_chip exynos_wkup_irq_chip = {
309static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc) 306static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc)
310{ 307{
311 struct exynos_weint_data *eintd = irq_get_handler_data(irq); 308 struct exynos_weint_data *eintd = irq_get_handler_data(irq);
309 struct samsung_pin_bank *bank = eintd->bank;
312 struct irq_chip *chip = irq_get_chip(irq); 310 struct irq_chip *chip = irq_get_chip(irq);
313 int eint_irq; 311 int eint_irq;
314 312
@@ -318,20 +316,20 @@ static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc)
318 if (chip->irq_ack) 316 if (chip->irq_ack)
319 chip->irq_ack(&desc->irq_data); 317 chip->irq_ack(&desc->irq_data);
320 318
321 eint_irq = irq_linear_revmap(eintd->domain, eintd->irq); 319 eint_irq = irq_linear_revmap(bank->irq_domain, eintd->irq);
322 generic_handle_irq(eint_irq); 320 generic_handle_irq(eint_irq);
323 chip->irq_unmask(&desc->irq_data); 321 chip->irq_unmask(&desc->irq_data);
324 chained_irq_exit(chip, desc); 322 chained_irq_exit(chip, desc);
325} 323}
326 324
327static inline void exynos_irq_demux_eint(int irq_base, unsigned long pend, 325static inline void exynos_irq_demux_eint(unsigned long pend,
328 struct irq_domain *domain) 326 struct irq_domain *domain)
329{ 327{
330 unsigned int irq; 328 unsigned int irq;
331 329
332 while (pend) { 330 while (pend) {
333 irq = fls(pend) - 1; 331 irq = fls(pend) - 1;
334 generic_handle_irq(irq_find_mapping(domain, irq_base + irq)); 332 generic_handle_irq(irq_find_mapping(domain, irq));
335 pend &= ~(1 << irq); 333 pend &= ~(1 << irq);
336 } 334 }
337} 335}
@@ -340,18 +338,22 @@ static inline void exynos_irq_demux_eint(int irq_base, unsigned long pend,
340static void exynos_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) 338static void exynos_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
341{ 339{
342 struct irq_chip *chip = irq_get_chip(irq); 340 struct irq_chip *chip = irq_get_chip(irq);
343 struct exynos_weint_data *eintd = irq_get_handler_data(irq); 341 struct exynos_muxed_weint_data *eintd = irq_get_handler_data(irq);
344 struct samsung_pinctrl_drv_data *d = eintd->domain->host_data; 342 struct samsung_pinctrl_drv_data *d = eintd->banks[0]->drvdata;
343 struct samsung_pin_ctrl *ctrl = d->ctrl;
345 unsigned long pend; 344 unsigned long pend;
346 unsigned long mask; 345 unsigned long mask;
346 int i;
347 347
348 chained_irq_enter(chip, desc); 348 chained_irq_enter(chip, desc);
349 pend = readl(d->virt_base + d->ctrl->weint_pend + 0x8); 349
350 mask = readl(d->virt_base + d->ctrl->weint_mask + 0x8); 350 for (i = 0; i < eintd->nr_banks; ++i) {
351 exynos_irq_demux_eint(16, pend & ~mask, eintd->domain); 351 struct samsung_pin_bank *b = eintd->banks[i];
352 pend = readl(d->virt_base + d->ctrl->weint_pend + 0xC); 352 pend = readl(d->virt_base + ctrl->weint_pend + b->eint_offset);
353 mask = readl(d->virt_base + d->ctrl->weint_mask + 0xC); 353 mask = readl(d->virt_base + ctrl->weint_mask + b->eint_offset);
354 exynos_irq_demux_eint(24, pend & ~mask, eintd->domain); 354 exynos_irq_demux_eint(pend & ~mask, b->irq_domain);
355 }
356
355 chained_irq_exit(chip, desc); 357 chained_irq_exit(chip, desc);
356} 358}
357 359
@@ -381,7 +383,11 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
381 struct device *dev = d->dev; 383 struct device *dev = d->dev;
382 struct device_node *wkup_np = NULL; 384 struct device_node *wkup_np = NULL;
383 struct device_node *np; 385 struct device_node *np;
386 struct samsung_pin_bank *bank;
384 struct exynos_weint_data *weint_data; 387 struct exynos_weint_data *weint_data;
388 struct exynos_muxed_weint_data *muxed_data;
389 unsigned int muxed_banks = 0;
390 unsigned int i;
385 int idx, irq; 391 int idx, irq;
386 392
387 for_each_child_of_node(dev->of_node, np) { 393 for_each_child_of_node(dev->of_node, np) {
@@ -393,40 +399,74 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
393 if (!wkup_np) 399 if (!wkup_np)
394 return -ENODEV; 400 return -ENODEV;
395 401
396 d->wkup_irqd = irq_domain_add_linear(wkup_np, d->ctrl->nr_wint, 402 bank = d->ctrl->pin_banks;
397 &exynos_wkup_irqd_ops, d); 403 for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) {
398 if (!d->wkup_irqd) { 404 if (bank->eint_type != EINT_TYPE_WKUP)
399 dev_err(dev, "wakeup irq domain allocation failed\n"); 405 continue;
400 return -ENXIO;
401 }
402 406
403 weint_data = devm_kzalloc(dev, sizeof(*weint_data) * 17, GFP_KERNEL); 407 bank->irq_domain = irq_domain_add_linear(bank->of_node,
404 if (!weint_data) { 408 bank->nr_pins, &exynos_wkup_irqd_ops, bank);
405 dev_err(dev, "could not allocate memory for weint_data\n"); 409 if (!bank->irq_domain) {
406 return -ENOMEM; 410 dev_err(dev, "wkup irq domain add failed\n");
407 } 411 return -ENXIO;
412 }
408 413
409 irq = irq_of_parse_and_map(wkup_np, 16); 414 if (!of_find_property(bank->of_node, "interrupts", NULL)) {
410 if (irq) { 415 bank->eint_type = EINT_TYPE_WKUP_MUX;
411 weint_data[16].domain = d->wkup_irqd; 416 ++muxed_banks;
412 irq_set_chained_handler(irq, exynos_irq_demux_eint16_31); 417 continue;
413 irq_set_handler_data(irq, &weint_data[16]); 418 }
414 } else {
415 dev_err(dev, "irq number for EINT16-32 not found\n");
416 }
417 419
418 for (idx = 0; idx < 16; idx++) { 420 weint_data = devm_kzalloc(dev, bank->nr_pins
419 weint_data[idx].domain = d->wkup_irqd; 421 * sizeof(*weint_data), GFP_KERNEL);
420 weint_data[idx].irq = idx; 422 if (!weint_data) {
423 dev_err(dev, "could not allocate memory for weint_data\n");
424 return -ENOMEM;
425 }
421 426
422 irq = irq_of_parse_and_map(wkup_np, idx); 427 for (idx = 0; idx < bank->nr_pins; ++idx) {
423 if (irq) { 428 irq = irq_of_parse_and_map(bank->of_node, idx);
429 if (!irq) {
430 dev_err(dev, "irq number for eint-%s-%d not found\n",
431 bank->name, idx);
432 continue;
433 }
434 weint_data[idx].irq = idx;
435 weint_data[idx].bank = bank;
424 irq_set_handler_data(irq, &weint_data[idx]); 436 irq_set_handler_data(irq, &weint_data[idx]);
425 irq_set_chained_handler(irq, exynos_irq_eint0_15); 437 irq_set_chained_handler(irq, exynos_irq_eint0_15);
426 } else {
427 dev_err(dev, "irq number for eint-%x not found\n", idx);
428 } 438 }
429 } 439 }
440
441 if (!muxed_banks)
442 return 0;
443
444 irq = irq_of_parse_and_map(wkup_np, 0);
445 if (!irq) {
446 dev_err(dev, "irq number for muxed EINTs not found\n");
447 return 0;
448 }
449
450 muxed_data = devm_kzalloc(dev, sizeof(*muxed_data)
451 + muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL);
452 if (!muxed_data) {
453 dev_err(dev, "could not allocate memory for muxed_data\n");
454 return -ENOMEM;
455 }
456
457 irq_set_chained_handler(irq, exynos_irq_demux_eint16_31);
458 irq_set_handler_data(irq, muxed_data);
459
460 bank = d->ctrl->pin_banks;
461 idx = 0;
462 for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) {
463 if (bank->eint_type != EINT_TYPE_WKUP_MUX)
464 continue;
465
466 muxed_data->banks[idx++] = bank;
467 }
468 muxed_data->nr_banks = muxed_banks;
469
430 return 0; 470 return 0;
431} 471}
432 472
@@ -468,10 +508,10 @@ static struct samsung_pin_bank exynos4210_pin_banks1[] = {
468 EXYNOS_PIN_BANK_EINTN(8, 0x1A0, "gpy4"), 508 EXYNOS_PIN_BANK_EINTN(8, 0x1A0, "gpy4"),
469 EXYNOS_PIN_BANK_EINTN(8, 0x1C0, "gpy5"), 509 EXYNOS_PIN_BANK_EINTN(8, 0x1C0, "gpy5"),
470 EXYNOS_PIN_BANK_EINTN(8, 0x1E0, "gpy6"), 510 EXYNOS_PIN_BANK_EINTN(8, 0x1E0, "gpy6"),
471 EXYNOS_PIN_BANK_EINTN(8, 0xC00, "gpx0"), 511 EXYNOS_PIN_BANK_EINTW(8, 0xC00, "gpx0", 0x00),
472 EXYNOS_PIN_BANK_EINTN(8, 0xC20, "gpx1"), 512 EXYNOS_PIN_BANK_EINTW(8, 0xC20, "gpx1", 0x04),
473 EXYNOS_PIN_BANK_EINTN(8, 0xC40, "gpx2"), 513 EXYNOS_PIN_BANK_EINTW(8, 0xC40, "gpx2", 0x08),
474 EXYNOS_PIN_BANK_EINTN(8, 0xC60, "gpx3"), 514 EXYNOS_PIN_BANK_EINTW(8, 0xC60, "gpx3", 0x0c),
475}; 515};
476 516
477/* pin banks of exynos4210 pin-controller 2 */ 517/* pin banks of exynos4210 pin-controller 2 */
@@ -498,7 +538,6 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = {
498 /* pin-controller instance 1 data */ 538 /* pin-controller instance 1 data */
499 .pin_banks = exynos4210_pin_banks1, 539 .pin_banks = exynos4210_pin_banks1,
500 .nr_banks = ARRAY_SIZE(exynos4210_pin_banks1), 540 .nr_banks = ARRAY_SIZE(exynos4210_pin_banks1),
501 .nr_wint = 32,
502 .geint_con = EXYNOS_GPIO_ECON_OFFSET, 541 .geint_con = EXYNOS_GPIO_ECON_OFFSET,
503 .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, 542 .geint_mask = EXYNOS_GPIO_EMASK_OFFSET,
504 .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, 543 .geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h
index f05efa074658..0a708890d8b4 100644
--- a/drivers/pinctrl/pinctrl-exynos.h
+++ b/drivers/pinctrl/pinctrl-exynos.h
@@ -73,13 +73,36 @@
73 .name = id \ 73 .name = id \
74 } 74 }
75 75
76#define EXYNOS_PIN_BANK_EINTW(pins, reg, id, offs) \
77 { \
78 .pctl_offset = reg, \
79 .nr_pins = pins, \
80 .func_width = 4, \
81 .pud_width = 2, \
82 .drv_width = 2, \
83 .eint_type = EINT_TYPE_WKUP, \
84 .eint_offset = offs, \
85 .name = id \
86 }
87
76/** 88/**
77 * struct exynos_weint_data: irq specific data for all the wakeup interrupts 89 * struct exynos_weint_data: irq specific data for all the wakeup interrupts
78 * generated by the external wakeup interrupt controller. 90 * generated by the external wakeup interrupt controller.
79 * @domain: irq domain representing the external wakeup interrupts
80 * @irq: interrupt number within the domain. 91 * @irq: interrupt number within the domain.
92 * @bank: bank responsible for this interrupt
81 */ 93 */
82struct exynos_weint_data { 94struct exynos_weint_data {
83 struct irq_domain *domain; 95 unsigned int irq;
84 u32 irq; 96 struct samsung_pin_bank *bank;
97};
98
99/**
100 * struct exynos_muxed_weint_data: irq specific data for muxed wakeup interrupts
101 * generated by the external wakeup interrupt controller.
102 * @nr_banks: count of banks being part of the mux
103 * @banks: array of banks being part of the mux
104 */
105struct exynos_muxed_weint_data {
106 unsigned int nr_banks;
107 struct samsung_pin_bank *banks[];
85}; 108};
diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h
index dac40ffd5e67..0670d9ea43fa 100644
--- a/drivers/pinctrl/pinctrl-samsung.h
+++ b/drivers/pinctrl/pinctrl-samsung.h
@@ -66,6 +66,7 @@ enum pincfg_type {
66 * @EINT_TYPE_NONE: bank does not support external interrupts 66 * @EINT_TYPE_NONE: bank does not support external interrupts
67 * @EINT_TYPE_GPIO: bank supportes external gpio interrupts 67 * @EINT_TYPE_GPIO: bank supportes external gpio interrupts
68 * @EINT_TYPE_WKUP: bank supportes external wakeup interrupts 68 * @EINT_TYPE_WKUP: bank supportes external wakeup interrupts
69 * @EINT_TYPE_WKUP_MUX: bank supports multiplexed external wakeup interrupts
69 * 70 *
70 * Samsung GPIO controller groups all the available pins into banks. The pins 71 * Samsung GPIO controller groups all the available pins into banks. The pins
71 * in a pin bank can support external gpio interrupts or external wakeup 72 * in a pin bank can support external gpio interrupts or external wakeup
@@ -78,6 +79,7 @@ enum eint_type {
78 EINT_TYPE_NONE, 79 EINT_TYPE_NONE,
79 EINT_TYPE_GPIO, 80 EINT_TYPE_GPIO,
80 EINT_TYPE_WKUP, 81 EINT_TYPE_WKUP,
82 EINT_TYPE_WKUP_MUX,
81}; 83};
82 84
83/* maximum length of a pin in pin descriptor (example: "gpa0-0") */ 85/* maximum length of a pin in pin descriptor (example: "gpa0-0") */
@@ -143,7 +145,6 @@ struct samsung_pin_bank {
143 * @nr_banks: number of pin banks. 145 * @nr_banks: number of pin banks.
144 * @base: starting system wide pin number. 146 * @base: starting system wide pin number.
145 * @nr_pins: number of pins supported by the controller. 147 * @nr_pins: number of pins supported by the controller.
146 * @nr_wint: number of external wakeup interrupts supported.
147 * @geint_con: offset of the ext-gpio controller registers. 148 * @geint_con: offset of the ext-gpio controller registers.
148 * @geint_mask: offset of the ext-gpio interrupt mask registers. 149 * @geint_mask: offset of the ext-gpio interrupt mask registers.
149 * @geint_pend: offset of the ext-gpio interrupt pending registers. 150 * @geint_pend: offset of the ext-gpio interrupt pending registers.
@@ -163,7 +164,6 @@ struct samsung_pin_ctrl {
163 164
164 u32 base; 165 u32 base;
165 u32 nr_pins; 166 u32 nr_pins;
166 u32 nr_wint;
167 167
168 u32 geint_con; 168 u32 geint_con;
169 u32 geint_mask; 169 u32 geint_mask;
@@ -206,8 +206,6 @@ struct samsung_pinctrl_drv_data {
206 unsigned int nr_groups; 206 unsigned int nr_groups;
207 const struct samsung_pmx_func *pmx_functions; 207 const struct samsung_pmx_func *pmx_functions;
208 unsigned int nr_functions; 208 unsigned int nr_functions;
209
210 struct irq_domain *wkup_irqd;
211}; 209};
212 210
213/** 211/**