[PATCH 1/3] gpio: Add APM X-Gene SoC GPIO controller support
Feng Kan
fkan at apm.com
Tue May 20 16:54:29 PDT 2014
> +static void xgene_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
> +{
> + struct xgene_gpio_bank *bank =
> + container_of(gc, struct xgene_gpio_bank, chip);
> + unsigned long flags;
> + u32 data;
> +
> + spin_lock_irqsave(&bank->lock, flags);
> +
> + data = ioread32(bank->set_dr);
> + data = val ? data | GPIO_SET_MASK(gpio) : data & ~GPIO_SET_MASK(gpio);
>Couldn't you use set_bit() / clear_bit() instead here?
Set_bit/clear_bit seems a bit heavy, I can use the non atomic methods
of _set_bit/clear_bit
if you agree? In which case I will rewrite all the other method in
likewise fashion.
>> + bank = &gpio->banks[offs];
>> + bank->gpio = gpio;
>> + spin_lock_init(&bank->lock);
>> +
>> + if (of_property_read_u32(bank_np, "ngpios", &ngpio))
>> + ngpio = 16;
>
> Here I think you want to distinguish between "property not defined"
> (-EINVAL) and "property has no value" (-ENODATA) and other possible
> errors. In the later cases you will want to signal the error and
> return from here.
I kinda messed up here a bit, I am trying to support ACPI booting but other
properties are also affected. I will rewrite this part so minimum of_ access
methods are required.
>
>> +
>> + if ((ngpio > 16) || (ngpio < 1)) {
>> + dev_info(gpio->dev, "Incorrect number of gpio specified: %s\n",
>> + bank_np->full_name);
>> + return -EINVAL;
>> + }
>> +
>> + bank->data = gpio->regs + GPIO_DATA + (bank_idx * GPIO_DATA_OFFSET);
>> + bank->od = gpio->regs + GPIO_OD + (bank_idx * GPIO_OD_OFFSET);
>> + bank->mux = gpio->regs + GPIO_MUX + (bank_idx * GPIO_MUX_OFFSET);
>> + bank->set_dr = gpio->regs + GPIO_SET_DR
>> + + (bank_idx * GPIO_SET_DR_OFFSET);
>> +
>> + bank->chip.direction_input = xgene_gpio_dir_in;
>> + bank->chip.direction_output = xgene_gpio_dir_out;
>> + bank->chip.get = xgene_gpio_get;
>> + bank->chip.set = xgene_gpio_set;
>> +
>> + if (!of_property_read_u32(bank_np, "odcfg", &odcfg))
>> + iowrite32(odcfg, bank->od);
>> +
>> + bank->chip.ngpio = ngpio;
>> + bank->chip.of_node = bank_np;
>> + bank->chip.base = bank_idx * ngpio;
>> +
>> + err = gpiochip_add(&bank->chip);
>> + if (err)
>> + dev_err(gpio->dev, "failed to register gpiochip for %s\n",
>> + bank_np->full_name);
>> +
>> + return err;
>> +}
>> +
>> +static int xgene_gpio_probe(struct platform_device *pdev)
>> +{
>> + struct device_node *np = pdev->dev.of_node;
>> + struct resource *res;
>> + struct xgene_gpio *gpio;
>> + int err;
>> + unsigned int offs = 0;
>> +
>> + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
>> + if (!gpio)
>> + return -ENOMEM;
>> + gpio->dev = &pdev->dev;
>> +
>> + gpio->nr_banks = of_get_child_count(pdev->dev.of_node);
>> + if (!gpio->nr_banks) {
>> + err = -EINVAL;
>> + goto err;
>> + }
>
> Might be worth checking whether nr_banks >= XGENE_GPIO_MAX_PORTS and
> return an error here.
>
>> +
>> + gpio->banks = devm_kzalloc(&pdev->dev, gpio->nr_banks *
>> + sizeof(*gpio->banks), GFP_KERNEL);
>> + if (!gpio->banks) {
>> + err = -ENOMEM;
>> + goto err;
>> + }
>
> I'm fine with printing the error status the way you do, but could you
> also do the same for the first kzalloc too?
>
> Or maybe you could put your banks member at the end of the xgene_gpio
> struct as a flexible array member and perform only one kzalloc of size
> (sizeof(*gpio) + (sizeof(*gpio->banks) * gpio->nr_banks))?
>
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + gpio->regs = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(gpio->regs)) {
>> + err = PTR_ERR(gpio->regs);
>> + goto err;
>> + }
>> +
>> + for_each_child_of_node(pdev->dev.of_node, np) {
>> + err = xgene_gpio_add_bank(gpio, np, offs++);
>> + if (err)
>> + goto err;
>> + }
>> +
>> + platform_set_drvdata(pdev, gpio);
>> +
>> + dev_info(&pdev->dev, "X-Gene GPIO driver registered\n");
>> + return 0;
>> +err:
>> + dev_err(&pdev->dev, "X-Gene GPIO driver registration failed\n");
>> + return err;
>> +}
>> +
>> +static const struct of_device_id xgene_gpio_of_match[] = {
>> + { .compatible = "apm,xgene-gpio", },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, xgene_gpio_of_match);
>> +
>> +static struct platform_driver xgene_gpio_driver = {
>> + .driver = {
>> + .name = "xgene-gpio",
>> + .owner = THIS_MODULE,
>> + .of_match_table = xgene_gpio_of_match,
>> + },
>> + .probe = xgene_gpio_probe,
>> +};
>> +
>> +static int __init xgene_gpio_init(void)
>> +{
>> + return platform_driver_register(&xgene_gpio_driver);
>> +}
>> +postcore_initcall(xgene_gpio_init);
>> +
>> +MODULE_AUTHOR("AppliedMicro");
>> +MODULE_DESCRIPTION("APM X-Gene GPIO driver");
>> +MODULE_LICENSE("GPL");
>> --
>> 1.9.1
>>
>
> Globally the driver looks well-written, but I don't understand the
> need to have one gpio_chip per bank. Could you elaborate on the
> reasons for this design?
I see each bank as separate gpio chip. It is a simple way to
abstract the banks since they can operate independently. It also
provided me a way to fix the sysfs gpio base number, regardless if
a particular bank node is pulled out. This is also done in similar way
in some other gpio drivers such as the dwapb gpio driver.
>
> Alex.
More information about the linux-arm-kernel
mailing list