[PATCH v2 5/5] gpio: tegra186: Use valid mask instead of sparse number space

From: Thierry Reding
Date: Thu Nov 29 2018 - 12:03:29 EST


From: Thierry Reding <treding@xxxxxxxxxx>

While the sparse number space has the advantage of only providing (and
therefore allocating) the GPIOs (and interrupts) that really exist for
the given SoC, it also requires special cases in various callbacks.

Using the valid masks for GPIOs and interrupts enables reusing more of
the code provided by gpiolib at the expense of allocating more GPIOs
and interrupts than necessary.

Signed-off-by: Thierry Reding <treding@xxxxxxxxxx>
---
drivers/gpio/gpio-tegra186.c | 171 ++++++++++++-----------------------
1 file changed, 59 insertions(+), 112 deletions(-)

diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index 1215e08ffe02..69bcc057db26 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -70,35 +70,12 @@ struct tegra_gpio {
void __iomem *base;
};

-static const struct tegra_gpio_port *
-tegra186_gpio_get_port(struct tegra_gpio *gpio, unsigned int *pin)
-{
- unsigned int start = 0, i;
-
- for (i = 0; i < gpio->soc->num_ports; i++) {
- const struct tegra_gpio_port *port = &gpio->soc->ports[i];
-
- if (*pin >= start && *pin < start + port->pins) {
- *pin -= start;
- return port;
- }
-
- start += port->pins;
- }
-
- return NULL;
-}
-
static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio,
unsigned int pin)
{
- const struct tegra_gpio_port *port;
-
- port = tegra186_gpio_get_port(gpio, &pin);
- if (!port)
- return NULL;
+ const struct tegra_gpio_port *port = &gpio->soc->ports[pin / 8];

- return gpio->base + port->offset + pin * 0x20;
+ return gpio->base + port->offset + (pin % 8) * 0x20;
}

static int tegra186_gpio_get_direction(struct gpio_chip *chip,
@@ -208,68 +185,6 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset,
writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE);
}

-static int tegra186_gpio_of_xlate(struct gpio_chip *chip,
- const struct of_phandle_args *spec,
- u32 *flags)
-{
- struct tegra_gpio *gpio = gpiochip_get_data(chip);
- unsigned int port, pin, i, offset = 0;
-
- if (WARN_ON(chip->of_gpio_n_cells < 2))
- return -EINVAL;
-
- if (WARN_ON(spec->args_count < chip->of_gpio_n_cells))
- return -EINVAL;
-
- port = spec->args[0] / 8;
- pin = spec->args[0] % 8;
-
- if (port >= gpio->soc->num_ports) {
- dev_err(chip->parent, "invalid port number: %u\n", port);
- return -EINVAL;
- }
-
- for (i = 0; i < port; i++)
- offset += gpio->soc->ports[i].pins;
-
- if (flags)
- *flags = spec->args[1];
-
- return offset + pin;
-}
-
-static int tegra186_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
-{
- struct tegra_gpio *gpio = gpiochip_get_data(chip);
- struct irq_domain *domain = chip->irq.domain;
-
- if (!gpiochip_irqchip_irq_valid(chip, offset))
- return -ENXIO;
-
- if (irq_domain_is_hierarchy(domain)) {
- struct irq_fwspec spec;
- unsigned int i;
-
- for (i = 0; i < gpio->soc->num_ports; i++) {
- if (offset < gpio->soc->ports[i].pins)
- break;
-
- offset -= gpio->soc->ports[i].pins;
- }
-
- offset += i * 8;
-
- spec.fwnode = domain->fwnode;
- spec.param_count = 2;
- spec.param[0] = offset;
- spec.param[1] = 0;
-
- return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &spec);
- }
-
- return irq_create_mapping(domain, offset);
-}
-
static void tegra186_irq_ack(struct irq_data *data)
{
struct tegra_gpio *gpio = irq_data_get_irq_chip_data(data);
@@ -372,7 +287,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc)
struct irq_domain *domain = gpio->gpio.irq.domain;
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int parent = irq_desc_get_irq(desc);
- unsigned int i, offset = 0;
+ unsigned int i;

chained_irq_enter(chip, desc);

@@ -384,20 +299,17 @@ static void tegra186_gpio_irq(struct irq_desc *desc)

/* skip ports that are not associated with this controller */
if (parent != gpio->irq[port->irq])
- goto skip;
+ continue;

value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1));

for_each_set_bit(pin, &value, port->pins) {
- irq = irq_find_mapping(domain, offset + pin);
+ irq = irq_find_mapping(domain, i * 8 + pin);
if (WARN_ON(irq == 0))
continue;

generic_handle_irq(irq);
}
-
-skip:
- offset += port->pins;
}

chained_irq_exit(chip, desc);
@@ -409,7 +321,7 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain,
unsigned int *type)
{
struct tegra_gpio *gpio = gpiochip_get_data(domain->host_data);
- unsigned int port, pin, i, offset = 0;
+ unsigned int port, pin;

if (WARN_ON(gpio->gpio.of_gpio_n_cells < 2))
return -EINVAL;
@@ -423,11 +335,8 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain,
if (port >= gpio->soc->num_ports)
return -EINVAL;

- for (i = 0; i < port; i++)
- offset += gpio->soc->ports[i].pins;
-
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
- *hwirq = offset + pin;
+ *hwirq = port * 8 + pin;

return 0;
}
@@ -466,6 +375,43 @@ static const struct irq_domain_ops tegra186_gpio_irq_domain_ops = {
.unmap = gpiochip_irq_unmap,
};

+static void tegra186_gpio_set_valid_mask(struct tegra_gpio *gpio,
+ unsigned long *mask)
+{
+ unsigned int i;
+
+ bitmap_zero(mask, gpio->gpio.ngpio);
+
+ for (i = 0; i < gpio->soc->num_ports; i++) {
+ const struct tegra_gpio_port *port = &gpio->soc->ports[i];
+
+ bitmap_set(mask, i * 8, port->pins);
+ }
+}
+
+static int tegra186_gpio_init_valid_mask(struct gpio_chip *chip)
+{
+ struct tegra_gpio *gpio = gpiochip_get_data(chip);
+
+ tegra186_gpio_set_valid_mask(gpio, chip->valid_mask);
+
+ return 0;
+}
+
+static unsigned long *tegra186_gpio_setup_valid_mask(struct tegra_gpio *gpio)
+{
+ unsigned long *mask;
+
+ mask = devm_kcalloc(gpio->gpio.parent, BITS_TO_LONGS(gpio->gpio.ngpio),
+ sizeof(*mask), GFP_KERNEL);
+ if (!mask)
+ return ERR_PTR(-ENOMEM);
+
+ tegra186_gpio_set_valid_mask(gpio, mask);
+
+ return mask;
+}
+
static const struct of_device_id tegra186_pmc_of_match[] = {
{ .compatible = "nvidia,tegra186-pmc" },
{ .compatible = "nvidia,tegra194-pmc" },
@@ -474,11 +420,11 @@ static const struct of_device_id tegra186_pmc_of_match[] = {

static int tegra186_gpio_probe(struct platform_device *pdev)
{
- unsigned int i, j, offset;
struct gpio_irq_chip *irq;
struct tegra_gpio *gpio;
struct device_node *np;
struct resource *res;
+ unsigned int i, j;
char **names;
int err;

@@ -521,17 +467,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
gpio->gpio.get = tegra186_gpio_get,
gpio->gpio.set = tegra186_gpio_set;

+ gpio->gpio.ngpio = gpio->soc->num_ports * 8;
gpio->gpio.base = -1;

- for (i = 0; i < gpio->soc->num_ports; i++)
- gpio->gpio.ngpio += gpio->soc->ports[i].pins;
-
names = devm_kcalloc(gpio->gpio.parent, gpio->gpio.ngpio,
sizeof(*names), GFP_KERNEL);
if (!names)
return -ENOMEM;

- for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) {
+ for (i = 0; i < gpio->soc->num_ports; i++) {
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
char *name;

@@ -541,18 +485,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
if (!name)
return -ENOMEM;

- names[offset + j] = name;
+ names[i * 8 + j] = name;
}
-
- offset += port->pins;
}

gpio->gpio.names = (const char * const *)names;

gpio->gpio.of_node = pdev->dev.of_node;
gpio->gpio.of_gpio_n_cells = 2;
- gpio->gpio.of_xlate = tegra186_gpio_of_xlate;
- gpio->gpio.to_irq = tegra186_gpio_to_irq;
+
+ gpio->gpio.init_valid_mask = tegra186_gpio_init_valid_mask;
+ gpio->gpio.need_valid_mask = true;

gpio->intc.name = pdev->dev.of_node->name;
gpio->intc.irq_ack = tegra186_irq_ack;
@@ -580,18 +523,22 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}

+ irq->valid_mask = tegra186_gpio_setup_valid_mask(gpio);
+ if (IS_ERR(irq->valid_mask))
+ return PTR_ERR(irq->valid_mask);
+
+ irq->need_valid_mask = true;
+
irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio,
sizeof(*irq->map), GFP_KERNEL);
if (!irq->map)
return -ENOMEM;

- for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) {
+ for (i = 0; i < gpio->soc->num_ports; i++) {
const struct tegra_gpio_port *port = &gpio->soc->ports[i];

for (j = 0; j < port->pins; j++)
- irq->map[offset + j] = irq->parents[port->irq];
-
- offset += port->pins;
+ irq->map[i * 8 + j] = irq->parents[port->irq];
}

platform_set_drvdata(pdev, gpio);
--
2.19.1