From d7ee4d0a67315736b402291ed48b77e701c76224 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:39 -0500 Subject: pinctrl: qcom: spmi-gpio: add support for three new variants Add support for qcom,pm8005-gpio, qcom,pm8998-gpio, and qcom,pmi8998-gpio. These three variants are already in use in some arm64 dtsi files. Those boards work since the generic binding qcom,spmi-gpio is also specified. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index 4458d44dfcf6..5f2977982ef1 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -1063,10 +1063,13 @@ static int pmic_gpio_remove(struct platform_device *pdev) } static const struct of_device_id pmic_gpio_of_match[] = { + { .compatible = "qcom,pm8005-gpio" }, /* 4 GPIO's */ { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ { .compatible = "qcom,pmi8994-gpio" }, /* 10 GPIO's */ + { .compatible = "qcom,pm8998-gpio" }, /* 26 GPIO's */ + { .compatible = "qcom,pmi8998-gpio" }, /* 14 GPIO's */ { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ { .compatible = "qcom,pms405-gpio" }, /* 12 GPIO's, holes on 1 9 10 */ { .compatible = "qcom,spmi-gpio" }, /* Generic */ -- cgit v1.2.3 From cfacef373505dce8735c562a147d14611db259b9 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:40 -0500 Subject: pinctrl: qcom: spmi-gpio: hardcode IRQ counts The probing of this driver calls platform_irq_count, which will setup all of the IRQs that are configured in device tree. In preparation for converting this driver to be a hierarchical IRQ chip, hardcode the IRQ count based on the hardware type so that all the IRQs are not configured immediately and are configured on an as-needed basis later in the boot process. This change will also allow for the removal of the interrupts property later in this patch series once the hierarchical IRQ chip support is in. This patch also removes the generic qcom,spmi-gpio OF match since we don't know the number of pins. All of the existing upstream bindings already include the more-specific binding. The pm8941 code was tested on a LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index 5f2977982ef1..b6fa2c7dbb26 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -951,13 +951,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) return ret; } - npins = platform_irq_count(pdev); - if (!npins) - return -EINVAL; - if (npins < 0) - return npins; - - BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups)); + npins = (uintptr_t) device_get_match_data(&pdev->dev); state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) @@ -1063,16 +1057,16 @@ static int pmic_gpio_remove(struct platform_device *pdev) } static const struct of_device_id pmic_gpio_of_match[] = { - { .compatible = "qcom,pm8005-gpio" }, /* 4 GPIO's */ - { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ - { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ - { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ - { .compatible = "qcom,pmi8994-gpio" }, /* 10 GPIO's */ - { .compatible = "qcom,pm8998-gpio" }, /* 26 GPIO's */ - { .compatible = "qcom,pmi8998-gpio" }, /* 14 GPIO's */ - { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ - { .compatible = "qcom,pms405-gpio" }, /* 12 GPIO's, holes on 1 9 10 */ - { .compatible = "qcom,spmi-gpio" }, /* Generic */ + { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, + { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, + { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, + { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, + { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, + { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, + /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ + { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, { }, }; -- cgit v1.2.3 From ca69e2d165eb3d060cc9ad70a745e27a2cf4310b Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:44 -0500 Subject: qcom: spmi-gpio: add support for hierarchical IRQ chip spmi-gpio did not have any irqchip support so consumers of this in device tree would need to call gpio[d]_to_irq() in order to get the proper IRQ on the underlying PMIC. IRQ chips in device tree should be usable from the start without the consumer having to make an additional call to get the proper IRQ on the parent. This patch adds hierarchical IRQ chip support to the spmi-gpio code to correct this issue. Driver was tested using the volume buttons (via gpio-keys) on the LG Nexus 5 (hammerhead) phone with the following two configurations. volume-up { interrupts-extended = <&pm8941_gpios 2 IRQ_TYPE_EDGE_BOTH>; ... }; volume-up { gpios = <&pm8941_gpios 2 GPIO_ACTIVE_LOW>; ... }; Both configurations now show that spmi-gpio is the IRQ domain and that the IRQ is setup in a hierarchy. $ grep volume_up /proc/interrupts 72: 6 0 spmi-gpio 1 Edge volume_up $ cat /sys/kernel/debug/irq/irqs/72 handler: handle_edge_irq device: (null) status: 0x00000403 _IRQ_NOPROBE istate: 0x00000000 ddepth: 0 wdepth: 0 dstate: 0x02400203 IRQ_TYPE_EDGE_RISING IRQ_TYPE_EDGE_FALLING IRQD_ACTIVATED IRQD_IRQ_STARTED node: 0 affinity: 0-3 effectiv: domain: :soc:spmi@fc4cf000:pm8941@0:gpios@c000 hwirq: 0x1 chip: spmi-gpio flags: 0x4 IRQCHIP_MASK_ON_SUSPEND parent: domain: :soc:spmi@fc4cf000 hwirq: 0xc100057 chip: pmic_arb flags: 0x4 IRQCHIP_MASK_ON_SUSPEND Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Marc Zyngier Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 115 ++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 10 deletions(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index b6fa2c7dbb26..e01a556ac586 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -136,7 +137,6 @@ enum pmic_gpio_func_index { /** * struct pmic_gpio_pad - keep current GPIO settings * @base: Address base in SPMI device. - * @irq: IRQ number which this GPIO generate. * @is_enabled: Set to false when GPIO should be put in high Z state. * @out_value: Cached pin output value * @have_buffer: Set to true if GPIO output could be configured in push-pull, @@ -156,7 +156,6 @@ enum pmic_gpio_func_index { */ struct pmic_gpio_pad { u16 base; - int irq; bool is_enabled; bool out_value; bool have_buffer; @@ -179,6 +178,8 @@ struct pmic_gpio_state { struct regmap *map; struct pinctrl_dev *ctrl; struct gpio_chip chip; + struct fwnode_handle *fwnode; + struct irq_domain *domain; }; static const struct pinconf_generic_params pmic_gpio_bindings[] = { @@ -761,11 +762,18 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip, static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin) { struct pmic_gpio_state *state = gpiochip_get_data(chip); - struct pmic_gpio_pad *pad; + struct irq_fwspec fwspec; - pad = state->ctrl->desc->pins[pin].drv_data; + fwspec.fwnode = state->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = pin + PMIC_GPIO_PHYSICAL_OFFSET; + /* + * Set the type to a safe value temporarily. This will be overwritten + * later with the proper value by irq_set_type. + */ + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; - return pad->irq; + return irq_create_fwspec_mapping(&fwspec); } static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -935,8 +943,78 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, return 0; } +static struct irq_chip pmic_gpio_irq_chip = { + .name = "spmi-gpio", + .irq_ack = irq_chip_ack_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = irq_chip_set_wake_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND, +}; + +static int pmic_gpio_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct pmic_gpio_state *state = container_of(domain->host_data, + struct pmic_gpio_state, + chip); + + if (fwspec->param_count != 2 || fwspec->param[0] >= state->chip.ngpio) + return -EINVAL; + + *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; + *type = fwspec->param[1]; + + return 0; +} + +static int pmic_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pmic_gpio_state *state = container_of(domain->host_data, + struct pmic_gpio_state, + chip); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = pmic_gpio_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pmic_gpio_irq_chip, state, + handle_level_irq, NULL, NULL); + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 4; + parent_fwspec.param[0] = 0; + parent_fwspec.param[1] = hwirq + 0xc0; + parent_fwspec.param[2] = 0; + parent_fwspec.param[3] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops pmic_gpio_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .alloc = pmic_gpio_domain_alloc, + .deactivate = gpiochip_irq_domain_deactivate, + .free = irq_domain_free_irqs_common, + .translate = pmic_gpio_domain_translate, +}; + static int pmic_gpio_probe(struct platform_device *pdev) { + struct irq_domain *parent_domain; + struct device_node *parent_node; struct device *dev = &pdev->dev; struct pinctrl_pin_desc *pindesc; struct pinctrl_desc *pctrldesc; @@ -993,10 +1071,6 @@ static int pmic_gpio_probe(struct platform_device *pdev) pindesc->number = i; pindesc->name = pmic_gpio_groups[i]; - pad->irq = platform_get_irq(pdev, i); - if (pad->irq < 0) - return pad->irq; - pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; ret = pmic_gpio_populate(state, pad); @@ -1016,10 +1090,28 @@ static int pmic_gpio_probe(struct platform_device *pdev) if (IS_ERR(state->ctrl)) return PTR_ERR(state->ctrl); + parent_node = of_irq_find_parent(state->dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + state->fwnode = of_node_to_fwnode(state->dev->of_node); + state->domain = irq_domain_create_hierarchy(parent_domain, 0, + state->chip.ngpio, + state->fwnode, + &pmic_gpio_domain_ops, + &state->chip); + if (!state->domain) + return -ENODEV; + ret = gpiochip_add_data(&state->chip, state); if (ret) { dev_err(state->dev, "can't add gpio chip\n"); - return ret; + goto err_chip_add_data; } /* @@ -1045,6 +1137,8 @@ static int pmic_gpio_probe(struct platform_device *pdev) err_range: gpiochip_remove(&state->chip); +err_chip_add_data: + irq_domain_remove(state->domain); return ret; } @@ -1053,6 +1147,7 @@ static int pmic_gpio_remove(struct platform_device *pdev) struct pmic_gpio_state *state = platform_get_drvdata(pdev); gpiochip_remove(&state->chip); + irq_domain_remove(state->domain); return 0; } -- cgit v1.2.3 From 5c713d9394f3aabb73975dbf4db3c6502dc68956 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 4 Feb 2019 04:58:53 -0500 Subject: pinctrl: qcom: spmi-gpio: select IRQ_DOMAIN_HIERARCHY in Kconfig Select IRQ_DOMAIN_HIERARCHY for spmi-gpio in Kconfig since this driver is now setup as a hierarchical IRQ chip. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 836e9f3eae4c..93efbffcd3a4 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -137,6 +137,7 @@ config PINCTRL_QCOM_SPMI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, -- cgit v1.2.3 From dac7da986ba39e9ff6b078ae15b561c4929edec1 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 11 Feb 2019 19:52:05 -0800 Subject: qcom: spmi-gpio: Fix boundary conditions IRQ domain translate GPIOs on the SPMI PMIC are numbered 1..ngpio, so the boundary check in pmic_gpio_domain_translate() is off by one, correct this. Fixes: ca69e2d165eb ("qcom: spmi-gpio: add support for hierarchical IRQ chip") Signed-off-by: Bjorn Andersson Reviewed-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index e01a556ac586..cb512c7a5251 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -962,7 +962,8 @@ static int pmic_gpio_domain_translate(struct irq_domain *domain, struct pmic_gpio_state, chip); - if (fwspec->param_count != 2 || fwspec->param[0] >= state->chip.ngpio) + if (fwspec->param_count != 2 || + fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio) return -EINVAL; *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; -- cgit v1.2.3 From 86291029e97eaf6a9c2ed43e7968ba8cf9f9f3b7 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:21 -0500 Subject: pinctrl: qcom: ssbi-gpio: hardcode IRQ counts The probing of this driver calls platform_irq_count, which will setup all of the IRQs that are configured in device tree. In preparation for converting this driver to be a hierarchical IRQ chip, hardcode the IRQ count based on the hardware type so that all the IRQs are not configured immediately and are configured on an as-needed basis later in the boot process. This change will also allow for the removal of the interrupts property later in this patch series once the hierarchical IRQ chip support is in. This patch also removes the generic qcom,ssbi-gpio OF match since we don't know the number of pins. All of the existing upstream bindings already include the more-specific binding. This change was tested on an APQ8060 DragonBoard. Signed-off-by: Brian Masney Tested-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index ded7d765af2e..23641e4d4521 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -665,12 +665,11 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl, } static const struct of_device_id pm8xxx_gpio_of_match[] = { - { .compatible = "qcom,pm8018-gpio" }, - { .compatible = "qcom,pm8038-gpio" }, - { .compatible = "qcom,pm8058-gpio" }, - { .compatible = "qcom,pm8917-gpio" }, - { .compatible = "qcom,pm8921-gpio" }, - { .compatible = "qcom,ssbi-gpio" }, + { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, + { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, + { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 }, + { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 }, + { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 }, { }, }; MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); @@ -680,20 +679,14 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) struct pm8xxx_pin_data *pin_data; struct pinctrl_pin_desc *pins; struct pm8xxx_gpio *pctrl; - int ret; - int i, npins; + int ret, i; pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); if (!pctrl) return -ENOMEM; pctrl->dev = &pdev->dev; - npins = platform_irq_count(pdev); - if (!npins) - return -EINVAL; - if (npins < 0) - return npins; - pctrl->npins = npins; + pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev); pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!pctrl->regmap) { -- cgit v1.2.3 From 9d2b563bc23acfa93e7716b3396fd2f79fa8f0cd Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:26 -0500 Subject: qcom: ssbi-gpio: add support for hierarchical IRQ chip ssbi-gpio did not have any irqchip support so consumers of this in device tree would need to call gpio[d]_to_irq() in order to get the proper IRQ on the underlying PMIC. IRQ chips in device tree should be usable from the start without the consumer having to make an additional call to get the proper IRQ on the parent. This patch adds hierarchical IRQ chip support to the ssbi-gpio code to correct this issue. The constant PM8XXX_GPIO_PHYSICAL_OFFSET is introduced to replace the hardcoded '1' that previously existed in two places in this driver to improve code readability. This change was tested on an APQ8060 DragonBoard. Signed-off-by: Brian Masney Tested-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig | 1 + drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 135 ++++++++++++++++++++++++++++--- 2 files changed, 124 insertions(+), 12 deletions(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 836e9f3eae4c..8e7e4255b7a4 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -149,6 +149,7 @@ config PINCTRL_QCOM_SSBI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index 23641e4d4521..84a232450000 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -55,6 +55,8 @@ #define PM8XXX_MAX_GPIOS 44 +#define PM8XXX_GPIO_PHYSICAL_OFFSET 1 + /* custom pinconf parameters */ #define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1) #define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2) @@ -99,6 +101,9 @@ struct pm8xxx_gpio { struct pinctrl_desc desc; unsigned npins; + + struct fwnode_handle *fwnode; + struct irq_domain *domain; }; static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = { @@ -499,11 +504,12 @@ static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset) if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) { ret = pin->output_value; - } else { + } else if (pin->irq >= 0) { ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state); if (!ret) ret = !!state; - } + } else + ret = -EINVAL; return ret; } @@ -533,16 +539,39 @@ static int pm8xxx_gpio_of_xlate(struct gpio_chip *chip, if (flags) *flags = gpio_desc->args[1]; - return gpio_desc->args[0] - 1; + return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; } static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); + struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; + struct irq_fwspec fwspec; + int ret; + + fwspec.fwnode = pctrl->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset + PM8XXX_GPIO_PHYSICAL_OFFSET; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + + ret = irq_create_fwspec_mapping(&fwspec); + + /* + * Cache the IRQ since pm8xxx_gpio_get() needs this to get determine the + * line level. + */ + pin->irq = ret; + + return ret; +} + +static void pm8xxx_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; - return pin->irq; + pin->irq = -1; } #ifdef CONFIG_DEBUG_FS @@ -571,7 +600,7 @@ static void pm8xxx_gpio_dbg_show_one(struct seq_file *s, "no", "high", "medium", "low" }; - seq_printf(s, " gpio%-2d:", offset + 1); + seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET); if (pin->disable) { seq_puts(s, " ---"); } else { @@ -603,6 +632,7 @@ static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) #endif static const struct gpio_chip pm8xxx_gpio_template = { + .free = pm8xxx_gpio_free, .direction_input = pm8xxx_gpio_direction_input, .direction_output = pm8xxx_gpio_direction_output, .get = pm8xxx_gpio_get, @@ -664,6 +694,68 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl, return 0; } +static struct irq_chip pm8xxx_irq_chip = { + .name = "ssbi-gpio", + .irq_mask_ack = irq_chip_mask_ack_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, +}; + +static int pm8xxx_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, + struct pm8xxx_gpio, chip); + + if (fwspec->param_count != 2 || fwspec->param[0] >= pctrl->chip.ngpio) + return -EINVAL; + + *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; + *type = fwspec->param[1]; + + return 0; +} + +static int pm8xxx_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, + struct pm8xxx_gpio, chip); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = pm8xxx_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pm8xxx_irq_chip, pctrl, handle_level_irq, + NULL, NULL); + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = hwirq + 0xc0; + parent_fwspec.param[1] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops pm8xxx_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .alloc = pm8xxx_domain_alloc, + .deactivate = gpiochip_irq_domain_deactivate, + .free = irq_domain_free_irqs_common, + .translate = pm8xxx_domain_translate, +}; + static const struct of_device_id pm8xxx_gpio_of_match[] = { { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, @@ -677,6 +769,8 @@ MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); static int pm8xxx_gpio_probe(struct platform_device *pdev) { struct pm8xxx_pin_data *pin_data; + struct irq_domain *parent_domain; + struct device_node *parent_node; struct pinctrl_pin_desc *pins; struct pm8xxx_gpio *pctrl; int ret, i; @@ -713,12 +807,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) for (i = 0; i < pctrl->desc.npins; i++) { pin_data[i].reg = SSBI_REG_ADDR_GPIO(i); - pin_data[i].irq = platform_get_irq(pdev, i); - if (pin_data[i].irq < 0) { - dev_err(&pdev->dev, - "missing interrupts for pin %d\n", i); - return pin_data[i].irq; - } + pin_data[i].irq = -1; ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); if (ret) @@ -749,10 +838,29 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) pctrl->chip.of_gpio_n_cells = 2; pctrl->chip.label = dev_name(pctrl->dev); pctrl->chip.ngpio = pctrl->npins; + + parent_node = of_irq_find_parent(pctrl->dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + pctrl->fwnode = of_node_to_fwnode(pctrl->dev->of_node); + pctrl->domain = irq_domain_create_hierarchy(parent_domain, 0, + pctrl->chip.ngpio, + pctrl->fwnode, + &pm8xxx_domain_ops, + &pctrl->chip); + if (!pctrl->domain) + return -ENODEV; + ret = gpiochip_add_data(&pctrl->chip, pctrl); if (ret) { dev_err(&pdev->dev, "failed register gpiochip\n"); - return ret; + goto err_chip_add_data; } /* @@ -782,6 +890,8 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) unregister_gpiochip: gpiochip_remove(&pctrl->chip); +err_chip_add_data: + irq_domain_remove(pctrl->domain); return ret; } @@ -791,6 +901,7 @@ static int pm8xxx_gpio_remove(struct platform_device *pdev) struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev); gpiochip_remove(&pctrl->chip); + irq_domain_remove(pctrl->domain); return 0; } -- cgit v1.2.3 From 79890c2ec4860c3b715f89248c51abcc76a1fa39 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Wed, 13 Feb 2019 20:36:41 -0500 Subject: qcom: ssbi-gpio: correct boundary conditions in pm8xxx_domain_translate SSBI GPIOs are numbered 1..ngpio, so the boundary check in pm8xxx_domain_translate() is off by one. This patch corrects that check. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index 84a232450000..08dd62b5cebe 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -710,7 +710,8 @@ static int pm8xxx_domain_translate(struct irq_domain *domain, struct pm8xxx_gpio *pctrl = container_of(domain->host_data, struct pm8xxx_gpio, chip); - if (fwspec->param_count != 2 || fwspec->param[0] >= pctrl->chip.ngpio) + if (fwspec->param_count != 2 || fwspec->param[0] < 1 || + fwspec->param[0] > pctrl->chip.ngpio) return -EINVAL; *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; -- cgit v1.2.3