in sunxi/pinctrl-sunxi.c [1396:1587]
int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
const struct sunxi_pinctrl_desc *desc,
unsigned long variant)
{
struct device_node *node = pdev->dev.of_node;
struct pinctrl_desc *pctrl_desc;
struct pinctrl_pin_desc *pins;
struct sunxi_pinctrl *pctl;
struct pinmux_ops *pmxops;
int i, ret, last_pin, pin_idx;
struct clk *clk;
pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
if (!pctl)
return -ENOMEM;
platform_set_drvdata(pdev, pctl);
raw_spin_lock_init(&pctl->lock);
pctl->membase = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pctl->membase))
return PTR_ERR(pctl->membase);
pctl->dev = &pdev->dev;
pctl->desc = desc;
pctl->variant = variant;
pctl->irq_array = devm_kcalloc(&pdev->dev,
IRQ_PER_BANK * pctl->desc->irq_banks,
sizeof(*pctl->irq_array),
GFP_KERNEL);
if (!pctl->irq_array)
return -ENOMEM;
ret = sunxi_pinctrl_build_state(pdev);
if (ret) {
dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
return ret;
}
pins = devm_kcalloc(&pdev->dev,
pctl->desc->npins, sizeof(*pins),
GFP_KERNEL);
if (!pins)
return -ENOMEM;
for (i = 0, pin_idx = 0; i < pctl->desc->npins; i++) {
const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
if (pin->variant && !(pctl->variant & pin->variant))
continue;
pins[pin_idx++] = pin->pin;
}
pctrl_desc = devm_kzalloc(&pdev->dev,
sizeof(*pctrl_desc),
GFP_KERNEL);
if (!pctrl_desc)
return -ENOMEM;
pctrl_desc->name = dev_name(&pdev->dev);
pctrl_desc->owner = THIS_MODULE;
pctrl_desc->pins = pins;
pctrl_desc->npins = pctl->ngroups;
pctrl_desc->confops = &sunxi_pconf_ops;
pctrl_desc->pctlops = &sunxi_pctrl_ops;
pmxops = devm_kmemdup(&pdev->dev, &sunxi_pmx_ops, sizeof(sunxi_pmx_ops),
GFP_KERNEL);
if (!pmxops)
return -ENOMEM;
if (desc->disable_strict_mode)
pmxops->strict = false;
pctrl_desc->pmxops = pmxops;
pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, pctl);
if (IS_ERR(pctl->pctl_dev)) {
dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
return PTR_ERR(pctl->pctl_dev);
}
pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL);
if (!pctl->chip)
return -ENOMEM;
last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;
pctl->chip->owner = THIS_MODULE;
pctl->chip->request = gpiochip_generic_request;
pctl->chip->free = gpiochip_generic_free;
pctl->chip->set_config = gpiochip_generic_config;
pctl->chip->direction_input = sunxi_pinctrl_gpio_direction_input;
pctl->chip->direction_output = sunxi_pinctrl_gpio_direction_output;
pctl->chip->get = sunxi_pinctrl_gpio_get;
pctl->chip->set = sunxi_pinctrl_gpio_set;
pctl->chip->of_xlate = sunxi_pinctrl_gpio_of_xlate;
pctl->chip->to_irq = sunxi_pinctrl_gpio_to_irq;
pctl->chip->of_gpio_n_cells = 3;
pctl->chip->can_sleep = false;
pctl->chip->ngpio = round_up(last_pin, PINS_PER_BANK) -
pctl->desc->pin_base;
pctl->chip->label = dev_name(&pdev->dev);
pctl->chip->parent = &pdev->dev;
pctl->chip->base = pctl->desc->pin_base;
ret = gpiochip_add_data(pctl->chip, pctl);
if (ret)
return ret;
for (i = 0; i < pctl->desc->npins; i++) {
const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev),
pin->pin.number - pctl->desc->pin_base,
pin->pin.number, 1);
if (ret)
goto gpiochip_error;
}
ret = of_clk_get_parent_count(node);
clk = devm_clk_get(&pdev->dev, ret == 1 ? NULL : "apb");
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto gpiochip_error;
}
ret = clk_prepare_enable(clk);
if (ret)
goto gpiochip_error;
pctl->irq = devm_kcalloc(&pdev->dev,
pctl->desc->irq_banks,
sizeof(*pctl->irq),
GFP_KERNEL);
if (!pctl->irq) {
ret = -ENOMEM;
goto clk_error;
}
for (i = 0; i < pctl->desc->irq_banks; i++) {
pctl->irq[i] = platform_get_irq(pdev, i);
if (pctl->irq[i] < 0) {
ret = pctl->irq[i];
goto clk_error;
}
}
pctl->domain = irq_domain_add_linear(node,
pctl->desc->irq_banks * IRQ_PER_BANK,
&sunxi_pinctrl_irq_domain_ops,
pctl);
if (!pctl->domain) {
dev_err(&pdev->dev, "Couldn't register IRQ domain\n");
ret = -ENOMEM;
goto clk_error;
}
for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) {
int irqno = irq_create_mapping(pctl->domain, i);
irq_set_chip_and_handler(irqno, &sunxi_pinctrl_edge_irq_chip,
handle_edge_irq);
irq_set_chip_data(irqno, pctl);
}
for (i = 0; i < pctl->desc->irq_banks; i++) {
/* Mask and clear all IRQs before registering a handler */
writel(0, pctl->membase +
sunxi_irq_ctrl_reg_from_bank(pctl->desc, i));
writel(0xffffffff,
pctl->membase +
sunxi_irq_status_reg_from_bank(pctl->desc, i));
irq_set_chained_handler_and_data(pctl->irq[i],
sunxi_pinctrl_irq_handler,
pctl);
}
sunxi_pinctrl_setup_debounce(pctl, node);
dev_info(&pdev->dev, "initialized sunXi PIO driver\n");
return 0;
clk_error:
clk_disable_unprepare(clk);
gpiochip_error:
gpiochip_remove(pctl->chip);
return ret;
}