@@ -552,14 +552,20 @@ static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
unsigned int *parent_hwirq,
unsigned int *parent_type)
{
- *parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq);
+ int ret;
+
+ ret = chip->irq.child_offset_to_irq(chip, hwirq, parent_hwirq);
+ if (ret)
+ return ret;
+
*parent_type = type;
return 0;
}
static unsigned int tegra186_gpio_child_offset_to_irq(struct gpio_chip *chip,
- unsigned int offset)
+ unsigned int offset,
+ unsigned int *hwirq)
{
struct tegra_gpio *gpio = gpiochip_get_data(chip);
unsigned int i;
@@ -571,7 +577,9 @@ static unsigned int tegra186_gpio_child_offset_to_irq(struct gpio_chip *chip,
offset -= gpio->soc->ports[i].pins;
}
- return offset + i * 8;
+ *hwirq = offset + i * 8;
+
+ return 0;
}
static const struct of_device_id tegra186_pmc_of_match[] = {
@@ -1036,6 +1036,7 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc,
unsigned int parent_hwirq;
unsigned int parent_type;
struct gpio_irq_chip *girq = &gc->irq;
+ unsigned int hwirq;
/*
* We call the child to parent translation function
@@ -1053,9 +1054,16 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc,
continue;
}
+ ret = girq->child_offset_to_irq(gc, i, &hwirq);
+ if (ret) {
+ chip_err(gc,
+ "child_offset_to_irq() failed to return hwirq for GPIO line %d: %d\n",
+ i, ret);
+ continue;
+ }
fwspec.fwnode = gc->irq.fwnode;
/* This is the hwirq for the GPIO line side of things */
- fwspec.param[0] = girq->child_offset_to_irq(gc, i);
+ fwspec.param[0] = hwirq;
/* Just pick something */
fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
fwspec.param_count = 2;
@@ -1176,10 +1184,12 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
return ret;
}
-static unsigned int gpiochip_child_offset_to_irq_noop(struct gpio_chip *gc,
- unsigned int offset)
+static int gpiochip_child_offset_to_irq_noop(struct gpio_chip *gc,
+ unsigned int offset,
+ unsigned int *hwirq)
{
- return offset;
+ *hwirq = offset;
+ return 0;
}
static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops)
@@ -1420,10 +1430,15 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset)
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
if (irq_domain_is_hierarchy(domain)) {
struct irq_fwspec spec;
+ unsigned int hwirq;
+ int ret;
+ ret = gc->irq.child_offset_to_irq(gc, offset, &hwirq);
+ if (ret)
+ return ret;
spec.fwnode = domain->fwnode;
spec.param_count = 2;
- spec.param[0] = gc->irq.child_offset_to_irq(gc, offset);
+ spec.param[0] = hwirq;
spec.param[1] = IRQ_TYPE_NONE;
return irq_create_fwspec_mapping(&spec);
@@ -947,9 +947,12 @@ static int pmic_gpio_domain_translate(struct irq_domain *domain,
}
static unsigned int pmic_gpio_child_offset_to_irq(struct gpio_chip *chip,
- unsigned int offset)
+ unsigned int offset,
+ unsigned int *hwirq)
{
- return offset + PMIC_GPIO_PHYSICAL_OFFSET;
+ *hwirq = offset + PMIC_GPIO_PHYSICAL_OFFSET;
+
+ return 0;
}
static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
@@ -804,9 +804,12 @@ static int pmic_mpp_domain_translate(struct irq_domain *domain,
}
static unsigned int pmic_mpp_child_offset_to_irq(struct gpio_chip *chip,
- unsigned int offset)
+ unsigned int offset,
+ unsigned int *hwirq)
{
- return offset + PMIC_MPP_PHYSICAL_OFFSET;
+ *hwirq = offset + PMIC_MPP_PHYSICAL_OFFSET;
+
+ return 0;
}
static int pmic_mpp_child_to_parent_hwirq(struct gpio_chip *chip,
@@ -678,9 +678,12 @@ static int pm8xxx_domain_translate(struct irq_domain *domain,
}
static unsigned int pm8xxx_child_offset_to_irq(struct gpio_chip *chip,
- unsigned int offset)
+ unsigned int offset,
+ unsigned int *hwirq)
{
- return offset + PM8XXX_GPIO_PHYSICAL_OFFSET;
+ *hwirq = offset + PM8XXX_GPIO_PHYSICAL_OFFSET;
+
+ return 0;
}
static int pm8xxx_child_to_parent_hwirq(struct gpio_chip *chip,
@@ -748,9 +748,12 @@ static int pm8xxx_mpp_domain_translate(struct irq_domain *domain,
}
static unsigned int pm8xxx_mpp_child_offset_to_irq(struct gpio_chip *chip,
- unsigned int offset)
+ unsigned int offset,
+ unsigned int *hwirq)
{
- return offset + PM8XXX_MPP_PHYSICAL_OFFSET;
+ *hwirq = offset + PM8XXX_MPP_PHYSICAL_OFFSET;
+
+ return 0;
}
static int pm8821_mpp_child_to_parent_hwirq(struct gpio_chip *chip,
@@ -120,10 +120,13 @@ struct gpio_irq_chip {
* This optional callback is used to translate the child's GPIO line
* offset on the GPIO chip to an IRQ number for the GPIO to_irq()
* callback. If this is not specified, then a default callback will be
- * provided that returns the line offset.
+ * provided that sets hwirq to the line offset.
+ *
+ * If the hardware is not capable of handling anymore IRQs a negative
+ * error code is returned and on success 0 is returned.
*/
- unsigned int (*child_offset_to_irq)(struct gpio_chip *gc,
- unsigned int pin);
+ int (*child_offset_to_irq)(struct gpio_chip *gc, unsigned int pin,
+ unsigned int *hwirq);
/**
* @child_irq_domain_ops:
On Renesas RZ/G2L SoC not all the GPIO pins can be simultaneously used as interrupts. The SoC allows 32 interrupts which is first come first serve basis and is dynamic i.e. if there is a free slot (after rmmod) this can be used by other GPIO pins being used as an interrupt. To handle such cases change child_offset_to_irq() callback to return error codes in case of failure. All the users of child_offset_to_irq() callback are also updated with this API change. Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> --- drivers/gpio/gpio-tegra186.c | 14 ++++++++++--- drivers/gpio/gpiolib.c | 25 +++++++++++++++++++----- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 7 +++++-- drivers/pinctrl/qcom/pinctrl-spmi-mpp.c | 7 +++++-- drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 7 +++++-- drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c | 7 +++++-- include/linux/gpio/driver.h | 9 ++++++--- 7 files changed, 57 insertions(+), 19 deletions(-)