diff options
Diffstat (limited to 'drivers/gpio/gpio-grgpio.c')
| -rw-r--r-- | drivers/gpio/gpio-grgpio.c | 209 |
1 files changed, 91 insertions, 118 deletions
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 45b8d6a02b87..e4fa84e22726 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for Aeroflex Gaisler GRGPIO General Purpose I/O cores. * @@ -12,28 +13,25 @@ * See "Documentation/devicetree/bindings/gpio/gpio-grgpio.txt" for * information on open firmware properties. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * * Contributors: Andreas Larsson <andreas@gaisler.com> */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/of_platform.h> -#include <linux/gpio/driver.h> -#include <linux/slab.h> +#include <linux/bitops.h> #include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/generic.h> +#include <linux/init.h> #include <linux/interrupt.h> +#include <linux/io.h> #include <linux/irq.h> #include <linux/irqdomain.h> -#include <linux/bitops.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string_choices.h> #define GRGPIO_MAX_NGPIO 32 @@ -48,7 +46,7 @@ /* Structure for an irq of the core - called an underlying irq */ struct grgpio_uirq { - u8 refcnt; /* Reference counter to manage requesting/freeing of uirq */ + atomic_t refcnt; /* Reference counter to manage requesting/freeing of uirq */ u8 uirq; /* Underlying irq of the gpio driver */ }; @@ -62,7 +60,7 @@ struct grgpio_lirq { }; struct grgpio_priv { - struct gpio_chip gc; + struct gpio_generic_chip chip; void __iomem *regs; struct device *dev; @@ -94,13 +92,12 @@ struct grgpio_priv { static void grgpio_set_imask(struct grgpio_priv *priv, unsigned int offset, int val) { - struct gpio_chip *gc = &priv->gc; - if (val) priv->imask |= BIT(offset); else priv->imask &= ~BIT(offset); - gc->write_reg(priv->regs + GRGPIO_IMASK, priv->imask); + + gpio_generic_write_reg(&priv->chip, priv->regs + GRGPIO_IMASK, priv->imask); } static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset) @@ -121,7 +118,6 @@ static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset) static int grgpio_irq_set_type(struct irq_data *d, unsigned int type) { struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); - unsigned long flags; u32 mask = BIT(d->hwirq); u32 ipol; u32 iedge; @@ -149,15 +145,13 @@ static int grgpio_irq_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); + guard(gpio_generic_lock_irqsave)(&priv->chip); - ipol = priv->gc.read_reg(priv->regs + GRGPIO_IPOL) & ~mask; - iedge = priv->gc.read_reg(priv->regs + GRGPIO_IEDGE) & ~mask; + ipol = gpio_generic_read_reg(&priv->chip, priv->regs + GRGPIO_IPOL) & ~mask; + iedge = gpio_generic_read_reg(&priv->chip, priv->regs + GRGPIO_IEDGE) & ~mask; - priv->gc.write_reg(priv->regs + GRGPIO_IPOL, ipol | pol); - priv->gc.write_reg(priv->regs + GRGPIO_IEDGE, iedge | edge); - - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); + gpio_generic_write_reg(&priv->chip, priv->regs + GRGPIO_IPOL, ipol | pol); + gpio_generic_write_reg(&priv->chip, priv->regs + GRGPIO_IEDGE, iedge | edge); return 0; } @@ -166,44 +160,42 @@ static void grgpio_irq_mask(struct irq_data *d) { struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); int offset = d->hwirq; - unsigned long flags; - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); + scoped_guard(gpio_generic_lock_irqsave, &priv->chip) + grgpio_set_imask(priv, offset, 0); - grgpio_set_imask(priv, offset, 0); - - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); + gpiochip_disable_irq(&priv->chip.gc, d->hwirq); } static void grgpio_irq_unmask(struct irq_data *d) { struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); int offset = d->hwirq; - unsigned long flags; - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); + gpiochip_enable_irq(&priv->chip.gc, d->hwirq); - grgpio_set_imask(priv, offset, 1); + guard(gpio_generic_lock_irqsave)(&priv->chip); - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); + grgpio_set_imask(priv, offset, 1); } -static struct irq_chip grgpio_irq_chip = { +static const struct irq_chip grgpio_irq_chip = { .name = "grgpio", .irq_mask = grgpio_irq_mask, .irq_unmask = grgpio_irq_unmask, .irq_set_type = grgpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static irqreturn_t grgpio_irq_handler(int irq, void *dev) { struct grgpio_priv *priv = dev; - int ngpio = priv->gc.ngpio; - unsigned long flags; + int ngpio = priv->chip.gc.ngpio; int i; int match = 0; - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); + guard(gpio_generic_lock_irqsave)(&priv->chip); /* * For each gpio line, call its interrupt handler if it its underlying @@ -219,8 +211,6 @@ static irqreturn_t grgpio_irq_handler(int irq, void *dev) } } - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); - if (!match) dev_warn(priv->dev, "No gpio line matched irq %d\n", irq); @@ -251,27 +241,23 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, dev_dbg(priv->dev, "Mapping irq %d for gpio line %d\n", irq, offset); - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); - - /* Request underlying irq if not already requested */ + gpio_generic_chip_lock_irqsave(&priv->chip, flags); lirq->irq = irq; uirq = &priv->uirqs[lirq->index]; - if (uirq->refcnt == 0) { + gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); + + /* Request underlying irq if not already requested */ + if (atomic_fetch_add(1, &uirq->refcnt) == 0) { ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, dev_name(priv->dev), priv); if (ret) { dev_err(priv->dev, "Could not request underlying irq %d\n", uirq->uirq); - - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); - + atomic_dec(&uirq->refcnt); /* rollback */ return ret; } } - uirq->refcnt++; - - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); /* Setup irq */ irq_set_chip_data(irq, priv); @@ -289,13 +275,13 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) struct grgpio_lirq *lirq; struct grgpio_uirq *uirq; unsigned long flags; - int ngpio = priv->gc.ngpio; + int ngpio = priv->chip.gc.ngpio; int i; irq_set_chip_and_handler(irq, NULL, NULL); irq_set_chip_data(irq, NULL); - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); + gpio_generic_chip_lock_irqsave(&priv->chip, flags); /* Free underlying irq if last user unmapped */ index = -1; @@ -312,12 +298,21 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) if (index >= 0) { uirq = &priv->uirqs[lirq->index]; - uirq->refcnt--; - if (uirq->refcnt == 0) + if (atomic_dec_and_test(&uirq->refcnt)) { + gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); free_irq(uirq->uirq, priv); + return; + } } - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); + gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); +} + +static void grgpio_irq_domain_remove(void *data) +{ + struct irq_domain *domain = data; + + irq_domain_remove(domain); } static const struct irq_domain_ops grgpio_irq_domain_ops = { @@ -330,49 +325,58 @@ static const struct irq_domain_ops grgpio_irq_domain_ops = { static int grgpio_probe(struct platform_device *ofdev) { struct device_node *np = ofdev->dev.of_node; + struct gpio_generic_chip_config config; + struct device *dev = &ofdev->dev; void __iomem *regs; struct gpio_chip *gc; struct grgpio_priv *priv; - struct resource *res; int err; u32 prop; s32 *irqmap; int size; int i; - priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(&ofdev->dev, res); + regs = devm_platform_ioremap_resource(ofdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); - gc = &priv->gc; - err = bgpio_init(gc, &ofdev->dev, 4, regs + GRGPIO_DATA, - regs + GRGPIO_OUTPUT, NULL, regs + GRGPIO_DIR, NULL, - BGPIOF_BIG_ENDIAN_BYTE_ORDER); + config = (struct gpio_generic_chip_config) { + .dev = dev, + .sz = 4, + .dat = regs + GRGPIO_DATA, + .set = regs + GRGPIO_OUTPUT, + .dirout = regs + GRGPIO_DIR, + .flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER, + }; + + gc = &priv->chip.gc; + err = gpio_generic_chip_init(&priv->chip, &config); if (err) { - dev_err(&ofdev->dev, "bgpio_init() failed\n"); + dev_err(dev, "failed to initialize the generic GPIO chip\n"); return err; } priv->regs = regs; - priv->imask = gc->read_reg(regs + GRGPIO_IMASK); - priv->dev = &ofdev->dev; + priv->imask = gpio_generic_read_reg(&priv->chip, regs + GRGPIO_IMASK); + priv->dev = dev; - gc->of_node = np; gc->owner = THIS_MODULE; gc->to_irq = grgpio_to_irq; - gc->label = devm_kasprintf(&ofdev->dev, GFP_KERNEL, "%pOF", np); + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np); + if (!gc->label) + return -ENOMEM; + gc->base = -1; err = of_property_read_u32(np, "nbits", &prop); if (err || prop <= 0 || prop > GRGPIO_MAX_NGPIO) { gc->ngpio = GRGPIO_MAX_NGPIO; - dev_dbg(&ofdev->dev, - "No or invalid nbits property: assume %d\n", gc->ngpio); + dev_dbg(dev, "No or invalid nbits property: assume %d\n", + gc->ngpio); } else { gc->ngpio = prop; } @@ -384,20 +388,24 @@ static int grgpio_probe(struct platform_device *ofdev) irqmap = (s32 *)of_get_property(np, "irqmap", &size); if (irqmap) { if (size < gc->ngpio) { - dev_err(&ofdev->dev, + dev_err(dev, "irqmap shorter than ngpio (%d < %d)\n", size, gc->ngpio); return -EINVAL; } - priv->domain = irq_domain_add_linear(np, gc->ngpio, - &grgpio_irq_domain_ops, - priv); + priv->domain = irq_domain_create_linear(dev_fwnode(&ofdev->dev), gc->ngpio, + &grgpio_irq_domain_ops, priv); if (!priv->domain) { - dev_err(&ofdev->dev, "Could not add irq domain\n"); + dev_err(dev, "Could not add irq domain\n"); return -EINVAL; } + err = devm_add_action_or_reset(dev, grgpio_irq_domain_remove, + priv->domain); + if (err) + return err; + for (i = 0; i < gc->ngpio; i++) { struct grgpio_lirq *lirq; int ret; @@ -414,59 +422,25 @@ static int grgpio_probe(struct platform_device *ofdev) * Continue without irq functionality for that * gpio line */ - dev_err(priv->dev, - "Failed to get irq for offset %d\n", i); continue; } priv->uirqs[lirq->index].uirq = ret; + atomic_set(&priv->uirqs[lirq->index].refcnt, 0); } } - platform_set_drvdata(ofdev, priv); - - err = gpiochip_add_data(gc, priv); + err = devm_gpiochip_add_data(dev, gc, priv); if (err) { - dev_err(&ofdev->dev, "Could not add gpiochip\n"); - if (priv->domain) - irq_domain_remove(priv->domain); + dev_err(dev, "Could not add gpiochip\n"); return err; } - dev_info(&ofdev->dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n", - priv->regs, gc->base, gc->ngpio, priv->domain ? "on" : "off"); + dev_info(dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n", + priv->regs, gc->base, gc->ngpio, str_on_off(priv->domain)); return 0; } -static int grgpio_remove(struct platform_device *ofdev) -{ - struct grgpio_priv *priv = platform_get_drvdata(ofdev); - unsigned long flags; - int i; - int ret = 0; - - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); - - if (priv->domain) { - for (i = 0; i < GRGPIO_MAX_NGPIO; i++) { - if (priv->uirqs[i].refcnt != 0) { - ret = -EBUSY; - goto out; - } - } - } - - gpiochip_remove(&priv->gc); - - if (priv->domain) - irq_domain_remove(priv->domain); - -out: - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); - - return ret; -} - static const struct of_device_id grgpio_match[] = { {.name = "GAISLER_GPIO"}, {.name = "01_01a"}, @@ -481,7 +455,6 @@ static struct platform_driver grgpio_driver = { .of_match_table = grgpio_match, }, .probe = grgpio_probe, - .remove = grgpio_remove, }; module_platform_driver(grgpio_driver); |
