Thread (9 messages) 9 messages, 4 authors, 2019-10-16

Re: [PATCH 2/2] gpio: Add xgs-iproc driver

From: Linus Walleij <hidden>
Date: 2019-10-11 07:43:44
Also in: linux-devicetree, linux-gpio, lkml

Hi Chris!

Thanks for your patch!

On Fri, Oct 4, 2019 at 3:25 AM Chris Packham
[off-list ref] wrote:
This driver supports the Chip Common A GPIO controller present on a
number of Broadcom switch ASICs with integrated SoCs. The controller is
similar to the pinctrl-nsp-gpio and pinctrl-iproc-gpio blocks but
different enough that a separate driver is required.

This has been ported from Broadcom's XLDK 5.0.3 retaining only the CCA
support (pinctrl-iproc-gpio covers CCB).

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
(...)
+config GPIO_XGS_IPROC
+       tristate "BRCM XGS iProc GPIO support"
+       depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST)
+       select GPIO_GENERIC
+       select GPIOLIB_IRQCHIP
Nice reuse of abstractions.
+static u32 iproc_gpio_readl(struct iproc_gpio_chip *chip, int reg)
+{
+       return readl(chip->base + reg);
+}
+
+static void iproc_gpio_writel(struct iproc_gpio_chip *chip, u32 val, int reg)
+{
+       writel(val, chip->base + reg);
+}
These wrappers don't really add anything do they? Just inline the
direct readl()/writel() to base + reg everywhere instead.
+/* returns the corresponding gpio register bit */
+static int iproc_irq_to_gpio(struct iproc_gpio_chip *chip, u32 irq)
+{
+       struct irq_data *data = irq_domain_get_irq_data(chip->irq_domain, irq);
+
+       return data->hwirq;
+}
I would name it something clearer since "gpio" is pretty ambigous.

Like iproc_irq_to_gpio_offset()

Maybe this is also a bit of unnecessary wrapper?
+static irqreturn_t iproc_gpio_irq_handler(int irq, void *data)
+{
+       struct iproc_gpio_chip *chip = (struct iproc_gpio_chip *)data;
+       struct gpio_chip gc = chip->gc;
+       int bit;
+       unsigned long int_bits = 0;
+       u32 int_status;
+
+       /* go through the entire GPIOs and handle all interrupts */
+       int_status = readl(chip->intr + CCA_INT_STS);
+       if (int_status & CCA_INT_F_GPIOINT) {
+               u32 event, level;
+
+               /* Get level and edge interrupts */
+               event = readl(chip->base + GPIO_CCA_INT_EVENT_MASK);
+               event &= readl(chip->base + GPIO_CCA_INT_EVENT);
+               level = readl(chip->base + GPIO_CCA_DIN);
+               level ^= readl(chip->base + GPIO_CCA_INT_LEVEL);
+               level &= readl(chip->base + GPIO_CCA_INT_LEVEL_MASK);
+               int_bits = level | event;
+
+               for_each_set_bit(bit, &int_bits, gc.ngpio)
+                       generic_handle_irq(
+                               irq_linear_revmap(chip->irq_domain, bit));
+       }
+
+       return  int_bits ? IRQ_HANDLED : IRQ_NONE;
+}
I think this should be a chained interrupt handler (see below how to
register it).

See e.g. drivers/gpio/gpio-ftgpio010.c for an example:
change function prototype, no return value, use
chained_irq_enter/exit(irqchip, desc); etc.
+static int iproc_gpiolib_input(struct gpio_chip *gc, u32 gpio)
+static int iproc_gpiolib_output(struct gpio_chip *gc, u32 gpio, int value)
+static void iproc_gpiolib_set(struct gpio_chip *gc, u32 gpio, int value)
+static int iproc_gpiolib_get(struct gpio_chip *gc, u32 gpio)
These callbacks seems to reimplement parts of GPIO_GENERIC
that you should already be using.

Again look at drivers/gpio/gpio-ftgpio010.c() use bgpio_init()
to set up the library callbacks, look in
drivers/gpio/gpio-mmio.c for kerneldoc on the function.
+static int iproc_gpiolib_to_irq(struct gpio_chip *gc, u32 offset)
+{
+       struct iproc_gpio_chip *chip = gpiochip_get_data(gc);
+
+       return irq_linear_revmap(chip->irq_domain, offset);
+}
GPIOLIB_IRQCHIP provides a .to_irq() implementation
so drop this and let the library handle this.
+static int iproc_gpio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *dn = pdev->dev.of_node;
+       struct iproc_gpio_chip *chip;
+       u32 num_gpios;
+       int irq, ret;
+
+       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->dev = dev;
+       platform_set_drvdata(pdev, chip);
+
+       chip->gc.label = dev_name(dev);
+
+       chip->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(chip->base))
+               return PTR_ERR(chip->base);
+
+       /* Get number of GPIO pin */
+       if (of_property_read_u32(dn, "ngpios", &num_gpios)) {
+               dev_err(dev, "missing ngpios DT property\n");
+               return -EINVAL;
+       }
Maybe provide a sensible default?
+       chip->gc.ngpio = num_gpios;
+       chip->gc.parent = dev;
+       chip->gc.of_node = dn;
+       chip->gc.direction_input = iproc_gpiolib_input;
+       chip->gc.direction_output = iproc_gpiolib_output;
+       chip->gc.set = iproc_gpiolib_set;
+       chip->gc.get = iproc_gpiolib_get;
Drop this and call bgpio_init() to set up the callbacks
instead. However, set up .ngpio *after* calling
bgpio_init() so users can't access the nonexisting
gpios.
+       chip->gc.to_irq = iproc_gpiolib_to_irq;
Drop this and use the GPIOLIB_IRQCHIP.
+       ret = gpiochip_add_data(&chip->gc, chip);
+       if (ret) {
+               dev_err(dev, "unable to add GPIO chip\n");
+               return ret;
+       }
Why not use devm_gpiochip_add_data()?
+       irq = platform_get_irq(pdev, 0);
+       if (irq > 0) {
+               u32 val, count;
+               struct irq_chip *irqc;
+
+               irqc = &chip->irqchip;
+               irqc->name = dev_name(dev);
+               irqc->irq_ack = iproc_gpio_irq_ack;
+               irqc->irq_mask = iproc_gpio_irq_mask;
+               irqc->irq_unmask = iproc_gpio_irq_unmask;
+               irqc->irq_set_type = iproc_gpio_irq_set_type;
+               irqc->irq_enable = iproc_gpio_irq_unmask;
+               irqc->irq_disable = iproc_gpio_irq_mask;
+               chip->intr = devm_platform_ioremap_resource(pdev, 1);
+               if (IS_ERR(chip->intr))
+                       return PTR_ERR(chip->intr);
Move all this above the [devm_]gpiochip_add_data()
call and add:

struct gpio_irq_chip *girq;

girq = &chip->gc.irq;
girq->chip = irqc;

etc follow the pattern from other drivers like the
mentioned ftgpio010.c.
+               /* Create irq domain */
+               chip->irq_domain = irq_domain_add_linear(dn, num_gpios,
+                               &irq_domain_simple_ops, chip);
+
+               if (!chip->irq_domain) {
+                       dev_err(dev, "Couldn't allocate IRQ domain\n");
+                       ret = -ENODEV;
+                       goto err_irq_domain;
+               }
+
+               /* Map each gpio pin to an IRQ and set the handler */
+               for (count = 0; count < num_gpios; count++) {
+                       int irq;
+
+                       irq = irq_create_mapping(chip->irq_domain, count);
+                       irq_set_chip_and_handler(irq, irqc, handle_simple_irq);
+                       irq_set_chip_data(irq, chip);
+               }
Drop this and let the generic GPIO irqchip handle
the domain.
+               /* Enable GPIO interrupts for CCA GPIO */
+               val = readl(chip->intr + CCA_INT_MASK);
+               val |= CCA_INT_F_GPIOINT;
+               writel(val, chip->intr + CCA_INT_MASK);
Move this before registering the chip.
+               /* Install ISR for this GPIO controller */
+               ret = devm_request_irq(dev, irq, iproc_gpio_irq_handler,
+                                      IRQF_SHARED, chip->gc.label, chip);
+               if (ret) {
+                       dev_err(dev, "Fail to request IRQ%d: %d\n", irq, ret);
+                       goto err_irq_request;
+               }
Drop this and use the gpiolib irqchip library.

Yours,
Linus Walleij

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help