Message ID | 20210903101512.32430-2-pshete@nvidia.com |
---|---|
State | Changes Requested |
Headers | show |
Series | gpio: tegra: add multiple interrupt support | expand |
On Fri, Sep 03, 2021 at 03:45:11PM +0530, Prathamesh Shete wrote: > From: pshete <pshete@nvidia.com> > > T19x GPIO controller's support multiple interrupts. The GPIO > controller is capable to route 8 interrupts per controller in > case of NON-AON GPIO's and 4 interrupts per controller in AON GPIO. > This is new feature starting T194 > The interrupt route map determines which interrupt line is to be used. > > Signed-off-by: Prathamesh Shete <pshete@nvidia.com> > --- > drivers/gpio/gpio-tegra186.c | 25 +++++++++++++++++++++---- > 1 file changed, 21 insertions(+), 4 deletions(-) > > diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c > index d38980b9923a..36bd8de6d401 100644 > --- a/drivers/gpio/gpio-tegra186.c > +++ b/drivers/gpio/gpio-tegra186.c > @@ -1,6 +1,6 @@ > // SPDX-License-Identifier: GPL-2.0-only > /* > - * Copyright (c) 2016-2017 NVIDIA Corporation > + * Copyright (c) 2016-2021 NVIDIA Corporation > * > * Author: Thierry Reding <treding@nvidia.com> > */ > @@ -68,6 +68,7 @@ struct tegra_gpio_soc { > unsigned int num_ports; > const char *name; > unsigned int instance; > + bool multi_ints; Do we really have to add this? Can we not simply derive it from the number of interrupts actually read from device tree? Doing so would also make it easier to keep the code backwards-compatible. Remember that this code must not fail if fed with an old device tree where not 8 interrupts have been specified per controller. > > const struct tegra186_pin_range *pin_ranges; > unsigned int num_pin_ranges; > @@ -451,6 +452,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) > struct irq_chip *chip = irq_desc_get_chip(desc); > unsigned int parent = irq_desc_get_irq(desc); > unsigned int i, offset = 0; > + int j, flag; j can be unsigned in, so you can put it after i in the line above. Also, maybe name the flag variable to something more specific to make it clear what it's used for. > > chained_irq_enter(chip, desc); > > @@ -462,9 +464,20 @@ static void tegra186_gpio_irq(struct irq_desc *desc) > > base = gpio->base + port->bank * 0x1000 + port->port * 0x200; > > - /* skip ports that are not associated with this bank */ > - if (parent != gpio->irq[port->bank]) > - goto skip; > + if (!gpio->soc->multi_ints) { > + /* skip ports that are not associated with this bank */ > + if (parent != gpio->irq[port->bank]) > + goto skip; > + > + } else { > + flag = 0; > + for (j = 0; j < 8; j++) { > + if (parent != gpio->irq[(port->bank * 8) + j]) > + flag++; > + } > + if (!(flag & 0xF)) > + goto skip; > + } > > value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); > > @@ -772,6 +785,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = { > .ports = tegra186_main_ports, > .name = "tegra186-gpio", > .instance = 0, > + .multi_ints = false, > }; > > #define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ > @@ -798,6 +812,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { > .ports = tegra186_aon_ports, > .name = "tegra186-gpio-aon", > .instance = 1, > + .multi_ints = false, > }; > > #define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ > @@ -852,6 +867,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = { > .num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges), > .pin_ranges = tegra194_main_pin_ranges, > .pinmux = "nvidia,tegra194-pinmux", > + .multi_ints = true, > }; > > #define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ > @@ -875,6 +891,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { > .ports = tegra194_aon_ports, > .name = "tegra194-gpio-aon", > .instance = 1, > + .multi_ints = true, > }; > > static const struct of_device_id tegra186_gpio_of_match[] = { Going over this patch reminded me that I had written a similar patch a while ago, which does things a bit differently. I've attached both patches below. Please take a look. It's slightly bigger that your version above, but it addresses the backwards-compatibility issue. It also has a couple of comments that describe why the interrupt routing is done the way it is. For completeness I should say that I'm not sure if I've ever tested the second patch because I had it marked "WIP", which I usually do if there is work I know remains to be done and since there's no TODO comments or anything in the code, I assume that I never tested it completely. Looking at the history of the branch where I have that patch, I don't see changes to the device tree files, so I probably never got around to adding the multiple interrupts per bank and hence couldn't test it properly. I can do that based on your second patch, but it'd be great if you could go over the attached patches and let me know what you think. Thierry From f205c14353aa1c3bf05c79710abd1d7a6d4105de Mon Sep 17 00:00:00 2001 From: Thierry Reding <treding@nvidia.com> Date: Mon, 18 May 2020 14:49:25 +0200 Subject: [PATCH 1/2] gpio: tegra186: Force one interrupt per bank Newer chips support up to 8 interrupts per bank, which can be useful to balance the load and decrease latency. However, it also required a very complicated interrupt routing to be set up. To keep things simple for now, ensure that a single interrupt per bank is enforced, even if all possible interrupts are described in device tree. Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/gpio/gpio-tegra186.c | 68 ++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index c99858f40a27..7cb0222ecee8 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -81,6 +81,8 @@ struct tegra_gpio { unsigned int *irq; const struct tegra_gpio_soc *soc; + unsigned int num_irqs_per_bank; + unsigned int num_banks; void __iomem *secure; void __iomem *base; @@ -594,6 +596,28 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) } } +static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +{ + struct device *dev = gpio->gpio.parent; + + if (gpio->num_irq > gpio->num_banks) { + if (gpio->num_irq % gpio->num_banks != 0) + goto error; + } + + if (gpio->num_irq < gpio->num_banks) + goto error; + + gpio->num_irqs_per_bank = gpio->num_irq / gpio->num_banks; + + return 0; + +error: + dev_err(dev, "invalid number of interrupts (%u) for %u banks\n", + gpio->num_irq, gpio->num_banks); + return -EINVAL; +} + static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; @@ -608,7 +632,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; gpio->soc = device_get_match_data(&pdev->dev); + gpio->gpio.label = gpio->soc->name; + gpio->gpio.parent = &pdev->dev; + + /* count the number of banks in the controller */ + for (i = 0; i < gpio->soc->num_ports; i++) + if (gpio->soc->ports[i].bank > gpio->num_banks) + gpio->num_banks = gpio->soc->ports[i].bank; + + gpio->num_banks++; + /* get register apertures */ gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); if (IS_ERR(gpio->secure)) { gpio->secure = devm_platform_ioremap_resource(pdev, 0); @@ -629,6 +663,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_irq = err; + err = tegra186_gpio_irqs_per_bank(gpio); + if (err < 0) + return err; + gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); if (!gpio->irq) @@ -642,9 +680,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->irq[i] = err; } - gpio->gpio.label = gpio->soc->name; - gpio->gpio.parent = &pdev->dev; - gpio->gpio.request = gpiochip_generic_request; gpio->gpio.free = gpiochip_generic_free; gpio->gpio.get_direction = tegra186_gpio_get_direction; @@ -708,7 +743,30 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler = tegra186_gpio_irq; irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; - irq->parents = gpio->irq; + + /* + * To simplify things, use a single interrupt per bank for now. Some + * chips support up to 8 interrupts per bank, which can be useful to + * distribute the load and decrease the processing latency for GPIOs + * but it also requires a more complicated interrupt routing than we + * currently program. + */ + if (gpio->num_irqs_per_bank > 1) { + irq->parents = devm_kcalloc(&pdev->dev, gpio->num_banks, + sizeof(*irq->parents), GFP_KERNEL); + if (!irq->parents) + return -ENOMEM; + + for (i = 0; i < gpio->num_irq; i++) + irq->parents[i] = gpio->irq[i * gpio->num_irqs_per_bank]; + + irq->num_parents = gpio->num_banks; + } else { + irq->num_parents = gpio->num_irq; + irq->parents = gpio->irq; + } + + tegra186_gpio_init_route_mapping(gpio); np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (np) { @@ -719,8 +777,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - tegra186_gpio_init_route_mapping(gpio); - irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio, sizeof(*irq->map), GFP_KERNEL); if (!irq->map)
Answers to review comments inlined. -----Original Message----- From: Thierry Reding <thierry.reding@gmail.com> Sent: Monday, September 6, 2021 10:21 AM To: Prathamesh Shete <pshete@nvidia.com> Cc: linus.walleij@linaro.org; bgolaszewski@baylibre.com; Jonathan Hunter <jonathanh@nvidia.com>; linux-gpio@vger.kernel.org; linux-tegra@vger.kernel.org; linux-kernel@vger.kernel.org; Suresh Mangipudi <smangipudi@nvidia.com> Subject: Re: [PATCH v2 1/2] gpio: tegra: add multiple interrupt support On Fri, Sep 03, 2021 at 03:45:11PM +0530, Prathamesh Shete wrote: > From: pshete <pshete@nvidia.com> > > T19x GPIO controller's support multiple interrupts. The GPIO > controller is capable to route 8 interrupts per controller in case of > NON-AON GPIO's and 4 interrupts per controller in AON GPIO. > This is new feature starting T194 > The interrupt route map determines which interrupt line is to be used. > > Signed-off-by: Prathamesh Shete <pshete@nvidia.com> > --- > drivers/gpio/gpio-tegra186.c | 25 +++++++++++++++++++++---- > 1 file changed, 21 insertions(+), 4 deletions(-) > > diff --git a/drivers/gpio/gpio-tegra186.c > b/drivers/gpio/gpio-tegra186.c index d38980b9923a..36bd8de6d401 100644 > --- a/drivers/gpio/gpio-tegra186.c > +++ b/drivers/gpio/gpio-tegra186.c > @@ -1,6 +1,6 @@ > // SPDX-License-Identifier: GPL-2.0-only > /* > - * Copyright (c) 2016-2017 NVIDIA Corporation > + * Copyright (c) 2016-2021 NVIDIA Corporation > * > * Author: Thierry Reding <treding@nvidia.com> > */ > @@ -68,6 +68,7 @@ struct tegra_gpio_soc { > unsigned int num_ports; > const char *name; > unsigned int instance; > + bool multi_ints; Do we really have to add this? Can we not simply derive it from the number of interrupts actually read from device tree? Doing so would also make it easier to keep the code backwards-compatible. Remember that this code must not fail if fed with an old device tree where not 8 interrupts have been specified per controller. [PS]: True, we can derive from DT but adding variable in soc data which is static is more easier and effective to maintain as well. Yes I have verified the change on older DT blobs as well and it is working as expected. > > const struct tegra186_pin_range *pin_ranges; > unsigned int num_pin_ranges; > @@ -451,6 +452,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) > struct irq_chip *chip = irq_desc_get_chip(desc); > unsigned int parent = irq_desc_get_irq(desc); > unsigned int i, offset = 0; > + int j, flag; j can be unsigned in, so you can put it after i in the line above. Also, maybe name the flag variable to something more specific to make it clear what it's used for. [PS]: Addressed this in version v3. > > chained_irq_enter(chip, desc); > > @@ -462,9 +464,20 @@ static void tegra186_gpio_irq(struct irq_desc > *desc) > > base = gpio->base + port->bank * 0x1000 + port->port * 0x200; > > - /* skip ports that are not associated with this bank */ > - if (parent != gpio->irq[port->bank]) > - goto skip; > + if (!gpio->soc->multi_ints) { > + /* skip ports that are not associated with this bank */ > + if (parent != gpio->irq[port->bank]) > + goto skip; > + > + } else { > + flag = 0; > + for (j = 0; j < 8; j++) { > + if (parent != gpio->irq[(port->bank * 8) + j]) > + flag++; > + } > + if (!(flag & 0xF)) > + goto skip; > + } > > value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); > > @@ -772,6 +785,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = { > .ports = tegra186_main_ports, > .name = "tegra186-gpio", > .instance = 0, > + .multi_ints = false, > }; > > #define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ > @@ -798,6 +812,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { > .ports = tegra186_aon_ports, > .name = "tegra186-gpio-aon", > .instance = 1, > + .multi_ints = false, > }; > > #define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ > @@ -852,6 +867,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = { > .num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges), > .pin_ranges = tegra194_main_pin_ranges, > .pinmux = "nvidia,tegra194-pinmux", > + .multi_ints = true, > }; > > #define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ > @@ -875,6 +891,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { > .ports = tegra194_aon_ports, > .name = "tegra194-gpio-aon", > .instance = 1, > + .multi_ints = true, > }; > > static const struct of_device_id tegra186_gpio_of_match[] = { Going over this patch reminded me that I had written a similar patch a while ago, which does things a bit differently. I've attached both patches below. Please take a look. It's slightly bigger that your version above, but it addresses the backwards-compatibility issue. It also has a couple of comments that describe why the interrupt routing is done the way it is. For completeness I should say that I'm not sure if I've ever tested the second patch because I had it marked "WIP", which I usually do if there is work I know remains to be done and since there's no TODO comments or anything in the code, I assume that I never tested it completely. Looking at the history of the branch where I have that patch, I don't see changes to the device tree files, so I probably never got around to adding the multiple interrupts per bank and hence couldn't test it properly. I can do that based on your second patch, but it'd be great if you could go over the attached patches and let me know what you think. [PS]: I think this change is much shorter and simpler as it does not add much code and hence reduce complexity. The change you are suggesting is lengthier and also I have not looked into your patch in more detailed level. Thierry
From: pshete <pshete@nvidia.com>
These patches adds multiple interrupt support.
Each main GPIO is associated with 8 interrupts
per controller in case of NON-AON GPIO's and
4 interrupts per controller in AON GPIO.
This is new feature starting T194
The interrupt route map determines which interrupt line is to be used.
pshete (2):
gpio: tegra: add multiple interrupt support
arm64: tegra: GPIO Interrupt entries
arch/arm64/boot/dts/nvidia/tegra194.dtsi | 49 +++++++++++++++++++++++-
drivers/gpio/gpio-tegra186.c | 25 ++++++++++--
2 files changed, 68 insertions(+), 6 deletions(-)
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index d38980b9923a..36bd8de6d401 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2017 NVIDIA Corporation + * Copyright (c) 2016-2021 NVIDIA Corporation * * Author: Thierry Reding <treding@nvidia.com> */ @@ -68,6 +68,7 @@ struct tegra_gpio_soc { unsigned int num_ports; const char *name; unsigned int instance; + bool multi_ints; const struct tegra186_pin_range *pin_ranges; unsigned int num_pin_ranges; @@ -451,6 +452,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int parent = irq_desc_get_irq(desc); unsigned int i, offset = 0; + int j, flag; chained_irq_enter(chip, desc); @@ -462,9 +464,20 @@ static void tegra186_gpio_irq(struct irq_desc *desc) base = gpio->base + port->bank * 0x1000 + port->port * 0x200; - /* skip ports that are not associated with this bank */ - if (parent != gpio->irq[port->bank]) - goto skip; + if (!gpio->soc->multi_ints) { + /* skip ports that are not associated with this bank */ + if (parent != gpio->irq[port->bank]) + goto skip; + + } else { + flag = 0; + for (j = 0; j < 8; j++) { + if (parent != gpio->irq[(port->bank * 8) + j]) + flag++; + } + if (!(flag & 0xF)) + goto skip; + } value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); @@ -772,6 +785,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .ports = tegra186_main_ports, .name = "tegra186-gpio", .instance = 0, + .multi_ints = false, }; #define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -798,6 +812,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { .ports = tegra186_aon_ports, .name = "tegra186-gpio-aon", .instance = 1, + .multi_ints = false, }; #define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -852,6 +867,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = { .num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges), .pin_ranges = tegra194_main_pin_ranges, .pinmux = "nvidia,tegra194-pinmux", + .multi_ints = true, }; #define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -875,6 +891,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .ports = tegra194_aon_ports, .name = "tegra194-gpio-aon", .instance = 1, + .multi_ints = true, }; static const struct of_device_id tegra186_gpio_of_match[] = {