Signed-off-by: Lina Iyer <ilina@xxxxxxxxxxxxxx> --- drivers/pinctrl/qcom/pinctrl-msm.c | 125 ++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 7c7d083e2c0d..3857aa5539e0 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -17,6 +17,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinctrl.h> @@ -735,6 +736,9 @@ static void msm_gpio_irq_mask(struct irq_data *d) clear_bit(d->hwirq, pctrl->enabled_irqs); raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + if (d->parent_data) + irq_chip_mask_parent(d); } static void msm_gpio_irq_unmask(struct irq_data *d) @@ -757,6 +761,9 @@ static void msm_gpio_irq_unmask(struct irq_data *d) set_bit(d->hwirq, pctrl->enabled_irqs); raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + if (d->parent_data) + irq_chip_unmask_parent(d); } static void msm_gpio_irq_ack(struct irq_data *d) @@ -875,6 +882,9 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) irq_set_handler_locked(d, handle_edge_irq); + if (d->parent_data) + irq_chip_set_type_parent(d, type); + return 0; } @@ -890,6 +900,9 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) raw_spin_unlock_irqrestore(&pctrl->lock, flags); + if (d->parent_data) + irq_chip_set_wake_parent(d, on); + return 0; } @@ -967,11 +980,103 @@ static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl) return device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0) > 0; } +static int get_parent_hwirq(struct msm_pinctrl *pctrl, unsigned int hwirq) +{ + int i, n, ret; + int gpio, pdc = -EINVAL; + struct device_node *np = pctrl->dev->of_node; + + n = of_property_count_elems_of_size(np, "wake-irq", sizeof(u32)); + if (n <= 0 || n % 2) + return -EINVAL; + + for (i = 0; i < n / 2; i++) { + ret = of_property_read_u32_index(np, "wake-irq", 2 * i, &gpio); + if (ret) + return ret; + if (gpio != hwirq) + continue; + ret = of_property_read_u32_index(np, "wake-irq", 2 * i + 1, + &pdc); + if (ret) + return ret; + } + + return pdc; +} + +static int msm_gpio_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, unsigned int *type) +{ + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count < 2) + return -EINVAL; + *hwirq = fwspec->param[0]; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + return 0; + } + + return -EINVAL; +} + +static int msm_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + int ret = 0; + irq_hw_number_t hwirq; + int parent_hwirq; + struct gpio_chip *gc = domain->host_data; + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct irq_fwspec parent_fwspec, *fwspec = arg; + unsigned int type; + + ret = msm_gpio_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + parent_hwirq = get_parent_hwirq(pctrl, hwirq); + if (parent_hwirq < 0) + return 0; + + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + &pctrl->irq_chip, gc); + if (ret < 0) + return ret; + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = parent_hwirq; + parent_fwspec.param[1] = type; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct irq_fwspec fwspec; + + fwspec.fwnode = of_node_to_fwnode(chip->of_node); + fwspec.param[0] = offset; + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; + fwspec.param_count = 2; + + return irq_create_fwspec_mapping(&fwspec); +} + +static const struct irq_domain_ops msm_gpiod_ops = { + .translate = msm_gpio_domain_translate, + .alloc = msm_gpio_domain_alloc, + .free = irq_domain_free_irqs_top, +}; + static int msm_gpio_init(struct msm_pinctrl *pctrl) { struct gpio_chip *chip; int ret; unsigned ngpio = pctrl->soc->ngpios; + struct device_node *dn; if (WARN_ON(ngpio > MAX_NR_GPIO)) return -EINVAL; @@ -1015,9 +1120,19 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) dev_name(pctrl->dev), 0, 0, chip->ngpio); if (ret) { dev_err(pctrl->dev, "Failed to add pin range\n"); - gpiochip_remove(&pctrl->chip); - return ret; + goto fail; + } + } + + dn = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0); + if (dn) { + chip->irq.parent_domain = irq_find_host(dn); + of_node_put(dn); + if (!chip->irq.parent_domain) { + ret = -EPROBE_DEFER; + goto fail; } + chip->to_irq = msm_gpio_to_irq; } ret = gpiochip_irqchip_add(chip, @@ -1027,14 +1142,16 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) IRQ_TYPE_NONE); if (ret) { dev_err(pctrl->dev, "Failed to add irqchip to gpiochip\n"); - gpiochip_remove(&pctrl->chip); - return -ENOSYS; + goto fail; } gpiochip_set_chained_irqchip(chip, &pctrl->irq_chip, pctrl->irq, msm_gpio_irq_handler); return 0; +fail: + gpiochip_remove(&pctrl->chip); + return ret; } static int msm_ps_hold_restart(struct notifier_block *nb, unsigned long action, -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project