Commit 91a29af4 authored by Marc Zyngier's avatar Marc Zyngier

gpio: Remove dynamic allocation from populate_parent_alloc_arg()

The gpiolib is unique in the way it uses intermediate fwspecs
when feeding an interrupt specifier to the parent domain, as it
relies on the populate_parent_alloc_arg() callback to perform
a dynamic allocation.

This is pretty inefficient (we free the structure almost immediately),
and the only reason this isn't a stack allocation is that our
ThunderX friend uses MSIs rather than a FW-constructed structure.

Let's solve it by providing a new type composed of the union
of a struct irq_fwspec and a msi_info_t, which satisfies both
requirements. This allows us to use a stack allocation, and we
can move the handful of users to this new scheme.

Also perform some additional cleanup, such as getting rid of the
stub versions of the irq_domain_translate_*cell helpers, which
are never used when CONFIG_IRQ_DOMAIN_HIERARCHY isn't selected.

Tested on a Tegra186.
Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Cc: Daniel Palmer <daniel@thingy.jp>
Cc: Romain Perier <romain.perier@gmail.com>
Cc: Bartosz Golaszewski <brgl@bgdev.pl>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Jonathan Hunter <jonathanh@nvidia.com>
Cc: Robert Richter <rric@kernel.org>
Cc: Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
Cc: Andy Gross <agross@kernel.org>
Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Acked-by: default avatarBartosz Golaszewski <brgl@bgdev.pl>
Link: https://lore.kernel.org/r/20220707182314.66610-2-prabhakar.mahadev-lad.rj@bp.renesas.com
parent a111daf0
...@@ -550,15 +550,12 @@ static struct irq_chip msc313_gpio_irqchip = { ...@@ -550,15 +550,12 @@ static struct irq_chip msc313_gpio_irqchip = {
* so we need to provide the fwspec. Essentially gpiochip_populate_parent_fwspec_twocell * so we need to provide the fwspec. Essentially gpiochip_populate_parent_fwspec_twocell
* that puts GIC_SPI into the first cell. * that puts GIC_SPI into the first cell.
*/ */
static void *msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc, static int msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct irq_fwspec *fwspec; struct irq_fwspec *fwspec = &gfwspec->fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = gc->irq.parent_domain->fwnode; fwspec->fwnode = gc->irq.parent_domain->fwnode;
fwspec->param_count = 3; fwspec->param_count = 3;
...@@ -566,7 +563,7 @@ static void *msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc, ...@@ -566,7 +563,7 @@ static void *msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc,
fwspec->param[1] = parent_hwirq; fwspec->param[1] = parent_hwirq;
fwspec->param[2] = parent_type; fwspec->param[2] = parent_type;
return fwspec; return 0;
} }
static int msc313e_gpio_child_to_parent_hwirq(struct gpio_chip *chip, static int msc313e_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
......
...@@ -443,15 +443,12 @@ static int tegra_gpio_child_to_parent_hwirq(struct gpio_chip *chip, ...@@ -443,15 +443,12 @@ static int tegra_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
return 0; return 0;
} }
static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip, static int tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct irq_fwspec *fwspec; struct irq_fwspec *fwspec = &gfwspec->fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 3; fwspec->param_count = 3;
...@@ -459,7 +456,7 @@ static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip, ...@@ -459,7 +456,7 @@ static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip,
fwspec->param[1] = parent_hwirq; fwspec->param[1] = parent_hwirq;
fwspec->param[2] = parent_type; fwspec->param[2] = parent_type;
return fwspec; return 0;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
......
...@@ -621,16 +621,13 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain, ...@@ -621,16 +621,13 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain,
return 0; return 0;
} }
static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, static int tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct tegra_gpio *gpio = gpiochip_get_data(chip); struct tegra_gpio *gpio = gpiochip_get_data(chip);
struct irq_fwspec *fwspec; struct irq_fwspec *fwspec = &gfwspec->fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 3; fwspec->param_count = 3;
...@@ -638,7 +635,7 @@ static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, ...@@ -638,7 +635,7 @@ static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
fwspec->param[1] = parent_hwirq; fwspec->param[1] = parent_hwirq;
fwspec->param[2] = parent_type; fwspec->param[2] = parent_type;
return fwspec; return 0;
} }
static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip, static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
......
...@@ -408,18 +408,15 @@ static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, ...@@ -408,18 +408,15 @@ static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
return 0; return 0;
} }
static void *thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip, static int thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
msi_alloc_info_t *info; msi_alloc_info_t *info = &gfwspec->msiinfo;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return NULL;
info->hwirq = parent_hwirq; info->hwirq = parent_hwirq;
return info; return 0;
} }
static int thunderx_gpio_probe(struct pci_dev *pdev, static int thunderx_gpio_probe(struct pci_dev *pdev,
......
...@@ -103,15 +103,12 @@ static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc, ...@@ -103,15 +103,12 @@ static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
return -EINVAL; return -EINVAL;
} }
static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip, static int visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct irq_fwspec *fwspec; struct irq_fwspec *fwspec = &gfwspec->fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 3; fwspec->param_count = 3;
...@@ -119,7 +116,7 @@ static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip, ...@@ -119,7 +116,7 @@ static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
fwspec->param[1] = parent_hwirq; fwspec->param[1] = parent_hwirq;
fwspec->param[2] = parent_type; fwspec->param[2] = parent_type;
return fwspec; return 0;
} }
static int visconti_gpio_probe(struct platform_device *pdev) static int visconti_gpio_probe(struct platform_device *pdev)
......
...@@ -1107,7 +1107,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, ...@@ -1107,7 +1107,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
irq_hw_number_t hwirq; irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE; unsigned int type = IRQ_TYPE_NONE;
struct irq_fwspec *fwspec = data; struct irq_fwspec *fwspec = data;
void *parent_arg; union gpio_irq_fwspec gpio_parent_fwspec = {};
unsigned int parent_hwirq; unsigned int parent_hwirq;
unsigned int parent_type; unsigned int parent_type;
struct gpio_irq_chip *girq = &gc->irq; struct gpio_irq_chip *girq = &gc->irq;
...@@ -1147,14 +1147,15 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, ...@@ -1147,14 +1147,15 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
irq_set_probe(irq); irq_set_probe(irq);
/* This parent only handles asserted level IRQs */ /* This parent only handles asserted level IRQs */
parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq, parent_type); ret = girq->populate_parent_alloc_arg(gc, &gpio_parent_fwspec,
if (!parent_arg) parent_hwirq, parent_type);
return -ENOMEM; if (ret)
return ret;
chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
irq, parent_hwirq); irq, parent_hwirq);
irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key); irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key);
ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg); ret = irq_domain_alloc_irqs_parent(d, irq, 1, &gpio_parent_fwspec);
/* /*
* If the parent irqdomain is msi, the interrupts have already * If the parent irqdomain is msi, the interrupts have already
* been allocated, so the EEXIST is good. * been allocated, so the EEXIST is good.
...@@ -1166,7 +1167,6 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, ...@@ -1166,7 +1167,6 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
"failed to allocate parent hwirq %d for hwirq %lu\n", "failed to allocate parent hwirq %d for hwirq %lu\n",
parent_hwirq, hwirq); parent_hwirq, hwirq);
kfree(parent_arg);
return ret; return ret;
} }
...@@ -1230,34 +1230,28 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) ...@@ -1230,34 +1230,28 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
return !!gc->irq.parent_domain; return !!gc->irq.parent_domain;
} }
void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, int gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct irq_fwspec *fwspec; struct irq_fwspec *fwspec = &gfwspec->fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = gc->irq.parent_domain->fwnode; fwspec->fwnode = gc->irq.parent_domain->fwnode;
fwspec->param_count = 2; fwspec->param_count = 2;
fwspec->param[0] = parent_hwirq; fwspec->param[0] = parent_hwirq;
fwspec->param[1] = parent_type; fwspec->param[1] = parent_type;
return fwspec; return 0;
} }
EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell); EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell);
void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, int gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct irq_fwspec *fwspec; struct irq_fwspec *fwspec = &gfwspec->fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = gc->irq.parent_domain->fwnode; fwspec->fwnode = gc->irq.parent_domain->fwnode;
fwspec->param_count = 4; fwspec->param_count = 4;
...@@ -1266,7 +1260,7 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, ...@@ -1266,7 +1260,7 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
fwspec->param[2] = 0; fwspec->param[2] = 0;
fwspec->param[3] = parent_type; fwspec->param[3] = parent_type;
return fwspec; return 0;
} }
EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell); EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell);
......
...@@ -966,16 +966,13 @@ static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip, ...@@ -966,16 +966,13 @@ static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
return 0; return 0;
} }
static void *pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip, static int pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct pmic_gpio_state *state = gpiochip_get_data(chip); struct pmic_gpio_state *state = gpiochip_get_data(chip);
struct irq_fwspec *fwspec; struct irq_fwspec *fwspec = &gfwspec->fwspec;
fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->fwnode = chip->irq.parent_domain->fwnode;
...@@ -985,7 +982,7 @@ static void *pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip, ...@@ -985,7 +982,7 @@ static void *pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip,
/* param[2] must be left as 0 */ /* param[2] must be left as 0 */
fwspec->param[3] = parent_type; fwspec->param[3] = parent_type;
return fwspec; return 0;
} }
static int pmic_gpio_probe(struct platform_device *pdev) static int pmic_gpio_probe(struct platform_device *pdev)
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include <linux/property.h> #include <linux/property.h>
#include <linux/types.h> #include <linux/types.h>
#include <asm/msi.h>
struct gpio_desc; struct gpio_desc;
struct of_phandle_args; struct of_phandle_args;
struct device_node; struct device_node;
...@@ -23,6 +25,13 @@ enum gpio_lookup_flags; ...@@ -23,6 +25,13 @@ enum gpio_lookup_flags;
struct gpio_chip; struct gpio_chip;
union gpio_irq_fwspec {
struct irq_fwspec fwspec;
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
msi_alloc_info_t msiinfo;
#endif
};
#define GPIO_LINE_DIRECTION_IN 1 #define GPIO_LINE_DIRECTION_IN 1
#define GPIO_LINE_DIRECTION_OUT 0 #define GPIO_LINE_DIRECTION_OUT 0
...@@ -103,7 +112,8 @@ struct gpio_irq_chip { ...@@ -103,7 +112,8 @@ struct gpio_irq_chip {
* variant named &gpiochip_populate_parent_fwspec_fourcell is also * variant named &gpiochip_populate_parent_fwspec_fourcell is also
* available. * available.
*/ */
void *(*populate_parent_alloc_arg)(struct gpio_chip *gc, int (*populate_parent_alloc_arg)(struct gpio_chip *gc,
union gpio_irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type); unsigned int parent_type);
...@@ -646,29 +656,15 @@ struct bgpio_pdata { ...@@ -646,29 +656,15 @@ struct bgpio_pdata {
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, int gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type); unsigned int parent_type);
void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc, int gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
union gpio_irq_fwspec *gfwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type); unsigned int parent_type);
#else
static inline void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
unsigned int parent_hwirq,
unsigned int parent_type)
{
return NULL;
}
static inline void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
unsigned int parent_hwirq,
unsigned int parent_type)
{
return NULL;
}
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ #endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
int bgpio_init(struct gpio_chip *gc, struct device *dev, int bgpio_init(struct gpio_chip *gc, struct device *dev,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment