gpio: tegra186: Force one interrupt per bank
authorThierry Reding <treding@nvidia.com>
Fri, 17 Sep 2021 10:54:11 +0000 (12:54 +0200)
committerBartosz Golaszewski <brgl@bgdev.pl>
Wed, 22 Sep 2021 09:26:04 +0000 (11:26 +0200)
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>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
drivers/gpio/gpio-tegra186.c

index c99858f..1bc4152 100644 (file)
@@ -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_banks; 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)