162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Aeroflex Gaisler GRGPIO General Purpose I/O cores. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * 2013 (c) Aeroflex Gaisler AB 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This driver supports the GRGPIO GPIO core available in the GRLIB VHDL 862306a36Sopenharmony_ci * IP core library. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Full documentation of the GRGPIO core can be found here: 1162306a36Sopenharmony_ci * http://www.gaisler.com/products/grlib/grip.pdf 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * See "Documentation/devicetree/bindings/gpio/gpio-grgpio.txt" for 1462306a36Sopenharmony_ci * information on open firmware properties. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Contributors: Andreas Larsson <andreas@gaisler.com> 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/spinlock.h> 2462306a36Sopenharmony_ci#include <linux/io.h> 2562306a36Sopenharmony_ci#include <linux/of.h> 2662306a36Sopenharmony_ci#include <linux/gpio/driver.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/err.h> 2962306a36Sopenharmony_ci#include <linux/interrupt.h> 3062306a36Sopenharmony_ci#include <linux/irq.h> 3162306a36Sopenharmony_ci#include <linux/irqdomain.h> 3262306a36Sopenharmony_ci#include <linux/bitops.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define GRGPIO_MAX_NGPIO 32 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define GRGPIO_DATA 0x00 3762306a36Sopenharmony_ci#define GRGPIO_OUTPUT 0x04 3862306a36Sopenharmony_ci#define GRGPIO_DIR 0x08 3962306a36Sopenharmony_ci#define GRGPIO_IMASK 0x0c 4062306a36Sopenharmony_ci#define GRGPIO_IPOL 0x10 4162306a36Sopenharmony_ci#define GRGPIO_IEDGE 0x14 4262306a36Sopenharmony_ci#define GRGPIO_BYPASS 0x18 4362306a36Sopenharmony_ci#define GRGPIO_IMAP_BASE 0x20 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* Structure for an irq of the core - called an underlying irq */ 4662306a36Sopenharmony_cistruct grgpio_uirq { 4762306a36Sopenharmony_ci u8 refcnt; /* Reference counter to manage requesting/freeing of uirq */ 4862306a36Sopenharmony_ci u8 uirq; /* Underlying irq of the gpio driver */ 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Structure for an irq of a gpio line handed out by this driver. The index is 5362306a36Sopenharmony_ci * used to map to the corresponding underlying irq. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistruct grgpio_lirq { 5662306a36Sopenharmony_ci s8 index; /* Index into struct grgpio_priv's uirqs, or -1 */ 5762306a36Sopenharmony_ci u8 irq; /* irq for the gpio line */ 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct grgpio_priv { 6162306a36Sopenharmony_ci struct gpio_chip gc; 6262306a36Sopenharmony_ci void __iomem *regs; 6362306a36Sopenharmony_ci struct device *dev; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci u32 imask; /* irq mask shadow register */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* 6862306a36Sopenharmony_ci * The grgpio core can have multiple "underlying" irqs. The gpio lines 6962306a36Sopenharmony_ci * can be mapped to any one or none of these underlying irqs 7062306a36Sopenharmony_ci * independently of each other. This driver sets up an irq domain and 7162306a36Sopenharmony_ci * hands out separate irqs to each gpio line 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci struct irq_domain *domain; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * This array contains information on each underlying irq, each 7762306a36Sopenharmony_ci * irq of the grgpio core itself. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci struct grgpio_uirq uirqs[GRGPIO_MAX_NGPIO]; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * This array contains information for each gpio line on the irqs 8362306a36Sopenharmony_ci * obtains from this driver. An index value of -1 for a certain gpio 8462306a36Sopenharmony_ci * line indicates that the line has no irq. Otherwise the index connects 8562306a36Sopenharmony_ci * the irq to the underlying irq by pointing into the uirqs array. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci struct grgpio_lirq lirqs[GRGPIO_MAX_NGPIO]; 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void grgpio_set_imask(struct grgpio_priv *priv, unsigned int offset, 9162306a36Sopenharmony_ci int val) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct gpio_chip *gc = &priv->gc; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (val) 9662306a36Sopenharmony_ci priv->imask |= BIT(offset); 9762306a36Sopenharmony_ci else 9862306a36Sopenharmony_ci priv->imask &= ~BIT(offset); 9962306a36Sopenharmony_ci gc->write_reg(priv->regs + GRGPIO_IMASK, priv->imask); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int grgpio_to_irq(struct gpio_chip *gc, unsigned offset) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct grgpio_priv *priv = gpiochip_get_data(gc); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (offset >= gc->ngpio) 10762306a36Sopenharmony_ci return -ENXIO; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (priv->lirqs[offset].index < 0) 11062306a36Sopenharmony_ci return -ENXIO; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return irq_create_mapping(priv->domain, offset); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* -------------------- IRQ chip functions -------------------- */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int grgpio_irq_set_type(struct irq_data *d, unsigned int type) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); 12062306a36Sopenharmony_ci unsigned long flags; 12162306a36Sopenharmony_ci u32 mask = BIT(d->hwirq); 12262306a36Sopenharmony_ci u32 ipol; 12362306a36Sopenharmony_ci u32 iedge; 12462306a36Sopenharmony_ci u32 pol; 12562306a36Sopenharmony_ci u32 edge; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci switch (type) { 12862306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 12962306a36Sopenharmony_ci pol = 0; 13062306a36Sopenharmony_ci edge = 0; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 13362306a36Sopenharmony_ci pol = mask; 13462306a36Sopenharmony_ci edge = 0; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 13762306a36Sopenharmony_ci pol = 0; 13862306a36Sopenharmony_ci edge = mask; 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 14162306a36Sopenharmony_ci pol = mask; 14262306a36Sopenharmony_ci edge = mask; 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci default: 14562306a36Sopenharmony_ci return -EINVAL; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ipol = priv->gc.read_reg(priv->regs + GRGPIO_IPOL) & ~mask; 15162306a36Sopenharmony_ci iedge = priv->gc.read_reg(priv->regs + GRGPIO_IEDGE) & ~mask; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci priv->gc.write_reg(priv->regs + GRGPIO_IPOL, ipol | pol); 15462306a36Sopenharmony_ci priv->gc.write_reg(priv->regs + GRGPIO_IEDGE, iedge | edge); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void grgpio_irq_mask(struct irq_data *d) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); 16462306a36Sopenharmony_ci int offset = d->hwirq; 16562306a36Sopenharmony_ci unsigned long flags; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci grgpio_set_imask(priv, offset, 0); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void grgpio_irq_unmask(struct irq_data *d) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); 17762306a36Sopenharmony_ci int offset = d->hwirq; 17862306a36Sopenharmony_ci unsigned long flags; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci grgpio_set_imask(priv, offset, 1); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic struct irq_chip grgpio_irq_chip = { 18862306a36Sopenharmony_ci .name = "grgpio", 18962306a36Sopenharmony_ci .irq_mask = grgpio_irq_mask, 19062306a36Sopenharmony_ci .irq_unmask = grgpio_irq_unmask, 19162306a36Sopenharmony_ci .irq_set_type = grgpio_irq_set_type, 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic irqreturn_t grgpio_irq_handler(int irq, void *dev) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct grgpio_priv *priv = dev; 19762306a36Sopenharmony_ci int ngpio = priv->gc.ngpio; 19862306a36Sopenharmony_ci unsigned long flags; 19962306a36Sopenharmony_ci int i; 20062306a36Sopenharmony_ci int match = 0; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * For each gpio line, call its interrupt handler if it its underlying 20662306a36Sopenharmony_ci * irq matches the current irq that is handled. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci for (i = 0; i < ngpio; i++) { 20962306a36Sopenharmony_ci struct grgpio_lirq *lirq = &priv->lirqs[i]; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (priv->imask & BIT(i) && lirq->index >= 0 && 21262306a36Sopenharmony_ci priv->uirqs[lirq->index].uirq == irq) { 21362306a36Sopenharmony_ci generic_handle_irq(lirq->irq); 21462306a36Sopenharmony_ci match = 1; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!match) 22162306a36Sopenharmony_ci dev_warn(priv->dev, "No gpio line matched irq %d\n", irq); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return IRQ_HANDLED; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* 22762306a36Sopenharmony_ci * This function will be called as a consequence of the call to 22862306a36Sopenharmony_ci * irq_create_mapping in grgpio_to_irq 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_cistatic int grgpio_irq_map(struct irq_domain *d, unsigned int irq, 23162306a36Sopenharmony_ci irq_hw_number_t hwirq) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct grgpio_priv *priv = d->host_data; 23462306a36Sopenharmony_ci struct grgpio_lirq *lirq; 23562306a36Sopenharmony_ci struct grgpio_uirq *uirq; 23662306a36Sopenharmony_ci unsigned long flags; 23762306a36Sopenharmony_ci int offset = hwirq; 23862306a36Sopenharmony_ci int ret = 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!priv) 24162306a36Sopenharmony_ci return -EINVAL; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci lirq = &priv->lirqs[offset]; 24462306a36Sopenharmony_ci if (lirq->index < 0) 24562306a36Sopenharmony_ci return -EINVAL; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci dev_dbg(priv->dev, "Mapping irq %d for gpio line %d\n", 24862306a36Sopenharmony_ci irq, offset); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Request underlying irq if not already requested */ 25362306a36Sopenharmony_ci lirq->irq = irq; 25462306a36Sopenharmony_ci uirq = &priv->uirqs[lirq->index]; 25562306a36Sopenharmony_ci if (uirq->refcnt == 0) { 25662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); 25762306a36Sopenharmony_ci ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, 25862306a36Sopenharmony_ci dev_name(priv->dev), priv); 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci dev_err(priv->dev, 26162306a36Sopenharmony_ci "Could not request underlying irq %d\n", 26262306a36Sopenharmony_ci uirq->uirq); 26362306a36Sopenharmony_ci return ret; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci uirq->refcnt++; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Setup irq */ 27262306a36Sopenharmony_ci irq_set_chip_data(irq, priv); 27362306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &grgpio_irq_chip, 27462306a36Sopenharmony_ci handle_simple_irq); 27562306a36Sopenharmony_ci irq_set_noprobe(irq); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct grgpio_priv *priv = d->host_data; 28362306a36Sopenharmony_ci int index; 28462306a36Sopenharmony_ci struct grgpio_lirq *lirq; 28562306a36Sopenharmony_ci struct grgpio_uirq *uirq; 28662306a36Sopenharmony_ci unsigned long flags; 28762306a36Sopenharmony_ci int ngpio = priv->gc.ngpio; 28862306a36Sopenharmony_ci int i; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci irq_set_chip_and_handler(irq, NULL, NULL); 29162306a36Sopenharmony_ci irq_set_chip_data(irq, NULL); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Free underlying irq if last user unmapped */ 29662306a36Sopenharmony_ci index = -1; 29762306a36Sopenharmony_ci for (i = 0; i < ngpio; i++) { 29862306a36Sopenharmony_ci lirq = &priv->lirqs[i]; 29962306a36Sopenharmony_ci if (lirq->irq == irq) { 30062306a36Sopenharmony_ci grgpio_set_imask(priv, i, 0); 30162306a36Sopenharmony_ci lirq->irq = 0; 30262306a36Sopenharmony_ci index = lirq->index; 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci WARN_ON(index < 0); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (index >= 0) { 30962306a36Sopenharmony_ci uirq = &priv->uirqs[lirq->index]; 31062306a36Sopenharmony_ci uirq->refcnt--; 31162306a36Sopenharmony_ci if (uirq->refcnt == 0) { 31262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); 31362306a36Sopenharmony_ci free_irq(uirq->uirq, priv); 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic const struct irq_domain_ops grgpio_irq_domain_ops = { 32262306a36Sopenharmony_ci .map = grgpio_irq_map, 32362306a36Sopenharmony_ci .unmap = grgpio_irq_unmap, 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* ------------------------------------------------------------ */ 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int grgpio_probe(struct platform_device *ofdev) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 33162306a36Sopenharmony_ci void __iomem *regs; 33262306a36Sopenharmony_ci struct gpio_chip *gc; 33362306a36Sopenharmony_ci struct grgpio_priv *priv; 33462306a36Sopenharmony_ci int err; 33562306a36Sopenharmony_ci u32 prop; 33662306a36Sopenharmony_ci s32 *irqmap; 33762306a36Sopenharmony_ci int size; 33862306a36Sopenharmony_ci int i; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL); 34162306a36Sopenharmony_ci if (!priv) 34262306a36Sopenharmony_ci return -ENOMEM; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci regs = devm_platform_ioremap_resource(ofdev, 0); 34562306a36Sopenharmony_ci if (IS_ERR(regs)) 34662306a36Sopenharmony_ci return PTR_ERR(regs); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci gc = &priv->gc; 34962306a36Sopenharmony_ci err = bgpio_init(gc, &ofdev->dev, 4, regs + GRGPIO_DATA, 35062306a36Sopenharmony_ci regs + GRGPIO_OUTPUT, NULL, regs + GRGPIO_DIR, NULL, 35162306a36Sopenharmony_ci BGPIOF_BIG_ENDIAN_BYTE_ORDER); 35262306a36Sopenharmony_ci if (err) { 35362306a36Sopenharmony_ci dev_err(&ofdev->dev, "bgpio_init() failed\n"); 35462306a36Sopenharmony_ci return err; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci priv->regs = regs; 35862306a36Sopenharmony_ci priv->imask = gc->read_reg(regs + GRGPIO_IMASK); 35962306a36Sopenharmony_ci priv->dev = &ofdev->dev; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci gc->owner = THIS_MODULE; 36262306a36Sopenharmony_ci gc->to_irq = grgpio_to_irq; 36362306a36Sopenharmony_ci gc->label = devm_kasprintf(&ofdev->dev, GFP_KERNEL, "%pOF", np); 36462306a36Sopenharmony_ci gc->base = -1; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci err = of_property_read_u32(np, "nbits", &prop); 36762306a36Sopenharmony_ci if (err || prop <= 0 || prop > GRGPIO_MAX_NGPIO) { 36862306a36Sopenharmony_ci gc->ngpio = GRGPIO_MAX_NGPIO; 36962306a36Sopenharmony_ci dev_dbg(&ofdev->dev, 37062306a36Sopenharmony_ci "No or invalid nbits property: assume %d\n", gc->ngpio); 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci gc->ngpio = prop; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * The irqmap contains the index values indicating which underlying irq, 37762306a36Sopenharmony_ci * if anyone, is connected to that line 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci irqmap = (s32 *)of_get_property(np, "irqmap", &size); 38062306a36Sopenharmony_ci if (irqmap) { 38162306a36Sopenharmony_ci if (size < gc->ngpio) { 38262306a36Sopenharmony_ci dev_err(&ofdev->dev, 38362306a36Sopenharmony_ci "irqmap shorter than ngpio (%d < %d)\n", 38462306a36Sopenharmony_ci size, gc->ngpio); 38562306a36Sopenharmony_ci return -EINVAL; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci priv->domain = irq_domain_add_linear(np, gc->ngpio, 38962306a36Sopenharmony_ci &grgpio_irq_domain_ops, 39062306a36Sopenharmony_ci priv); 39162306a36Sopenharmony_ci if (!priv->domain) { 39262306a36Sopenharmony_ci dev_err(&ofdev->dev, "Could not add irq domain\n"); 39362306a36Sopenharmony_ci return -EINVAL; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci for (i = 0; i < gc->ngpio; i++) { 39762306a36Sopenharmony_ci struct grgpio_lirq *lirq; 39862306a36Sopenharmony_ci int ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci lirq = &priv->lirqs[i]; 40162306a36Sopenharmony_ci lirq->index = irqmap[i]; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (lirq->index < 0) 40462306a36Sopenharmony_ci continue; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ret = platform_get_irq(ofdev, lirq->index); 40762306a36Sopenharmony_ci if (ret <= 0) { 40862306a36Sopenharmony_ci /* 40962306a36Sopenharmony_ci * Continue without irq functionality for that 41062306a36Sopenharmony_ci * gpio line 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci continue; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci priv->uirqs[lirq->index].uirq = ret; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci platform_set_drvdata(ofdev, priv); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci err = gpiochip_add_data(gc, priv); 42162306a36Sopenharmony_ci if (err) { 42262306a36Sopenharmony_ci dev_err(&ofdev->dev, "Could not add gpiochip\n"); 42362306a36Sopenharmony_ci if (priv->domain) 42462306a36Sopenharmony_ci irq_domain_remove(priv->domain); 42562306a36Sopenharmony_ci return err; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci dev_info(&ofdev->dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n", 42962306a36Sopenharmony_ci priv->regs, gc->base, gc->ngpio, priv->domain ? "on" : "off"); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int grgpio_remove(struct platform_device *ofdev) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct grgpio_priv *priv = platform_get_drvdata(ofdev); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci gpiochip_remove(&priv->gc); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (priv->domain) 44162306a36Sopenharmony_ci irq_domain_remove(priv->domain); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic const struct of_device_id grgpio_match[] = { 44762306a36Sopenharmony_ci {.name = "GAISLER_GPIO"}, 44862306a36Sopenharmony_ci {.name = "01_01a"}, 44962306a36Sopenharmony_ci {}, 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, grgpio_match); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic struct platform_driver grgpio_driver = { 45562306a36Sopenharmony_ci .driver = { 45662306a36Sopenharmony_ci .name = "grgpio", 45762306a36Sopenharmony_ci .of_match_table = grgpio_match, 45862306a36Sopenharmony_ci }, 45962306a36Sopenharmony_ci .probe = grgpio_probe, 46062306a36Sopenharmony_ci .remove = grgpio_remove, 46162306a36Sopenharmony_ci}; 46262306a36Sopenharmony_cimodule_platform_driver(grgpio_driver); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ciMODULE_AUTHOR("Aeroflex Gaisler AB."); 46562306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Aeroflex Gaisler GRGPIO"); 46662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 467