static int pmic_gpio_probe()

in qcom/pinctrl-spmi-gpio.c [991:1133]


static int pmic_gpio_probe(struct platform_device *pdev)
{
	struct irq_domain *parent_domain;
	struct device_node *parent_node;
	struct device *dev = &pdev->dev;
	struct pinctrl_pin_desc *pindesc;
	struct pinctrl_desc *pctrldesc;
	struct pmic_gpio_pad *pad, *pads;
	struct pmic_gpio_state *state;
	struct gpio_irq_chip *girq;
	const struct spmi_device *parent_spmi_dev;
	int ret, npins, i;
	u32 reg;

	ret = of_property_read_u32(dev->of_node, "reg", &reg);
	if (ret < 0) {
		dev_err(dev, "missing base address");
		return ret;
	}

	npins = (uintptr_t) device_get_match_data(&pdev->dev);

	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
	if (!state)
		return -ENOMEM;

	platform_set_drvdata(pdev, state);

	state->dev = &pdev->dev;
	state->map = dev_get_regmap(dev->parent, NULL);
	parent_spmi_dev = to_spmi_device(dev->parent);
	state->usid = parent_spmi_dev->usid;
	state->pid_base = reg >> 8;

	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
	if (!pindesc)
		return -ENOMEM;

	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
	if (!pads)
		return -ENOMEM;

	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
	if (!pctrldesc)
		return -ENOMEM;

	pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
	pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
	pctrldesc->confops = &pmic_gpio_pinconf_ops;
	pctrldesc->owner = THIS_MODULE;
	pctrldesc->name = dev_name(dev);
	pctrldesc->pins = pindesc;
	pctrldesc->npins = npins;
	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings);
	pctrldesc->custom_params = pmic_gpio_bindings;
#ifdef CONFIG_DEBUG_FS
	pctrldesc->custom_conf_items = pmic_conf_items;
#endif

	for (i = 0; i < npins; i++, pindesc++) {
		pad = &pads[i];
		pindesc->drv_data = pad;
		pindesc->number = i;
		pindesc->name = pmic_gpio_groups[i];

		pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;

		ret = pmic_gpio_populate(state, pad);
		if (ret < 0)
			return ret;
	}

	state->chip = pmic_gpio_gpio_template;
	state->chip.parent = dev;
	state->chip.base = -1;
	state->chip.ngpio = npins;
	state->chip.label = dev_name(dev);
	state->chip.of_gpio_n_cells = 2;
	state->chip.can_sleep = false;

	state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
	if (IS_ERR(state->ctrl))
		return PTR_ERR(state->ctrl);

	parent_node = of_irq_find_parent(state->dev->of_node);
	if (!parent_node)
		return -ENXIO;

	parent_domain = irq_find_host(parent_node);
	of_node_put(parent_node);
	if (!parent_domain)
		return -ENXIO;

	state->irq.name = "spmi-gpio",
	state->irq.irq_ack = irq_chip_ack_parent,
	state->irq.irq_mask = irq_chip_mask_parent,
	state->irq.irq_unmask = irq_chip_unmask_parent,
	state->irq.irq_set_type = irq_chip_set_type_parent,
	state->irq.irq_set_wake = irq_chip_set_wake_parent,
	state->irq.flags = IRQCHIP_MASK_ON_SUSPEND,

	girq = &state->chip.irq;
	girq->chip = &state->irq;
	girq->default_type = IRQ_TYPE_NONE;
	girq->handler = handle_level_irq;
	girq->fwnode = of_node_to_fwnode(state->dev->of_node);
	girq->parent_domain = parent_domain;
	girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq;
	girq->populate_parent_alloc_arg = pmic_gpio_populate_parent_fwspec;
	girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq;
	girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate;

	ret = gpiochip_add_data(&state->chip, state);
	if (ret) {
		dev_err(state->dev, "can't add gpio chip\n");
		return ret;
	}

	/*
	 * For DeviceTree-supported systems, the gpio core checks the
	 * pinctrl's device node for the "gpio-ranges" property.
	 * If it is present, it takes care of adding the pin ranges
	 * for the driver. In this case the driver can skip ahead.
	 *
	 * In order to remain compatible with older, existing DeviceTree
	 * files which don't set the "gpio-ranges" property or systems that
	 * utilize ACPI the driver has to call gpiochip_add_pin_range().
	 */
	if (!of_property_read_bool(dev->of_node, "gpio-ranges")) {
		ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0,
					     npins);
		if (ret) {
			dev_err(dev, "failed to add pin range\n");
			goto err_range;
		}
	}

	return 0;

err_range:
	gpiochip_remove(&state->chip);
	return ret;
}