static int tegra186_gpio_probe()

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);
}