162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (C) 2015-2017 Broadcom 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bitops.h> 562306a36Sopenharmony_ci#include <linux/gpio/driver.h> 662306a36Sopenharmony_ci#include <linux/of.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/irqdomain.h> 962306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cienum gio_reg_index { 1462306a36Sopenharmony_ci GIO_REG_ODEN = 0, 1562306a36Sopenharmony_ci GIO_REG_DATA, 1662306a36Sopenharmony_ci GIO_REG_IODIR, 1762306a36Sopenharmony_ci GIO_REG_EC, 1862306a36Sopenharmony_ci GIO_REG_EI, 1962306a36Sopenharmony_ci GIO_REG_MASK, 2062306a36Sopenharmony_ci GIO_REG_LEVEL, 2162306a36Sopenharmony_ci GIO_REG_STAT, 2262306a36Sopenharmony_ci NUMBER_OF_GIO_REGISTERS 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define GIO_BANK_SIZE (NUMBER_OF_GIO_REGISTERS * sizeof(u32)) 2662306a36Sopenharmony_ci#define GIO_BANK_OFF(bank, off) (((bank) * GIO_BANK_SIZE) + (off * sizeof(u32))) 2762306a36Sopenharmony_ci#define GIO_ODEN(bank) GIO_BANK_OFF(bank, GIO_REG_ODEN) 2862306a36Sopenharmony_ci#define GIO_DATA(bank) GIO_BANK_OFF(bank, GIO_REG_DATA) 2962306a36Sopenharmony_ci#define GIO_IODIR(bank) GIO_BANK_OFF(bank, GIO_REG_IODIR) 3062306a36Sopenharmony_ci#define GIO_EC(bank) GIO_BANK_OFF(bank, GIO_REG_EC) 3162306a36Sopenharmony_ci#define GIO_EI(bank) GIO_BANK_OFF(bank, GIO_REG_EI) 3262306a36Sopenharmony_ci#define GIO_MASK(bank) GIO_BANK_OFF(bank, GIO_REG_MASK) 3362306a36Sopenharmony_ci#define GIO_LEVEL(bank) GIO_BANK_OFF(bank, GIO_REG_LEVEL) 3462306a36Sopenharmony_ci#define GIO_STAT(bank) GIO_BANK_OFF(bank, GIO_REG_STAT) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct brcmstb_gpio_bank { 3762306a36Sopenharmony_ci struct list_head node; 3862306a36Sopenharmony_ci int id; 3962306a36Sopenharmony_ci struct gpio_chip gc; 4062306a36Sopenharmony_ci struct brcmstb_gpio_priv *parent_priv; 4162306a36Sopenharmony_ci u32 width; 4262306a36Sopenharmony_ci u32 wake_active; 4362306a36Sopenharmony_ci u32 saved_regs[GIO_REG_STAT]; /* Don't save and restore GIO_REG_STAT */ 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct brcmstb_gpio_priv { 4762306a36Sopenharmony_ci struct list_head bank_list; 4862306a36Sopenharmony_ci void __iomem *reg_base; 4962306a36Sopenharmony_ci struct platform_device *pdev; 5062306a36Sopenharmony_ci struct irq_domain *irq_domain; 5162306a36Sopenharmony_ci struct irq_chip irq_chip; 5262306a36Sopenharmony_ci int parent_irq; 5362306a36Sopenharmony_ci int gpio_base; 5462306a36Sopenharmony_ci int num_gpios; 5562306a36Sopenharmony_ci int parent_wake_irq; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define MAX_GPIO_PER_BANK 32 5962306a36Sopenharmony_ci#define GPIO_BANK(gpio) ((gpio) >> 5) 6062306a36Sopenharmony_ci/* assumes MAX_GPIO_PER_BANK is a multiple of 2 */ 6162306a36Sopenharmony_ci#define GPIO_BIT(gpio) ((gpio) & (MAX_GPIO_PER_BANK - 1)) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic inline struct brcmstb_gpio_priv * 6462306a36Sopenharmony_cibrcmstb_gpio_gc_to_priv(struct gpio_chip *gc) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 6762306a36Sopenharmony_ci return bank->parent_priv; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic unsigned long 7162306a36Sopenharmony_ci__brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci void __iomem *reg_base = bank->parent_priv->reg_base; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return bank->gc.read_reg(reg_base + GIO_STAT(bank->id)) & 7662306a36Sopenharmony_ci bank->gc.read_reg(reg_base + GIO_MASK(bank->id)); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic unsigned long 8062306a36Sopenharmony_cibrcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci unsigned long status; 8362306a36Sopenharmony_ci unsigned long flags; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->gc.bgpio_lock, flags); 8662306a36Sopenharmony_ci status = __brcmstb_gpio_get_active_irqs(bank); 8762306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return status; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq, 9362306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci return hwirq - (bank->gc.base - bank->parent_priv->gpio_base); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, 9962306a36Sopenharmony_ci unsigned int hwirq, bool enable) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct gpio_chip *gc = &bank->gc; 10262306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 10362306a36Sopenharmony_ci u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank)); 10462306a36Sopenharmony_ci u32 imask; 10562306a36Sopenharmony_ci unsigned long flags; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci raw_spin_lock_irqsave(&gc->bgpio_lock, flags); 10862306a36Sopenharmony_ci imask = gc->read_reg(priv->reg_base + GIO_MASK(bank->id)); 10962306a36Sopenharmony_ci if (enable) 11062306a36Sopenharmony_ci imask |= mask; 11162306a36Sopenharmony_ci else 11262306a36Sopenharmony_ci imask &= ~mask; 11362306a36Sopenharmony_ci gc->write_reg(priv->reg_base + GIO_MASK(bank->id), imask); 11462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); 12062306a36Sopenharmony_ci /* gc_offset is relative to this gpio_chip; want real offset */ 12162306a36Sopenharmony_ci int hwirq = offset + (gc->base - priv->gpio_base); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (hwirq >= priv->num_gpios) 12462306a36Sopenharmony_ci return -ENXIO; 12562306a36Sopenharmony_ci return irq_create_mapping(priv->irq_domain, hwirq); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* -------------------- IRQ chip functions -------------------- */ 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void brcmstb_gpio_irq_mask(struct irq_data *d) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 13362306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci brcmstb_gpio_set_imask(bank, d->hwirq, false); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void brcmstb_gpio_irq_unmask(struct irq_data *d) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 14162306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci brcmstb_gpio_set_imask(bank, d->hwirq, true); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void brcmstb_gpio_irq_ack(struct irq_data *d) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 14962306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 15062306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 15162306a36Sopenharmony_ci u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci gc->write_reg(priv->reg_base + GIO_STAT(bank->id), mask); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 15962306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 16062306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 16162306a36Sopenharmony_ci u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); 16262306a36Sopenharmony_ci u32 edge_insensitive, iedge_insensitive; 16362306a36Sopenharmony_ci u32 edge_config, iedge_config; 16462306a36Sopenharmony_ci u32 level, ilevel; 16562306a36Sopenharmony_ci unsigned long flags; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci switch (type) { 16862306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 16962306a36Sopenharmony_ci level = mask; 17062306a36Sopenharmony_ci edge_config = 0; 17162306a36Sopenharmony_ci edge_insensitive = 0; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 17462306a36Sopenharmony_ci level = mask; 17562306a36Sopenharmony_ci edge_config = mask; 17662306a36Sopenharmony_ci edge_insensitive = 0; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 17962306a36Sopenharmony_ci level = 0; 18062306a36Sopenharmony_ci edge_config = 0; 18162306a36Sopenharmony_ci edge_insensitive = 0; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 18462306a36Sopenharmony_ci level = 0; 18562306a36Sopenharmony_ci edge_config = mask; 18662306a36Sopenharmony_ci edge_insensitive = 0; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 18962306a36Sopenharmony_ci level = 0; 19062306a36Sopenharmony_ci edge_config = 0; /* don't care, but want known value */ 19162306a36Sopenharmony_ci edge_insensitive = mask; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci default: 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->gc.bgpio_lock, flags); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci iedge_config = bank->gc.read_reg(priv->reg_base + 20062306a36Sopenharmony_ci GIO_EC(bank->id)) & ~mask; 20162306a36Sopenharmony_ci iedge_insensitive = bank->gc.read_reg(priv->reg_base + 20262306a36Sopenharmony_ci GIO_EI(bank->id)) & ~mask; 20362306a36Sopenharmony_ci ilevel = bank->gc.read_reg(priv->reg_base + 20462306a36Sopenharmony_ci GIO_LEVEL(bank->id)) & ~mask; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci bank->gc.write_reg(priv->reg_base + GIO_EC(bank->id), 20762306a36Sopenharmony_ci iedge_config | edge_config); 20862306a36Sopenharmony_ci bank->gc.write_reg(priv->reg_base + GIO_EI(bank->id), 20962306a36Sopenharmony_ci iedge_insensitive | edge_insensitive); 21062306a36Sopenharmony_ci bank->gc.write_reg(priv->reg_base + GIO_LEVEL(bank->id), 21162306a36Sopenharmony_ci ilevel | level); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags); 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int brcmstb_gpio_priv_set_wake(struct brcmstb_gpio_priv *priv, 21862306a36Sopenharmony_ci unsigned int enable) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci int ret = 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (enable) 22362306a36Sopenharmony_ci ret = enable_irq_wake(priv->parent_wake_irq); 22462306a36Sopenharmony_ci else 22562306a36Sopenharmony_ci ret = disable_irq_wake(priv->parent_wake_irq); 22662306a36Sopenharmony_ci if (ret) 22762306a36Sopenharmony_ci dev_err(&priv->pdev->dev, "failed to %s wake-up interrupt\n", 22862306a36Sopenharmony_ci enable ? "enable" : "disable"); 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int brcmstb_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 23562306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 23662306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 23762306a36Sopenharmony_ci u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * Do not do anything specific for now, suspend/resume callbacks will 24162306a36Sopenharmony_ci * configure the interrupt mask appropriately 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci if (enable) 24462306a36Sopenharmony_ci bank->wake_active |= mask; 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci bank->wake_active &= ~mask; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return brcmstb_gpio_priv_set_wake(priv, enable); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic irqreturn_t brcmstb_gpio_wake_irq_handler(int irq, void *data) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = data; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!priv || irq != priv->parent_wake_irq) 25662306a36Sopenharmony_ci return IRQ_NONE; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Nothing to do */ 25962306a36Sopenharmony_ci return IRQ_HANDLED; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 26562306a36Sopenharmony_ci struct irq_domain *domain = priv->irq_domain; 26662306a36Sopenharmony_ci int hwbase = bank->gc.base - priv->gpio_base; 26762306a36Sopenharmony_ci unsigned long status; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci while ((status = brcmstb_gpio_get_active_irqs(bank))) { 27062306a36Sopenharmony_ci unsigned int offset; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci for_each_set_bit(offset, &status, 32) { 27362306a36Sopenharmony_ci if (offset >= bank->width) 27462306a36Sopenharmony_ci dev_warn(&priv->pdev->dev, 27562306a36Sopenharmony_ci "IRQ for invalid GPIO (bank=%d, offset=%d)\n", 27662306a36Sopenharmony_ci bank->id, offset); 27762306a36Sopenharmony_ci generic_handle_domain_irq(domain, hwbase + offset); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* Each UPG GIO block has one IRQ for all banks */ 28362306a36Sopenharmony_cistatic void brcmstb_gpio_irq_handler(struct irq_desc *desc) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = irq_desc_get_handler_data(desc); 28662306a36Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 28762306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Interrupts weren't properly cleared during probe */ 29062306a36Sopenharmony_ci BUG_ON(!priv || !chip); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci chained_irq_enter(chip, desc); 29362306a36Sopenharmony_ci list_for_each_entry(bank, &priv->bank_list, node) 29462306a36Sopenharmony_ci brcmstb_gpio_irq_bank_handler(bank); 29562306a36Sopenharmony_ci chained_irq_exit(chip, desc); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct brcmstb_gpio_bank *brcmstb_gpio_hwirq_to_bank( 29962306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv, irq_hw_number_t hwirq) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank; 30262306a36Sopenharmony_ci int i = 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* banks are in descending order */ 30562306a36Sopenharmony_ci list_for_each_entry_reverse(bank, &priv->bank_list, node) { 30662306a36Sopenharmony_ci i += bank->gc.ngpio; 30762306a36Sopenharmony_ci if (hwirq < i) 30862306a36Sopenharmony_ci return bank; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci return NULL; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/* 31462306a36Sopenharmony_ci * This lock class tells lockdep that GPIO irqs are in a different 31562306a36Sopenharmony_ci * category than their parents, so it won't report false recursion. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_cistatic struct lock_class_key brcmstb_gpio_irq_lock_class; 31862306a36Sopenharmony_cistatic struct lock_class_key brcmstb_gpio_irq_request_class; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int brcmstb_gpio_irq_map(struct irq_domain *d, unsigned int irq, 32262306a36Sopenharmony_ci irq_hw_number_t hwirq) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = d->host_data; 32562306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank = 32662306a36Sopenharmony_ci brcmstb_gpio_hwirq_to_bank(priv, hwirq); 32762306a36Sopenharmony_ci struct platform_device *pdev = priv->pdev; 32862306a36Sopenharmony_ci int ret; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!bank) 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n", 33462306a36Sopenharmony_ci irq, (int)hwirq, bank->id); 33562306a36Sopenharmony_ci ret = irq_set_chip_data(irq, &bank->gc); 33662306a36Sopenharmony_ci if (ret < 0) 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci irq_set_lockdep_class(irq, &brcmstb_gpio_irq_lock_class, 33962306a36Sopenharmony_ci &brcmstb_gpio_irq_request_class); 34062306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &priv->irq_chip, handle_level_irq); 34162306a36Sopenharmony_ci irq_set_noprobe(irq); 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void brcmstb_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci irq_set_chip_and_handler(irq, NULL, NULL); 34862306a36Sopenharmony_ci irq_set_chip_data(irq, NULL); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic const struct irq_domain_ops brcmstb_gpio_irq_domain_ops = { 35262306a36Sopenharmony_ci .map = brcmstb_gpio_irq_map, 35362306a36Sopenharmony_ci .unmap = brcmstb_gpio_irq_unmap, 35462306a36Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* Make sure that the number of banks matches up between properties */ 35862306a36Sopenharmony_cistatic int brcmstb_gpio_sanity_check_banks(struct device *dev, 35962306a36Sopenharmony_ci struct device_node *np, struct resource *res) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci int res_num_banks = resource_size(res) / GIO_BANK_SIZE; 36262306a36Sopenharmony_ci int num_banks = 36362306a36Sopenharmony_ci of_property_count_u32_elems(np, "brcm,gpio-bank-widths"); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (res_num_banks != num_banks) { 36662306a36Sopenharmony_ci dev_err(dev, "Mismatch in banks: res had %d, bank-widths had %d\n", 36762306a36Sopenharmony_ci res_num_banks, num_banks); 36862306a36Sopenharmony_ci return -EINVAL; 36962306a36Sopenharmony_ci } else { 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int brcmstb_gpio_remove(struct platform_device *pdev) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev); 37762306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank; 37862306a36Sopenharmony_ci int offset, virq; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (priv->parent_irq > 0) 38162306a36Sopenharmony_ci irq_set_chained_handler_and_data(priv->parent_irq, NULL, NULL); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* Remove all IRQ mappings and delete the domain */ 38462306a36Sopenharmony_ci if (priv->irq_domain) { 38562306a36Sopenharmony_ci for (offset = 0; offset < priv->num_gpios; offset++) { 38662306a36Sopenharmony_ci virq = irq_find_mapping(priv->irq_domain, offset); 38762306a36Sopenharmony_ci irq_dispose_mapping(virq); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci irq_domain_remove(priv->irq_domain); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * You can lose return values below, but we report all errors, and it's 39462306a36Sopenharmony_ci * more important to actually perform all of the steps. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci list_for_each_entry(bank, &priv->bank_list, node) 39762306a36Sopenharmony_ci gpiochip_remove(&bank->gc); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int brcmstb_gpio_of_xlate(struct gpio_chip *gc, 40362306a36Sopenharmony_ci const struct of_phandle_args *gpiospec, u32 *flags) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); 40662306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 40762306a36Sopenharmony_ci int offset; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (gc->of_gpio_n_cells != 2) { 41062306a36Sopenharmony_ci WARN_ON(1); 41162306a36Sopenharmony_ci return -EINVAL; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci offset = gpiospec->args[0] - (gc->base - priv->gpio_base); 41862306a36Sopenharmony_ci if (offset >= gc->ngpio || offset < 0) 41962306a36Sopenharmony_ci return -EINVAL; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (unlikely(offset >= bank->width)) { 42262306a36Sopenharmony_ci dev_warn_ratelimited(&priv->pdev->dev, 42362306a36Sopenharmony_ci "Received request for invalid GPIO offset %d\n", 42462306a36Sopenharmony_ci gpiospec->args[0]); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (flags) 42862306a36Sopenharmony_ci *flags = gpiospec->args[1]; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return offset; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/* priv->parent_irq and priv->num_gpios must be set before calling */ 43462306a36Sopenharmony_cistatic int brcmstb_gpio_irq_setup(struct platform_device *pdev, 43562306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 43862306a36Sopenharmony_ci struct device_node *np = dev->of_node; 43962306a36Sopenharmony_ci int err; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci priv->irq_domain = 44262306a36Sopenharmony_ci irq_domain_add_linear(np, priv->num_gpios, 44362306a36Sopenharmony_ci &brcmstb_gpio_irq_domain_ops, 44462306a36Sopenharmony_ci priv); 44562306a36Sopenharmony_ci if (!priv->irq_domain) { 44662306a36Sopenharmony_ci dev_err(dev, "Couldn't allocate IRQ domain\n"); 44762306a36Sopenharmony_ci return -ENXIO; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (of_property_read_bool(np, "wakeup-source")) { 45162306a36Sopenharmony_ci priv->parent_wake_irq = platform_get_irq(pdev, 1); 45262306a36Sopenharmony_ci if (priv->parent_wake_irq < 0) { 45362306a36Sopenharmony_ci priv->parent_wake_irq = 0; 45462306a36Sopenharmony_ci dev_warn(dev, 45562306a36Sopenharmony_ci "Couldn't get wake IRQ - GPIOs will not be able to wake from sleep"); 45662306a36Sopenharmony_ci } else { 45762306a36Sopenharmony_ci /* 45862306a36Sopenharmony_ci * Set wakeup capability so we can process boot-time 45962306a36Sopenharmony_ci * "wakeups" (e.g., from S5 cold boot) 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci device_set_wakeup_capable(dev, true); 46262306a36Sopenharmony_ci device_wakeup_enable(dev); 46362306a36Sopenharmony_ci err = devm_request_irq(dev, priv->parent_wake_irq, 46462306a36Sopenharmony_ci brcmstb_gpio_wake_irq_handler, 46562306a36Sopenharmony_ci IRQF_SHARED, 46662306a36Sopenharmony_ci "brcmstb-gpio-wake", priv); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (err < 0) { 46962306a36Sopenharmony_ci dev_err(dev, "Couldn't request wake IRQ"); 47062306a36Sopenharmony_ci goto out_free_domain; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci priv->irq_chip.name = dev_name(dev); 47662306a36Sopenharmony_ci priv->irq_chip.irq_disable = brcmstb_gpio_irq_mask; 47762306a36Sopenharmony_ci priv->irq_chip.irq_mask = brcmstb_gpio_irq_mask; 47862306a36Sopenharmony_ci priv->irq_chip.irq_unmask = brcmstb_gpio_irq_unmask; 47962306a36Sopenharmony_ci priv->irq_chip.irq_ack = brcmstb_gpio_irq_ack; 48062306a36Sopenharmony_ci priv->irq_chip.irq_set_type = brcmstb_gpio_irq_set_type; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (priv->parent_wake_irq) 48362306a36Sopenharmony_ci priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci irq_set_chained_handler_and_data(priv->parent_irq, 48662306a36Sopenharmony_ci brcmstb_gpio_irq_handler, priv); 48762306a36Sopenharmony_ci irq_set_status_flags(priv->parent_irq, IRQ_DISABLE_UNLAZY); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ciout_free_domain: 49262306a36Sopenharmony_ci irq_domain_remove(priv->irq_domain); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return err; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void brcmstb_gpio_bank_save(struct brcmstb_gpio_priv *priv, 49862306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct gpio_chip *gc = &bank->gc; 50162306a36Sopenharmony_ci unsigned int i; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci for (i = 0; i < GIO_REG_STAT; i++) 50462306a36Sopenharmony_ci bank->saved_regs[i] = gc->read_reg(priv->reg_base + 50562306a36Sopenharmony_ci GIO_BANK_OFF(bank->id, i)); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic void brcmstb_gpio_quiesce(struct device *dev, bool save) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev); 51162306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank; 51262306a36Sopenharmony_ci struct gpio_chip *gc; 51362306a36Sopenharmony_ci u32 imask; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* disable non-wake interrupt */ 51662306a36Sopenharmony_ci if (priv->parent_irq >= 0) 51762306a36Sopenharmony_ci disable_irq(priv->parent_irq); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci list_for_each_entry(bank, &priv->bank_list, node) { 52062306a36Sopenharmony_ci gc = &bank->gc; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (save) 52362306a36Sopenharmony_ci brcmstb_gpio_bank_save(priv, bank); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Unmask GPIOs which have been flagged as wake-up sources */ 52662306a36Sopenharmony_ci if (priv->parent_wake_irq) 52762306a36Sopenharmony_ci imask = bank->wake_active; 52862306a36Sopenharmony_ci else 52962306a36Sopenharmony_ci imask = 0; 53062306a36Sopenharmony_ci gc->write_reg(priv->reg_base + GIO_MASK(bank->id), 53162306a36Sopenharmony_ci imask); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void brcmstb_gpio_shutdown(struct platform_device *pdev) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci /* Enable GPIO for S5 cold boot */ 53862306a36Sopenharmony_ci brcmstb_gpio_quiesce(&pdev->dev, false); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 54262306a36Sopenharmony_cistatic void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv, 54362306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct gpio_chip *gc = &bank->gc; 54662306a36Sopenharmony_ci unsigned int i; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci for (i = 0; i < GIO_REG_STAT; i++) 54962306a36Sopenharmony_ci gc->write_reg(priv->reg_base + GIO_BANK_OFF(bank->id, i), 55062306a36Sopenharmony_ci bank->saved_regs[i]); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int brcmstb_gpio_suspend(struct device *dev) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci brcmstb_gpio_quiesce(dev, true); 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int brcmstb_gpio_resume(struct device *dev) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev); 56262306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank; 56362306a36Sopenharmony_ci bool need_wakeup_event = false; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci list_for_each_entry(bank, &priv->bank_list, node) { 56662306a36Sopenharmony_ci need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank); 56762306a36Sopenharmony_ci brcmstb_gpio_bank_restore(priv, bank); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (priv->parent_wake_irq && need_wakeup_event) 57162306a36Sopenharmony_ci pm_wakeup_event(dev, 0); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* enable non-wake interrupt */ 57462306a36Sopenharmony_ci if (priv->parent_irq >= 0) 57562306a36Sopenharmony_ci enable_irq(priv->parent_irq); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci#else 58162306a36Sopenharmony_ci#define brcmstb_gpio_suspend NULL 58262306a36Sopenharmony_ci#define brcmstb_gpio_resume NULL 58362306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic const struct dev_pm_ops brcmstb_gpio_pm_ops = { 58662306a36Sopenharmony_ci .suspend_noirq = brcmstb_gpio_suspend, 58762306a36Sopenharmony_ci .resume_noirq = brcmstb_gpio_resume, 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int brcmstb_gpio_probe(struct platform_device *pdev) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 59362306a36Sopenharmony_ci struct device_node *np = dev->of_node; 59462306a36Sopenharmony_ci void __iomem *reg_base; 59562306a36Sopenharmony_ci struct brcmstb_gpio_priv *priv; 59662306a36Sopenharmony_ci struct resource *res; 59762306a36Sopenharmony_ci struct property *prop; 59862306a36Sopenharmony_ci const __be32 *p; 59962306a36Sopenharmony_ci u32 bank_width; 60062306a36Sopenharmony_ci int num_banks = 0; 60162306a36Sopenharmony_ci int err; 60262306a36Sopenharmony_ci static int gpio_base; 60362306a36Sopenharmony_ci unsigned long flags = 0; 60462306a36Sopenharmony_ci bool need_wakeup_event = false; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 60762306a36Sopenharmony_ci if (!priv) 60862306a36Sopenharmony_ci return -ENOMEM; 60962306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 61062306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->bank_list); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 61362306a36Sopenharmony_ci if (IS_ERR(reg_base)) 61462306a36Sopenharmony_ci return PTR_ERR(reg_base); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci priv->gpio_base = gpio_base; 61762306a36Sopenharmony_ci priv->reg_base = reg_base; 61862306a36Sopenharmony_ci priv->pdev = pdev; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (of_property_read_bool(np, "interrupt-controller")) { 62162306a36Sopenharmony_ci priv->parent_irq = platform_get_irq(pdev, 0); 62262306a36Sopenharmony_ci if (priv->parent_irq <= 0) 62362306a36Sopenharmony_ci return -ENOENT; 62462306a36Sopenharmony_ci } else { 62562306a36Sopenharmony_ci priv->parent_irq = -ENOENT; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (brcmstb_gpio_sanity_check_banks(dev, np, res)) 62962306a36Sopenharmony_ci return -EINVAL; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* 63262306a36Sopenharmony_ci * MIPS endianness is configured by boot strap, which also reverses all 63362306a36Sopenharmony_ci * bus endianness (i.e., big-endian CPU + big endian bus ==> native 63462306a36Sopenharmony_ci * endian I/O). 63562306a36Sopenharmony_ci * 63662306a36Sopenharmony_ci * Other architectures (e.g., ARM) either do not support big endian, or 63762306a36Sopenharmony_ci * else leave I/O in little endian mode. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ci#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN) 64062306a36Sopenharmony_ci flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; 64162306a36Sopenharmony_ci#endif 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p, 64462306a36Sopenharmony_ci bank_width) { 64562306a36Sopenharmony_ci struct brcmstb_gpio_bank *bank; 64662306a36Sopenharmony_ci struct gpio_chip *gc; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* 64962306a36Sopenharmony_ci * If bank_width is 0, then there is an empty bank in the 65062306a36Sopenharmony_ci * register block. Special handling for this case. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci if (bank_width == 0) { 65362306a36Sopenharmony_ci dev_dbg(dev, "Width 0 found: Empty bank @ %d\n", 65462306a36Sopenharmony_ci num_banks); 65562306a36Sopenharmony_ci num_banks++; 65662306a36Sopenharmony_ci gpio_base += MAX_GPIO_PER_BANK; 65762306a36Sopenharmony_ci continue; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL); 66162306a36Sopenharmony_ci if (!bank) { 66262306a36Sopenharmony_ci err = -ENOMEM; 66362306a36Sopenharmony_ci goto fail; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci bank->parent_priv = priv; 66762306a36Sopenharmony_ci bank->id = num_banks; 66862306a36Sopenharmony_ci if (bank_width <= 0 || bank_width > MAX_GPIO_PER_BANK) { 66962306a36Sopenharmony_ci dev_err(dev, "Invalid bank width %d\n", bank_width); 67062306a36Sopenharmony_ci err = -EINVAL; 67162306a36Sopenharmony_ci goto fail; 67262306a36Sopenharmony_ci } else { 67362306a36Sopenharmony_ci bank->width = bank_width; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* 67762306a36Sopenharmony_ci * Regs are 4 bytes wide, have data reg, no set/clear regs, 67862306a36Sopenharmony_ci * and direction bits have 0 = output and 1 = input 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ci gc = &bank->gc; 68162306a36Sopenharmony_ci err = bgpio_init(gc, dev, 4, 68262306a36Sopenharmony_ci reg_base + GIO_DATA(bank->id), 68362306a36Sopenharmony_ci NULL, NULL, NULL, 68462306a36Sopenharmony_ci reg_base + GIO_IODIR(bank->id), flags); 68562306a36Sopenharmony_ci if (err) { 68662306a36Sopenharmony_ci dev_err(dev, "bgpio_init() failed\n"); 68762306a36Sopenharmony_ci goto fail; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci gc->owner = THIS_MODULE; 69162306a36Sopenharmony_ci gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np); 69262306a36Sopenharmony_ci if (!gc->label) { 69362306a36Sopenharmony_ci err = -ENOMEM; 69462306a36Sopenharmony_ci goto fail; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci gc->base = gpio_base; 69762306a36Sopenharmony_ci gc->of_gpio_n_cells = 2; 69862306a36Sopenharmony_ci gc->of_xlate = brcmstb_gpio_of_xlate; 69962306a36Sopenharmony_ci /* not all ngpio lines are valid, will use bank width later */ 70062306a36Sopenharmony_ci gc->ngpio = MAX_GPIO_PER_BANK; 70162306a36Sopenharmony_ci gc->offset = bank->id * MAX_GPIO_PER_BANK; 70262306a36Sopenharmony_ci if (priv->parent_irq > 0) 70362306a36Sopenharmony_ci gc->to_irq = brcmstb_gpio_to_irq; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* 70662306a36Sopenharmony_ci * Mask all interrupts by default, since wakeup interrupts may 70762306a36Sopenharmony_ci * be retained from S5 cold boot 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ci need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank); 71062306a36Sopenharmony_ci gc->write_reg(reg_base + GIO_MASK(bank->id), 0); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci err = gpiochip_add_data(gc, bank); 71362306a36Sopenharmony_ci if (err) { 71462306a36Sopenharmony_ci dev_err(dev, "Could not add gpiochip for bank %d\n", 71562306a36Sopenharmony_ci bank->id); 71662306a36Sopenharmony_ci goto fail; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci gpio_base += gc->ngpio; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id, 72162306a36Sopenharmony_ci gc->base, gc->ngpio, bank->width); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Everything looks good, so add bank to list */ 72462306a36Sopenharmony_ci list_add(&bank->node, &priv->bank_list); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci num_banks++; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci priv->num_gpios = gpio_base - priv->gpio_base; 73062306a36Sopenharmony_ci if (priv->parent_irq > 0) { 73162306a36Sopenharmony_ci err = brcmstb_gpio_irq_setup(pdev, priv); 73262306a36Sopenharmony_ci if (err) 73362306a36Sopenharmony_ci goto fail; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (priv->parent_wake_irq && need_wakeup_event) 73762306a36Sopenharmony_ci pm_wakeup_event(dev, 0); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cifail: 74262306a36Sopenharmony_ci (void) brcmstb_gpio_remove(pdev); 74362306a36Sopenharmony_ci return err; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic const struct of_device_id brcmstb_gpio_of_match[] = { 74762306a36Sopenharmony_ci { .compatible = "brcm,brcmstb-gpio" }, 74862306a36Sopenharmony_ci {}, 74962306a36Sopenharmony_ci}; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcmstb_gpio_of_match); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic struct platform_driver brcmstb_gpio_driver = { 75462306a36Sopenharmony_ci .driver = { 75562306a36Sopenharmony_ci .name = "brcmstb-gpio", 75662306a36Sopenharmony_ci .of_match_table = brcmstb_gpio_of_match, 75762306a36Sopenharmony_ci .pm = &brcmstb_gpio_pm_ops, 75862306a36Sopenharmony_ci }, 75962306a36Sopenharmony_ci .probe = brcmstb_gpio_probe, 76062306a36Sopenharmony_ci .remove = brcmstb_gpio_remove, 76162306a36Sopenharmony_ci .shutdown = brcmstb_gpio_shutdown, 76262306a36Sopenharmony_ci}; 76362306a36Sopenharmony_cimodule_platform_driver(brcmstb_gpio_driver); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ciMODULE_AUTHOR("Gregory Fong"); 76662306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Broadcom BRCMSTB SoC UPG GPIO"); 76762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 768