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