summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl
diff options
context:
space:
mode:
authorQuentin Schulz <quentin.schulz@bootlin.com>2018-07-25 08:26:21 -0400
committerLinus Walleij <linus.walleij@linaro.org>2018-08-06 07:00:17 -0400
commitbe36abb71d878f3320412a84e6bbec8dd796010c (patch)
tree21b80e5e6c09b818ab62d47473a8ac1f116078fe /drivers/pinctrl
parentcb85d2b04b408ba8328eb5fa1e184cc538ba8c6a (diff)
pinctrl: ocelot: add support for interrupt controller
This GPIO controller can serve as an interrupt controller as well on the GPIOs it handles. An interrupt is generated whenever a GPIO line changes and the interrupt for this GPIO line is enabled. This means that both the changes from low to high and high to low generate an interrupt. For some use cases, it makes sense to ignore the high to low change and not generate an interrupt. Such a use case is a line that is hold in a level high/low manner until the event holding the line gets acked. This can be achieved by making sure the interrupt on the GPIO controller side gets acked and masked only after the line gets hold in its default state, this is what's done with the fasteoi functions. Only IRQ_TYPE_EDGE_BOTH and IRQ_TYPE_LEVEL_HIGH are supported for now. Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r--drivers/pinctrl/Kconfig1
-rw-r--r--drivers/pinctrl/pinctrl-ocelot.c102
2 files changed, 101 insertions, 2 deletions
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 522782d1ea49..8d4b7e999f02 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -332,6 +332,7 @@ config PINCTRL_OCELOT
332 depends on OF 332 depends on OF
333 depends on MSCC_OCELOT || COMPILE_TEST 333 depends on MSCC_OCELOT || COMPILE_TEST
334 select GPIOLIB 334 select GPIOLIB
335 select GPIOLIB_IRQCHIP
335 select GENERIC_PINCONF 336 select GENERIC_PINCONF
336 select GENERIC_PINCTRL_GROUPS 337 select GENERIC_PINCTRL_GROUPS
337 select GENERIC_PINMUX_FUNCTIONS 338 select GENERIC_PINMUX_FUNCTIONS
diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
index e93669f4d1c8..f9fc2b3a8731 100644
--- a/drivers/pinctrl/pinctrl-ocelot.c
+++ b/drivers/pinctrl/pinctrl-ocelot.c
@@ -11,6 +11,7 @@
11#include <linux/interrupt.h> 11#include <linux/interrupt.h>
12#include <linux/io.h> 12#include <linux/io.h>
13#include <linux/of_device.h> 13#include <linux/of_device.h>
14#include <linux/of_irq.h>
14#include <linux/of_platform.h> 15#include <linux/of_platform.h>
15#include <linux/pinctrl/pinctrl.h> 16#include <linux/pinctrl/pinctrl.h>
16#include <linux/pinctrl/pinmux.h> 17#include <linux/pinctrl/pinmux.h>
@@ -427,11 +428,98 @@ static const struct gpio_chip ocelot_gpiolib_chip = {
427 .owner = THIS_MODULE, 428 .owner = THIS_MODULE,
428}; 429};
429 430
431static void ocelot_irq_mask(struct irq_data *data)
432{
433 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
434 struct ocelot_pinctrl *info = gpiochip_get_data(chip);
435 unsigned int gpio = irqd_to_hwirq(data);
436
437 regmap_update_bits(info->map, OCELOT_GPIO_INTR_ENA, BIT(gpio), 0);
438}
439
440static void ocelot_irq_unmask(struct irq_data *data)
441{
442 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
443 struct ocelot_pinctrl *info = gpiochip_get_data(chip);
444 unsigned int gpio = irqd_to_hwirq(data);
445
446 regmap_update_bits(info->map, OCELOT_GPIO_INTR_ENA, BIT(gpio),
447 BIT(gpio));
448}
449
450static void ocelot_irq_ack(struct irq_data *data)
451{
452 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
453 struct ocelot_pinctrl *info = gpiochip_get_data(chip);
454 unsigned int gpio = irqd_to_hwirq(data);
455
456 regmap_write_bits(info->map, OCELOT_GPIO_INTR, BIT(gpio), BIT(gpio));
457}
458
459static int ocelot_irq_set_type(struct irq_data *data, unsigned int type);
460
461static struct irq_chip ocelot_eoi_irqchip = {
462 .name = "gpio",
463 .irq_mask = ocelot_irq_mask,
464 .irq_eoi = ocelot_irq_ack,
465 .irq_unmask = ocelot_irq_unmask,
466 .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED,
467 .irq_set_type = ocelot_irq_set_type,
468};
469
470static struct irq_chip ocelot_irqchip = {
471 .name = "gpio",
472 .irq_mask = ocelot_irq_mask,
473 .irq_ack = ocelot_irq_ack,
474 .irq_unmask = ocelot_irq_unmask,
475 .irq_set_type = ocelot_irq_set_type,
476};
477
478static int ocelot_irq_set_type(struct irq_data *data, unsigned int type)
479{
480 type &= IRQ_TYPE_SENSE_MASK;
481
482 if (!(type & (IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_HIGH)))
483 return -EINVAL;
484
485 if (type & IRQ_TYPE_LEVEL_HIGH)
486 irq_set_chip_handler_name_locked(data, &ocelot_eoi_irqchip,
487 handle_fasteoi_irq, NULL);
488 if (type & IRQ_TYPE_EDGE_BOTH)
489 irq_set_chip_handler_name_locked(data, &ocelot_irqchip,
490 handle_edge_irq, NULL);
491
492 return 0;
493}
494
495static void ocelot_irq_handler(struct irq_desc *desc)
496{
497 struct irq_chip *parent_chip = irq_desc_get_chip(desc);
498 struct gpio_chip *chip = irq_desc_get_handler_data(desc);
499 struct ocelot_pinctrl *info = gpiochip_get_data(chip);
500 unsigned int reg = 0, irq;
501 unsigned long irqs;
502
503 regmap_read(info->map, OCELOT_GPIO_INTR_IDENT, &reg);
504 if (!reg)
505 return;
506
507 chained_irq_enter(parent_chip, desc);
508
509 irqs = reg;
510
511 for_each_set_bit(irq, &irqs, OCELOT_PINS) {
512 generic_handle_irq(irq_linear_revmap(chip->irq.domain, irq));
513 }
514
515 chained_irq_exit(parent_chip, desc);
516}
517
430static int ocelot_gpiochip_register(struct platform_device *pdev, 518static int ocelot_gpiochip_register(struct platform_device *pdev,
431 struct ocelot_pinctrl *info) 519 struct ocelot_pinctrl *info)
432{ 520{
433 struct gpio_chip *gc; 521 struct gpio_chip *gc;
434 int ret; 522 int ret, irq;
435 523
436 info->gpio_chip = ocelot_gpiolib_chip; 524 info->gpio_chip = ocelot_gpiolib_chip;
437 525
@@ -446,7 +534,17 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
446 if (ret) 534 if (ret)
447 return ret; 535 return ret;
448 536
449 /* TODO: this can be used as an irqchip but no board is using that */ 537 irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
538 if (irq <= 0)
539 return irq;
540
541 ret = gpiochip_irqchip_add(gc, &ocelot_irqchip, 0, handle_edge_irq,
542 IRQ_TYPE_NONE);
543 if (ret)
544 return ret;
545
546 gpiochip_set_chained_irqchip(gc, &ocelot_irqchip, irq,
547 ocelot_irq_handler);
450 548
451 return 0; 549 return 0;
452} 550}