diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pinctrl/Kconfig | 1 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-ocelot.c | 102 |
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 depends on OF depends on MSCC_OCELOT || COMPILE_TEST select GPIOLIB + select GPIOLIB_IRQCHIP select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS 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 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> @@ -427,11 +428,98 @@ static const struct gpio_chip ocelot_gpiolib_chip = { .owner = THIS_MODULE, }; +static void ocelot_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct ocelot_pinctrl *info = gpiochip_get_data(chip); + unsigned int gpio = irqd_to_hwirq(data); + + regmap_update_bits(info->map, OCELOT_GPIO_INTR_ENA, BIT(gpio), 0); +} + +static void ocelot_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct ocelot_pinctrl *info = gpiochip_get_data(chip); + unsigned int gpio = irqd_to_hwirq(data); + + regmap_update_bits(info->map, OCELOT_GPIO_INTR_ENA, BIT(gpio), + BIT(gpio)); +} + +static void ocelot_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct ocelot_pinctrl *info = gpiochip_get_data(chip); + unsigned int gpio = irqd_to_hwirq(data); + + regmap_write_bits(info->map, OCELOT_GPIO_INTR, BIT(gpio), BIT(gpio)); +} + +static int ocelot_irq_set_type(struct irq_data *data, unsigned int type); + +static struct irq_chip ocelot_eoi_irqchip = { + .name = "gpio", + .irq_mask = ocelot_irq_mask, + .irq_eoi = ocelot_irq_ack, + .irq_unmask = ocelot_irq_unmask, + .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED, + .irq_set_type = ocelot_irq_set_type, +}; + +static struct irq_chip ocelot_irqchip = { + .name = "gpio", + .irq_mask = ocelot_irq_mask, + .irq_ack = ocelot_irq_ack, + .irq_unmask = ocelot_irq_unmask, + .irq_set_type = ocelot_irq_set_type, +}; + +static int ocelot_irq_set_type(struct irq_data *data, unsigned int type) +{ + type &= IRQ_TYPE_SENSE_MASK; + + if (!(type & (IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_HIGH))) + return -EINVAL; + + if (type & IRQ_TYPE_LEVEL_HIGH) + irq_set_chip_handler_name_locked(data, &ocelot_eoi_irqchip, + handle_fasteoi_irq, NULL); + if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_chip_handler_name_locked(data, &ocelot_irqchip, + handle_edge_irq, NULL); + + return 0; +} + +static void ocelot_irq_handler(struct irq_desc *desc) +{ + struct irq_chip *parent_chip = irq_desc_get_chip(desc); + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct ocelot_pinctrl *info = gpiochip_get_data(chip); + unsigned int reg = 0, irq; + unsigned long irqs; + + regmap_read(info->map, OCELOT_GPIO_INTR_IDENT, ®); + if (!reg) + return; + + chained_irq_enter(parent_chip, desc); + + irqs = reg; + + for_each_set_bit(irq, &irqs, OCELOT_PINS) { + generic_handle_irq(irq_linear_revmap(chip->irq.domain, irq)); + } + + chained_irq_exit(parent_chip, desc); +} + static int ocelot_gpiochip_register(struct platform_device *pdev, struct ocelot_pinctrl *info) { struct gpio_chip *gc; - int ret; + int ret, irq; info->gpio_chip = ocelot_gpiolib_chip; @@ -446,7 +534,17 @@ static int ocelot_gpiochip_register(struct platform_device *pdev, if (ret) return ret; - /* TODO: this can be used as an irqchip but no board is using that */ + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (irq <= 0) + return irq; + + ret = gpiochip_irqchip_add(gc, &ocelot_irqchip, 0, handle_edge_irq, + IRQ_TYPE_NONE); + if (ret) + return ret; + + gpiochip_set_chained_irqchip(gc, &ocelot_irqchip, irq, + ocelot_irq_handler); return 0; } |