in gpio-tegra186.c [652:826]
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;
char **names;
int err;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
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);
if (IS_ERR(gpio->secure))
return PTR_ERR(gpio->secure);
}
gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio");
if (IS_ERR(gpio->base)) {
gpio->base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(gpio->base))
return PTR_ERR(gpio->base);
}
err = platform_irq_count(pdev);
if (err < 0)
return err;
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)
return -ENOMEM;
for (i = 0; i < gpio->num_irq; i++) {
err = platform_get_irq(pdev, i);
if (err < 0)
return err;
gpio->irq[i] = err;
}
gpio->gpio.request = gpiochip_generic_request;
gpio->gpio.free = gpiochip_generic_free;
gpio->gpio.get_direction = tegra186_gpio_get_direction;
gpio->gpio.direction_input = tegra186_gpio_direction_input;
gpio->gpio.direction_output = tegra186_gpio_direction_output;
gpio->gpio.get = tegra186_gpio_get;
gpio->gpio.set = tegra186_gpio_set;
gpio->gpio.set_config = tegra186_gpio_set_config;
gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges;
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++) {
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
char *name;
for (j = 0; j < port->pins; j++) {
name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL,
"P%s.%02x", port->name, j);
if (!name)
return -ENOMEM;
names[offset + j] = name;
}
offset += port->pins;
}
gpio->gpio.names = (const char * const *)names;
#if defined(CONFIG_OF_GPIO)
gpio->gpio.of_gpio_n_cells = 2;
gpio->gpio.of_xlate = tegra186_gpio_of_xlate;
#endif /* CONFIG_OF_GPIO */
gpio->intc.name = dev_name(&pdev->dev);
gpio->intc.irq_ack = tegra186_irq_ack;
gpio->intc.irq_mask = tegra186_irq_mask;
gpio->intc.irq_unmask = tegra186_irq_unmask;
gpio->intc.irq_set_type = tegra186_irq_set_type;
gpio->intc.irq_set_wake = tegra186_irq_set_wake;
irq = &gpio->gpio.irq;
irq->chip = &gpio->intc;
irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq;
irq->populate_parent_alloc_arg = tegra186_gpio_populate_parent_fwspec;
irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq;
irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate;
irq->handler = handle_simple_irq;
irq->default_type = IRQ_TYPE_NONE;
irq->parent_handler = tegra186_gpio_irq;
irq->parent_handler_data = gpio;
irq->num_parents = gpio->num_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;
}
if (gpio->soc->num_irqs_per_bank > 1)
tegra186_gpio_init_route_mapping(gpio);
np = of_find_matching_node(NULL, tegra186_pmc_of_match);
if (np) {
irq->parent_domain = irq_find_host(np);
of_node_put(np);
if (!irq->parent_domain)
return -EPROBE_DEFER;
}
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++) {
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
for (j = 0; j < port->pins; j++)
irq->map[offset + j] = irq->parents[port->bank];
offset += port->pins;
}
return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio);
}