From mboxrd@z Thu Jan 1 00:00:00 1970 From: Alexandre TORGUE Subject: [PATCH v4 6/9] pinctrl: Add IRQ support to STM32 gpios Date: Tue, 6 Sep 2016 18:45:38 +0200 Message-ID: <1473180341-1999-7-git-send-email-alexandre.torgue@st.com> References: <1473180341-1999-1-git-send-email-alexandre.torgue@st.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: Received: from mx07-00178001.pphosted.com ([62.209.51.94]:25574 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S936277AbcIFQrX (ORCPT ); Tue, 6 Sep 2016 12:47:23 -0400 In-Reply-To: <1473180341-1999-1-git-send-email-alexandre.torgue@st.com> Sender: linux-gpio-owner@vger.kernel.org List-Id: linux-gpio@vger.kernel.org To: Maxime Coquelin , Thomas Gleixner , Jason Cooper , Marc Zyngier , Linus Walleij , Mark Rutland , Rob Herring , linux-gpio@vger.kernel.org, arnd@arndb.de Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, Daniel Thompson , bruherrera@gmail.com, lee.jones@linaro.org This patch adds IRQ support to STM32 gpios. The EXTI controller has 16 lines dedicated to GPIOs. EXTI line n can be connected to only line n of one of the GPIO ports, for example EXTI0 can be connected to either PA0, or PB0, or PC0... This port selection is done by specifying the port number into System Config registers. Signed-off-by: Maxime Coquelin Signed-off-by: Alexandre TORGUE diff --git a/drivers/pinctrl/stm32/Kconfig b/drivers/pinctrl/stm32/Kconfig index 4c40dae..40d5abc 100644 --- a/drivers/pinctrl/stm32/Kconfig +++ b/drivers/pinctrl/stm32/Kconfig @@ -6,6 +6,8 @@ config PINCTRL_STM32 select PINMUX select GENERIC_PINCONF select GPIOLIB + select GPIOLIB_IRQCHIP + select MFD_SYSCON config PINCTRL_STM32F429 bool "STMicroelectronics STM32F429 pin control" if COMPILE_TEST && !MACH_STM32F429 diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 4ae596b..9154bca 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -20,6 +22,7 @@ #include #include #include +#include #include #include @@ -40,6 +43,7 @@ #define STM32_GPIO_AFRH 0x24 #define STM32_GPIO_PINS_PER_BANK 16 +#define STM32_GPIO_IRQ_LINE 16 #define gpio_range_to_bank(chip) \ container_of(chip, struct stm32_gpio_bank, range) @@ -65,6 +69,7 @@ struct stm32_gpio_bank { spinlock_t lock; struct gpio_chip gpio_chip; struct pinctrl_gpio_range range; + struct fwnode_handle *fwnode; }; struct stm32_pinctrl { @@ -77,6 +82,9 @@ struct stm32_pinctrl { struct stm32_gpio_bank *banks; unsigned nbanks; const struct stm32_pinctrl_match_data *match_data; + struct irq_domain *domain; + struct regmap *regmap; + struct regmap_field *irqmux[STM32_GPIO_PINS_PER_BANK]; }; static inline int stm32_gpio_pin(int gpio) @@ -174,6 +182,20 @@ static int stm32_gpio_direction_output(struct gpio_chip *chip, return 0; } + +static int stm32_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct stm32_gpio_bank *bank = gpiochip_get_data(chip); + struct irq_fwspec fwspec; + + fwspec.fwnode = bank->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset; + fwspec.param[1] = IRQ_TYPE_NONE; + + return irq_create_fwspec_mapping(&fwspec); +} + static struct gpio_chip stm32_gpio_template = { .request = stm32_gpio_request, .free = stm32_gpio_free, @@ -181,10 +203,92 @@ static struct gpio_chip stm32_gpio_template = { .set = stm32_gpio_set, .direction_input = stm32_gpio_direction_input, .direction_output = stm32_gpio_direction_output, + .to_irq = stm32_gpio_to_irq, }; -/* Pinctrl functions */ +static struct irq_chip stm32_gpio_irq_chip = { + .name = "stm32gpio", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, +}; + +static int stm32_gpio_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if ((fwspec->param_count != 2) || + (fwspec->param[0] >= STM32_GPIO_IRQ_LINE)) + return -EINVAL; + + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + return 0; +} +static void stm32_gpio_domain_activate(struct irq_domain *d, + struct irq_data *irq_data) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + + regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->range.id); +} + +static int stm32_gpio_domain_alloc(struct irq_domain *d, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + int ret; + + hwirq = fwspec->param[0]; + parent_fwspec.fwnode = d->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = fwspec->param[0]; + parent_fwspec.param[1] = fwspec->param[1]; + + irq_domain_set_hwirq_and_chip(d, virq, hwirq, &stm32_gpio_irq_chip, + bank); + + ret = gpiochip_lock_as_irq(&bank->gpio_chip, hwirq); + if (ret) { + dev_err(pctl->dev, "Unable to configure STM32 %s%ld as IRQ\n", + bank->gpio_chip.label, hwirq); + return ret; + } + + ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &parent_fwspec); + if (ret) + gpiochip_unlock_as_irq(&bank->gpio_chip, hwirq); + + return ret; +} + +static void stm32_gpio_domain_free(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct irq_data *data = irq_get_irq_data(virq); + + irq_domain_free_irqs_common(d, virq, nr_irqs); + gpiochip_unlock_as_irq(&bank->gpio_chip, data->hwirq); +} + +static const struct irq_domain_ops stm32_gpio_domain_ops = { + .translate = stm32_gpio_domain_translate, + .alloc = stm32_gpio_domain_alloc, + .free = stm32_gpio_domain_free, + .activate = stm32_gpio_domain_activate, +}; + +/* Pinctrl functions */ static struct stm32_pinctrl_group * stm32_pctrl_find_group_by_pin(struct stm32_pinctrl *pctl, u32 pin) { @@ -857,6 +961,17 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, range->pin_base = range->base = range->id * STM32_GPIO_PINS_PER_BANK; range->npins = bank->gpio_chip.ngpio; range->gc = &bank->gpio_chip; + + /* create irq hierarchical domain */ + bank->fwnode = of_node_to_fwnode(np); + + bank->gpio_chip.irqdomain = irq_domain_create_hierarchy(pctl->domain, + 0, STM32_GPIO_IRQ_LINE, bank->fwnode, + &stm32_gpio_domain_ops, bank); + + if (!bank->gpio_chip.irqdomain) + return -ENODEV; + err = gpiochip_add_data(&bank->gpio_chip, bank); if (err) { dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_nr); @@ -867,6 +982,47 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, return 0; } +static int stm32_pctrl_dt_setup_irq(struct platform_device *pdev, + struct stm32_pinctrl *pctl) +{ + struct device_node *np = pdev->dev.of_node, *parent; + struct device *dev = &pdev->dev; + struct regmap *rm; + int offset, ret, i; + + parent = of_irq_find_parent(np); + if (!parent) + return -ENXIO; + + pctl->domain = irq_find_host(parent); + if (!pctl->domain) + return -ENXIO; + + pctl->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pctl->regmap)) + return PTR_ERR(pctl->regmap); + + rm = pctl->regmap; + + ret = of_property_read_u32_index(np, "st,syscfg", 1, &offset); + if (ret) + return ret; + + for (i = 0; i < STM32_GPIO_PINS_PER_BANK; i++) { + struct reg_field mux; + + mux.reg = offset + (i / 4) * 4; + mux.lsb = (i % 4) * 4; + mux.msb = mux.lsb + 3; + + pctl->irqmux[i] = devm_regmap_field_alloc(dev, rm, mux); + if (IS_ERR(pctl->irqmux[i])) + return PTR_ERR(pctl->irqmux[i]); + } + + return 0; +} + static int stm32_pctrl_build_state(struct platform_device *pdev) { struct stm32_pinctrl *pctl = platform_get_drvdata(pdev); @@ -935,6 +1091,10 @@ int stm32_pctl_probe(struct platform_device *pdev) return -EINVAL; } + ret = stm32_pctrl_dt_setup_irq(pdev, pctl); + if (ret) + return ret; + for_each_child_of_node(np, child) if (of_property_read_bool(child, "gpio-controller")) banks++; -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S938683AbcIFQr3 (ORCPT ); Tue, 6 Sep 2016 12:47:29 -0400 Received: from mx07-00178001.pphosted.com ([62.209.51.94]:25574 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S936277AbcIFQrX (ORCPT ); Tue, 6 Sep 2016 12:47:23 -0400 From: Alexandre TORGUE To: Maxime Coquelin , Thomas Gleixner , Jason Cooper , Marc Zyngier , Linus Walleij , Mark Rutland , Rob Herring , , CC: , , , Daniel Thompson , , Subject: [PATCH v4 6/9] pinctrl: Add IRQ support to STM32 gpios Date: Tue, 6 Sep 2016 18:45:38 +0200 Message-ID: <1473180341-1999-7-git-send-email-alexandre.torgue@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1473180341-1999-1-git-send-email-alexandre.torgue@st.com> References: <1473180341-1999-1-git-send-email-alexandre.torgue@st.com> MIME-Version: 1.0 Content-Type: text/plain X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:,, definitions=2016-09-06_06:,, signatures=0 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds IRQ support to STM32 gpios. The EXTI controller has 16 lines dedicated to GPIOs. EXTI line n can be connected to only line n of one of the GPIO ports, for example EXTI0 can be connected to either PA0, or PB0, or PC0... This port selection is done by specifying the port number into System Config registers. Signed-off-by: Maxime Coquelin Signed-off-by: Alexandre TORGUE diff --git a/drivers/pinctrl/stm32/Kconfig b/drivers/pinctrl/stm32/Kconfig index 4c40dae..40d5abc 100644 --- a/drivers/pinctrl/stm32/Kconfig +++ b/drivers/pinctrl/stm32/Kconfig @@ -6,6 +6,8 @@ config PINCTRL_STM32 select PINMUX select GENERIC_PINCONF select GPIOLIB + select GPIOLIB_IRQCHIP + select MFD_SYSCON config PINCTRL_STM32F429 bool "STMicroelectronics STM32F429 pin control" if COMPILE_TEST && !MACH_STM32F429 diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 4ae596b..9154bca 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -20,6 +22,7 @@ #include #include #include +#include #include #include @@ -40,6 +43,7 @@ #define STM32_GPIO_AFRH 0x24 #define STM32_GPIO_PINS_PER_BANK 16 +#define STM32_GPIO_IRQ_LINE 16 #define gpio_range_to_bank(chip) \ container_of(chip, struct stm32_gpio_bank, range) @@ -65,6 +69,7 @@ struct stm32_gpio_bank { spinlock_t lock; struct gpio_chip gpio_chip; struct pinctrl_gpio_range range; + struct fwnode_handle *fwnode; }; struct stm32_pinctrl { @@ -77,6 +82,9 @@ struct stm32_pinctrl { struct stm32_gpio_bank *banks; unsigned nbanks; const struct stm32_pinctrl_match_data *match_data; + struct irq_domain *domain; + struct regmap *regmap; + struct regmap_field *irqmux[STM32_GPIO_PINS_PER_BANK]; }; static inline int stm32_gpio_pin(int gpio) @@ -174,6 +182,20 @@ static int stm32_gpio_direction_output(struct gpio_chip *chip, return 0; } + +static int stm32_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct stm32_gpio_bank *bank = gpiochip_get_data(chip); + struct irq_fwspec fwspec; + + fwspec.fwnode = bank->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset; + fwspec.param[1] = IRQ_TYPE_NONE; + + return irq_create_fwspec_mapping(&fwspec); +} + static struct gpio_chip stm32_gpio_template = { .request = stm32_gpio_request, .free = stm32_gpio_free, @@ -181,10 +203,92 @@ static struct gpio_chip stm32_gpio_template = { .set = stm32_gpio_set, .direction_input = stm32_gpio_direction_input, .direction_output = stm32_gpio_direction_output, + .to_irq = stm32_gpio_to_irq, }; -/* Pinctrl functions */ +static struct irq_chip stm32_gpio_irq_chip = { + .name = "stm32gpio", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, +}; + +static int stm32_gpio_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if ((fwspec->param_count != 2) || + (fwspec->param[0] >= STM32_GPIO_IRQ_LINE)) + return -EINVAL; + + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + return 0; +} +static void stm32_gpio_domain_activate(struct irq_domain *d, + struct irq_data *irq_data) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + + regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->range.id); +} + +static int stm32_gpio_domain_alloc(struct irq_domain *d, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + int ret; + + hwirq = fwspec->param[0]; + parent_fwspec.fwnode = d->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = fwspec->param[0]; + parent_fwspec.param[1] = fwspec->param[1]; + + irq_domain_set_hwirq_and_chip(d, virq, hwirq, &stm32_gpio_irq_chip, + bank); + + ret = gpiochip_lock_as_irq(&bank->gpio_chip, hwirq); + if (ret) { + dev_err(pctl->dev, "Unable to configure STM32 %s%ld as IRQ\n", + bank->gpio_chip.label, hwirq); + return ret; + } + + ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &parent_fwspec); + if (ret) + gpiochip_unlock_as_irq(&bank->gpio_chip, hwirq); + + return ret; +} + +static void stm32_gpio_domain_free(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct irq_data *data = irq_get_irq_data(virq); + + irq_domain_free_irqs_common(d, virq, nr_irqs); + gpiochip_unlock_as_irq(&bank->gpio_chip, data->hwirq); +} + +static const struct irq_domain_ops stm32_gpio_domain_ops = { + .translate = stm32_gpio_domain_translate, + .alloc = stm32_gpio_domain_alloc, + .free = stm32_gpio_domain_free, + .activate = stm32_gpio_domain_activate, +}; + +/* Pinctrl functions */ static struct stm32_pinctrl_group * stm32_pctrl_find_group_by_pin(struct stm32_pinctrl *pctl, u32 pin) { @@ -857,6 +961,17 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, range->pin_base = range->base = range->id * STM32_GPIO_PINS_PER_BANK; range->npins = bank->gpio_chip.ngpio; range->gc = &bank->gpio_chip; + + /* create irq hierarchical domain */ + bank->fwnode = of_node_to_fwnode(np); + + bank->gpio_chip.irqdomain = irq_domain_create_hierarchy(pctl->domain, + 0, STM32_GPIO_IRQ_LINE, bank->fwnode, + &stm32_gpio_domain_ops, bank); + + if (!bank->gpio_chip.irqdomain) + return -ENODEV; + err = gpiochip_add_data(&bank->gpio_chip, bank); if (err) { dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_nr); @@ -867,6 +982,47 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, return 0; } +static int stm32_pctrl_dt_setup_irq(struct platform_device *pdev, + struct stm32_pinctrl *pctl) +{ + struct device_node *np = pdev->dev.of_node, *parent; + struct device *dev = &pdev->dev; + struct regmap *rm; + int offset, ret, i; + + parent = of_irq_find_parent(np); + if (!parent) + return -ENXIO; + + pctl->domain = irq_find_host(parent); + if (!pctl->domain) + return -ENXIO; + + pctl->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pctl->regmap)) + return PTR_ERR(pctl->regmap); + + rm = pctl->regmap; + + ret = of_property_read_u32_index(np, "st,syscfg", 1, &offset); + if (ret) + return ret; + + for (i = 0; i < STM32_GPIO_PINS_PER_BANK; i++) { + struct reg_field mux; + + mux.reg = offset + (i / 4) * 4; + mux.lsb = (i % 4) * 4; + mux.msb = mux.lsb + 3; + + pctl->irqmux[i] = devm_regmap_field_alloc(dev, rm, mux); + if (IS_ERR(pctl->irqmux[i])) + return PTR_ERR(pctl->irqmux[i]); + } + + return 0; +} + static int stm32_pctrl_build_state(struct platform_device *pdev) { struct stm32_pinctrl *pctl = platform_get_drvdata(pdev); @@ -935,6 +1091,10 @@ int stm32_pctl_probe(struct platform_device *pdev) return -EINVAL; } + ret = stm32_pctrl_dt_setup_irq(pdev, pctl); + if (ret) + return ret; + for_each_child_of_node(np, child) if (of_property_read_bool(child, "gpio-controller")) banks++; -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: alexandre.torgue@st.com (Alexandre TORGUE) Date: Tue, 6 Sep 2016 18:45:38 +0200 Subject: [PATCH v4 6/9] pinctrl: Add IRQ support to STM32 gpios In-Reply-To: <1473180341-1999-1-git-send-email-alexandre.torgue@st.com> References: <1473180341-1999-1-git-send-email-alexandre.torgue@st.com> Message-ID: <1473180341-1999-7-git-send-email-alexandre.torgue@st.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org This patch adds IRQ support to STM32 gpios. The EXTI controller has 16 lines dedicated to GPIOs. EXTI line n can be connected to only line n of one of the GPIO ports, for example EXTI0 can be connected to either PA0, or PB0, or PC0... This port selection is done by specifying the port number into System Config registers. Signed-off-by: Maxime Coquelin Signed-off-by: Alexandre TORGUE diff --git a/drivers/pinctrl/stm32/Kconfig b/drivers/pinctrl/stm32/Kconfig index 4c40dae..40d5abc 100644 --- a/drivers/pinctrl/stm32/Kconfig +++ b/drivers/pinctrl/stm32/Kconfig @@ -6,6 +6,8 @@ config PINCTRL_STM32 select PINMUX select GENERIC_PINCONF select GPIOLIB + select GPIOLIB_IRQCHIP + select MFD_SYSCON config PINCTRL_STM32F429 bool "STMicroelectronics STM32F429 pin control" if COMPILE_TEST && !MACH_STM32F429 diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 4ae596b..9154bca 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -20,6 +22,7 @@ #include #include #include +#include #include #include @@ -40,6 +43,7 @@ #define STM32_GPIO_AFRH 0x24 #define STM32_GPIO_PINS_PER_BANK 16 +#define STM32_GPIO_IRQ_LINE 16 #define gpio_range_to_bank(chip) \ container_of(chip, struct stm32_gpio_bank, range) @@ -65,6 +69,7 @@ struct stm32_gpio_bank { spinlock_t lock; struct gpio_chip gpio_chip; struct pinctrl_gpio_range range; + struct fwnode_handle *fwnode; }; struct stm32_pinctrl { @@ -77,6 +82,9 @@ struct stm32_pinctrl { struct stm32_gpio_bank *banks; unsigned nbanks; const struct stm32_pinctrl_match_data *match_data; + struct irq_domain *domain; + struct regmap *regmap; + struct regmap_field *irqmux[STM32_GPIO_PINS_PER_BANK]; }; static inline int stm32_gpio_pin(int gpio) @@ -174,6 +182,20 @@ static int stm32_gpio_direction_output(struct gpio_chip *chip, return 0; } + +static int stm32_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct stm32_gpio_bank *bank = gpiochip_get_data(chip); + struct irq_fwspec fwspec; + + fwspec.fwnode = bank->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset; + fwspec.param[1] = IRQ_TYPE_NONE; + + return irq_create_fwspec_mapping(&fwspec); +} + static struct gpio_chip stm32_gpio_template = { .request = stm32_gpio_request, .free = stm32_gpio_free, @@ -181,10 +203,92 @@ static struct gpio_chip stm32_gpio_template = { .set = stm32_gpio_set, .direction_input = stm32_gpio_direction_input, .direction_output = stm32_gpio_direction_output, + .to_irq = stm32_gpio_to_irq, }; -/* Pinctrl functions */ +static struct irq_chip stm32_gpio_irq_chip = { + .name = "stm32gpio", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, +}; + +static int stm32_gpio_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if ((fwspec->param_count != 2) || + (fwspec->param[0] >= STM32_GPIO_IRQ_LINE)) + return -EINVAL; + + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + return 0; +} +static void stm32_gpio_domain_activate(struct irq_domain *d, + struct irq_data *irq_data) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + + regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->range.id); +} + +static int stm32_gpio_domain_alloc(struct irq_domain *d, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + int ret; + + hwirq = fwspec->param[0]; + parent_fwspec.fwnode = d->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = fwspec->param[0]; + parent_fwspec.param[1] = fwspec->param[1]; + + irq_domain_set_hwirq_and_chip(d, virq, hwirq, &stm32_gpio_irq_chip, + bank); + + ret = gpiochip_lock_as_irq(&bank->gpio_chip, hwirq); + if (ret) { + dev_err(pctl->dev, "Unable to configure STM32 %s%ld as IRQ\n", + bank->gpio_chip.label, hwirq); + return ret; + } + + ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &parent_fwspec); + if (ret) + gpiochip_unlock_as_irq(&bank->gpio_chip, hwirq); + + return ret; +} + +static void stm32_gpio_domain_free(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs) +{ + struct stm32_gpio_bank *bank = d->host_data; + struct irq_data *data = irq_get_irq_data(virq); + + irq_domain_free_irqs_common(d, virq, nr_irqs); + gpiochip_unlock_as_irq(&bank->gpio_chip, data->hwirq); +} + +static const struct irq_domain_ops stm32_gpio_domain_ops = { + .translate = stm32_gpio_domain_translate, + .alloc = stm32_gpio_domain_alloc, + .free = stm32_gpio_domain_free, + .activate = stm32_gpio_domain_activate, +}; + +/* Pinctrl functions */ static struct stm32_pinctrl_group * stm32_pctrl_find_group_by_pin(struct stm32_pinctrl *pctl, u32 pin) { @@ -857,6 +961,17 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, range->pin_base = range->base = range->id * STM32_GPIO_PINS_PER_BANK; range->npins = bank->gpio_chip.ngpio; range->gc = &bank->gpio_chip; + + /* create irq hierarchical domain */ + bank->fwnode = of_node_to_fwnode(np); + + bank->gpio_chip.irqdomain = irq_domain_create_hierarchy(pctl->domain, + 0, STM32_GPIO_IRQ_LINE, bank->fwnode, + &stm32_gpio_domain_ops, bank); + + if (!bank->gpio_chip.irqdomain) + return -ENODEV; + err = gpiochip_add_data(&bank->gpio_chip, bank); if (err) { dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_nr); @@ -867,6 +982,47 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, return 0; } +static int stm32_pctrl_dt_setup_irq(struct platform_device *pdev, + struct stm32_pinctrl *pctl) +{ + struct device_node *np = pdev->dev.of_node, *parent; + struct device *dev = &pdev->dev; + struct regmap *rm; + int offset, ret, i; + + parent = of_irq_find_parent(np); + if (!parent) + return -ENXIO; + + pctl->domain = irq_find_host(parent); + if (!pctl->domain) + return -ENXIO; + + pctl->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pctl->regmap)) + return PTR_ERR(pctl->regmap); + + rm = pctl->regmap; + + ret = of_property_read_u32_index(np, "st,syscfg", 1, &offset); + if (ret) + return ret; + + for (i = 0; i < STM32_GPIO_PINS_PER_BANK; i++) { + struct reg_field mux; + + mux.reg = offset + (i / 4) * 4; + mux.lsb = (i % 4) * 4; + mux.msb = mux.lsb + 3; + + pctl->irqmux[i] = devm_regmap_field_alloc(dev, rm, mux); + if (IS_ERR(pctl->irqmux[i])) + return PTR_ERR(pctl->irqmux[i]); + } + + return 0; +} + static int stm32_pctrl_build_state(struct platform_device *pdev) { struct stm32_pinctrl *pctl = platform_get_drvdata(pdev); @@ -935,6 +1091,10 @@ int stm32_pctl_probe(struct platform_device *pdev) return -EINVAL; } + ret = stm32_pctrl_dt_setup_irq(pdev, pctl); + if (ret) + return ret; + for_each_child_of_node(np, child) if (of_property_read_bool(child, "gpio-controller")) banks++; -- 1.9.1