From 697818f383fc548cdbfb1528c7067994739ace04 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:38 -0500 Subject: [PATCH 01/15] dt-bindings: pinctrl: qcom-pmic-gpio: add qcom,pmi8998-gpio binding Add support for the PMI8998 GPIO variant to the Qualcomm PMIC GPIO binding document. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Reviewed-by: Rob Herring Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt index 759aa1732e48..7f64a7e92c28 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt @@ -19,6 +19,7 @@ PMIC's from Qualcomm. "qcom,pm8998-gpio" "qcom,pma8084-gpio" "qcom,pmi8994-gpio" + "qcom,pmi8998-gpio" "qcom,pms405-gpio" And must contain either "qcom,spmi-gpio" or "qcom,ssbi-gpio" From d7ee4d0a67315736b402291ed48b77e701c76224 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:39 -0500 Subject: [PATCH 02/15] 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(+) 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 */ From cfacef373505dce8735c562a147d14611db259b9 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:40 -0500 Subject: [PATCH 03/15] 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(-) 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 }, { }, }; From 12a9eeaebba34f4f0abe1548ecb460414e285c49 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:41 -0500 Subject: [PATCH 04/15] spmi: pmic-arb: convert to v2 irq interfaces to support hierarchical IRQ chips Convert the spmi-pmic-arb IRQ code to use the version 2 IRQ interface in order to support hierarchical IRQ chips. This is necessary so that spmi-gpio can be setup as a hierarchical IRQ chip with pmic-arb as the parent. IRQ chips in device tree should be usable from the start without the consumer having to make an additional call to gpio[d]_to_irq() to get the proper IRQ on the parent. The old qpnpint_irq_domain_map function would hardcode the handler as handle_level_irq, however qpnpint_irq_set_type would later override the handler. Properly set the handler when the IRQ is mapped. This new code doesn't return an error for IRQ_TYPE_NONE and preserves the existing behavior of using handle_level_irq since there are some broken device tree bindings that need to be corrected first. Driver was tested on a LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Marc Zyngier Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 67 +++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 360b8218f322..928759242e42 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -666,7 +666,8 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, return 0; } -static int qpnpint_irq_request_resources(struct irq_data *d) +static int qpnpint_irq_domain_activate(struct irq_domain *domain, + struct irq_data *d, bool reserve) { struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); u16 periph = hwirq_to_per(d->hwirq); @@ -692,27 +693,25 @@ static struct irq_chip pmic_arb_irqchip = { .irq_set_type = qpnpint_irq_set_type, .irq_set_wake = qpnpint_irq_set_wake, .irq_get_irqchip_state = qpnpint_get_irqchip_state, - .irq_request_resources = qpnpint_irq_request_resources, .flags = IRQCHIP_MASK_ON_SUSPEND, }; -static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, - struct device_node *controller, - const u32 *intspec, - unsigned int intsize, - unsigned long *out_hwirq, - unsigned int *out_type) +static int qpnpint_irq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type) { struct spmi_pmic_arb *pmic_arb = d->host_data; + u32 *intspec = fwspec->param; u16 apid, ppid; int rc; dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", intspec[0], intspec[1], intspec[2]); - if (irq_domain_get_of_node(d) != controller) + if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node) return -EINVAL; - if (intsize != 4) + if (fwspec->param_count != 4) return -EINVAL; if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) return -EINVAL; @@ -740,17 +739,43 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, return 0; } -static int qpnpint_irq_domain_map(struct irq_domain *d, - unsigned int virq, - irq_hw_number_t hwirq) + +static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, + struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, unsigned int type) { - struct spmi_pmic_arb *pmic_arb = d->host_data; + irq_flow_handler_t handler; - dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); + dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", + virq, hwirq, type); + + if (type & IRQ_TYPE_EDGE_BOTH) + handler = handle_edge_irq; + else + handler = handle_level_irq; + + irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, + handler, NULL, NULL); +} + +static int qpnpint_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *data) +{ + struct spmi_pmic_arb *pmic_arb = domain->host_data; + struct irq_fwspec *fwspec = data; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, + type); - irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); - irq_set_chip_data(virq, d->host_data); - irq_set_noprobe(virq); return 0; } @@ -1126,8 +1151,10 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = { }; static const struct irq_domain_ops pmic_arb_irq_domain_ops = { - .map = qpnpint_irq_domain_map, - .xlate = qpnpint_irq_domain_dt_translate, + .activate = qpnpint_irq_domain_activate, + .alloc = qpnpint_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .translate = qpnpint_irq_domain_translate, }; static int spmi_pmic_arb_probe(struct platform_device *pdev) From ef74f70e5a10cc2a78cc5529e564170cabcda9af Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:42 -0500 Subject: [PATCH 05/15] gpio: add irq domain activate/deactivate functions This adds the two new functions gpiochip_irq_domain_activate and gpiochip_irq_domain_deactivate that can be used as the activate and deactivate functions in the struct irq_domain_ops. This is for situations where only gpiochip_{lock,unlock}_as_irq needs to be called. SPMI and SSBI GPIO are two users that will initially use these functions. Signed-off-by: Brian Masney Suggested-by: Stephen Boyd Reviewed-by: Stephen Boyd Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/gpio/driver.h | 5 +++++ 2 files changed, 42 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1651d7f0a303..361a09c8138a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1775,6 +1775,43 @@ static const struct irq_domain_ops gpiochip_domain_ops = { .xlate = irq_domain_xlate_twocell, }; +/** + * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * @reserve: If set, only reserve an interrupt vector instead of assigning one + * + * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be + * used as the activate function for the &struct irq_domain_ops. The host_data + * for the IRQ domain must be the &struct gpio_chip. + */ +int gpiochip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve) +{ + struct gpio_chip *chip = domain->host_data; + + return gpiochip_lock_as_irq(chip, data->hwirq); +} +EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate); + +/** + * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * + * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to + * be used as the deactivate function for the &struct irq_domain_ops. The + * host_data for the IRQ domain must be the &struct gpio_chip. + */ +void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data) +{ + struct gpio_chip *chip = domain->host_data; + + return gpiochip_unlock_as_irq(chip, data->hwirq); +} +EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); + static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { if (!gpiochip_irqchip_irq_valid(chip, offset)) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 07cddbf45186..01497910f023 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -472,6 +472,11 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq); void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq); +int gpiochip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve); +void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data); + void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int parent_irq, From 682aefaa81e6c9b76bf45391f92a2b9c6ec96a0c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:43 -0500 Subject: [PATCH 06/15] spmi: pmic-arb: disassociate old virq if hwirq mapping already exists Check to see if the hwirq is already associated with another virq on this IRQ domain. If so, then disassociate it before associating the hwirq with the new virq. This is a temporary hack that is needed in order to not break git bisect for existing boards. The next patch in this series converts spmi-gpio to be a hierarchical IRQ chip, then there are several patches to update all of the device tree files, and finally this patch will be reverted within the same patch series. IRQs for spmi-gpio are all initially setup without an IRQ hierarchy on pmic-arb when mfd/qcom-spmi-pmic.c is probed (via the devm_of_platform_populate call) due to the interrupts property in device tree. Once spmi-gpio is converted to be a hierarchical IRQ chip in the next patch, existing users of gpio[d]_to_irq() will call pmic_gpio_to_irq(), and that will use the new IRQ chip code in spmi-gpio that sets up the IRQ in an IRQ hierarchy. The hwirq is now associated with two Linux virqs and interrupts will not work as expected. This patch corrects that issue. Driver was tested using gpio-keys and iadc/vadc on the LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Acked-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 928759242e42..bc7dc1d6e4b2 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -745,10 +745,15 @@ static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, irq_hw_number_t hwirq, unsigned int type) { irq_flow_handler_t handler; + unsigned int old_virq; dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", virq, hwirq, type); + old_virq = irq_find_mapping(domain, hwirq); + if (old_virq) + irq_domain_disassociate(domain, old_virq); + if (type & IRQ_TYPE_EDGE_BOTH) handler = handle_edge_irq; else From ca69e2d165eb3d060cc9ad70a745e27a2cf4310b Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:44 -0500 Subject: [PATCH 07/15] 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(-) 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; } From 5f540fb4821a5444350ab3311fff60013d755d8f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:45 -0500 Subject: [PATCH 08/15] ARM: dts: qcom: pm8941: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. Code was tested on the LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm/boot/dts/qcom-pm8941.dtsi | 38 ++---------------------------- 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/arch/arm/boot/dts/qcom-pm8941.dtsi b/arch/arm/boot/dts/qcom-pm8941.dtsi index 2515c5c217ac..e65049e0fb45 100644 --- a/arch/arm/boot/dts/qcom-pm8941.dtsi +++ b/arch/arm/boot/dts/qcom-pm8941.dtsi @@ -64,42 +64,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>, - <0 0xd6 0 IRQ_TYPE_NONE>, - <0 0xd7 0 IRQ_TYPE_NONE>, - <0 0xd8 0 IRQ_TYPE_NONE>, - <0 0xd9 0 IRQ_TYPE_NONE>, - <0 0xda 0 IRQ_TYPE_NONE>, - <0 0xdb 0 IRQ_TYPE_NONE>, - <0 0xdc 0 IRQ_TYPE_NONE>, - <0 0xdd 0 IRQ_TYPE_NONE>, - <0 0xde 0 IRQ_TYPE_NONE>, - <0 0xdf 0 IRQ_TYPE_NONE>, - <0 0xe0 0 IRQ_TYPE_NONE>, - <0 0xe1 0 IRQ_TYPE_NONE>, - <0 0xe2 0 IRQ_TYPE_NONE>, - <0 0xe3 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; boost_bypass_n_pin: boost-bypass { pins = "gpio21"; From c9a0ef552894ebdd6e4efc064ef10ddcb578a42f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:46 -0500 Subject: [PATCH 09/15] ARM: dts: qcom: pma8084: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm/boot/dts/qcom-pma8084.dtsi | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/arch/arm/boot/dts/qcom-pma8084.dtsi b/arch/arm/boot/dts/qcom-pma8084.dtsi index aac7e73b6872..8f5ea7add20f 100644 --- a/arch/arm/boot/dts/qcom-pma8084.dtsi +++ b/arch/arm/boot/dts/qcom-pma8084.dtsi @@ -32,28 +32,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; pma8084_mpps: mpps@a000 { From a61326c076f25b7b808cccb827576808ec1cfa9d Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:47 -0500 Subject: [PATCH 10/15] arm64: dts: qcom: pm8005: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/qcom/pm8005.dtsi | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pm8005.dtsi b/arch/arm64/boot/dts/qcom/pm8005.dtsi index 4d5aca3eeb69..c0ddf128136c 100644 --- a/arch/arm64/boot/dts/qcom/pm8005.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8005.dtsi @@ -16,10 +16,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; From a1738363e41a65733a0d277d8189efa98315fdff Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:48 -0500 Subject: [PATCH 11/15] arm64: dts: qcom: pm8998: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/qcom/pm8998.dtsi | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi index f1025a50c227..43cb5ea14089 100644 --- a/arch/arm64/boot/dts/qcom/pm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8998.dtsi @@ -94,32 +94,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>, - <0 0xd6 0 IRQ_TYPE_NONE>, - <0 0xd7 0 IRQ_TYPE_NONE>, - <0 0xd8 0 IRQ_TYPE_NONE>, - <0 0xd9 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; From 8cff9c8a78810ca09fd7c4d6d5d4f86a8b113059 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:49 -0500 Subject: [PATCH 12/15] arm64: dts: qcom: pmi8994: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/qcom/pmi8994.dtsi | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pmi8994.dtsi b/arch/arm64/boot/dts/qcom/pmi8994.dtsi index dae1cdc23f54..3aee10e3f921 100644 --- a/arch/arm64/boot/dts/qcom/pmi8994.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8994.dtsi @@ -15,16 +15,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <2 0xc0 0 IRQ_TYPE_NONE>, - <2 0xc1 0 IRQ_TYPE_NONE>, - <2 0xc2 0 IRQ_TYPE_NONE>, - <2 0xc3 0 IRQ_TYPE_NONE>, - <2 0xc4 0 IRQ_TYPE_NONE>, - <2 0xc5 0 IRQ_TYPE_NONE>, - <2 0xc6 0 IRQ_TYPE_NONE>, - <2 0xc7 0 IRQ_TYPE_NONE>, - <2 0xc8 0 IRQ_TYPE_NONE>, - <2 0xc9 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; From f14a5e6da4a50e78a18384faf81eef6171909d3f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:50 -0500 Subject: [PATCH 13/15] arm64: dts: qcom: pmi8998: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/qcom/pmi8998.dtsi | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index da3285e216e2..051f57e7d6ac 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -14,20 +14,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; From 135ef21ab064dd9811b51d5fa225829011adb10c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:51 -0500 Subject: [PATCH 14/15] spmi: pmic-arb: validate type when mapping IRQ qpnpint_irq_domain_map did not validate the IRQ type and this can cause IRQs to not work as expected if an unsupported type (such as IRQ_TYPE_NONE) is passed in. Now that spmi-gpio is a hierarchical IRQ controller, and all device tree bindings have been updated, add additional validation to the type field. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index bc7dc1d6e4b2..5666b99c9a92 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -740,9 +740,9 @@ static int qpnpint_irq_domain_translate(struct irq_domain *d, } -static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, - struct irq_domain *domain, unsigned int virq, - irq_hw_number_t hwirq, unsigned int type) +static int qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, + struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, unsigned int type) { irq_flow_handler_t handler; unsigned int old_virq; @@ -756,11 +756,15 @@ static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, if (type & IRQ_TYPE_EDGE_BOTH) handler = handle_edge_irq; - else + else if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) handler = handle_level_irq; + else + return -EINVAL; irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, handler, NULL, NULL); + + return 0; } static int qpnpint_irq_domain_alloc(struct irq_domain *domain, @@ -777,9 +781,12 @@ static int qpnpint_irq_domain_alloc(struct irq_domain *domain, if (ret) return ret; - for (i = 0; i < nr_irqs; i++) - qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, - type); + for (i = 0; i < nr_irqs; i++) { + ret = qpnpint_irq_domain_map(pmic_arb, domain, virq + i, + hwirq + i, type); + if (ret) + return ret; + } return 0; } From e7dc6af82c284b8c79a0be5d2c9b555c3d793a3e Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:52 -0500 Subject: [PATCH 15/15] spmi: pmic-arb: revert "disassociate old virq if hwirq mapping already exists" Now that spmi-gpio is a proper hierarchical IRQ chip, and all in-tree users of device tree have been updated, we can now drop the hack that was introduced to disassociate the old Linux virq if a hwirq mapping already exists. That patch was introduced to not break git bisect for any existing boards. Driver was tested using gpio-keys and iadc/vadc on the LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 5666b99c9a92..316e2708f3ba 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -745,15 +745,10 @@ static int qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, irq_hw_number_t hwirq, unsigned int type) { irq_flow_handler_t handler; - unsigned int old_virq; dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", virq, hwirq, type); - old_virq = irq_find_mapping(domain, hwirq); - if (old_virq) - irq_domain_disassociate(domain, old_virq); - if (type & IRQ_TYPE_EDGE_BOTH) handler = handle_edge_irq; else if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))