18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2008, 2009 Provigent Ltd.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Baruch Siach <baruch@tkos.co.il>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Data sheet: ARM DDI 0190B, September 2000
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/ioport.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/irq.h>
188c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/bitops.h>
218c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
228c2ecf20Sopenharmony_ci#include <linux/device.h>
238c2ecf20Sopenharmony_ci#include <linux/amba/bus.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h>
268c2ecf20Sopenharmony_ci#include <linux/pm.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define GPIODIR 0x400
298c2ecf20Sopenharmony_ci#define GPIOIS  0x404
308c2ecf20Sopenharmony_ci#define GPIOIBE 0x408
318c2ecf20Sopenharmony_ci#define GPIOIEV 0x40C
328c2ecf20Sopenharmony_ci#define GPIOIE  0x410
338c2ecf20Sopenharmony_ci#define GPIORIS 0x414
348c2ecf20Sopenharmony_ci#define GPIOMIS 0x418
358c2ecf20Sopenharmony_ci#define GPIOIC  0x41C
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define PL061_GPIO_NR	8
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
408c2ecf20Sopenharmony_cistruct pl061_context_save_regs {
418c2ecf20Sopenharmony_ci	u8 gpio_data;
428c2ecf20Sopenharmony_ci	u8 gpio_dir;
438c2ecf20Sopenharmony_ci	u8 gpio_is;
448c2ecf20Sopenharmony_ci	u8 gpio_ibe;
458c2ecf20Sopenharmony_ci	u8 gpio_iev;
468c2ecf20Sopenharmony_ci	u8 gpio_ie;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci#endif
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct pl061 {
518c2ecf20Sopenharmony_ci	raw_spinlock_t		lock;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	void __iomem		*base;
548c2ecf20Sopenharmony_ci	struct gpio_chip	gc;
558c2ecf20Sopenharmony_ci	struct irq_chip		irq_chip;
568c2ecf20Sopenharmony_ci	int			parent_irq;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
598c2ecf20Sopenharmony_ci	struct pl061_context_save_regs csave_regs;
608c2ecf20Sopenharmony_ci#endif
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (readb(pl061->base + GPIODIR) & BIT(offset))
688c2ecf20Sopenharmony_ci		return GPIO_LINE_DIRECTION_OUT;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
768c2ecf20Sopenharmony_ci	unsigned long flags;
778c2ecf20Sopenharmony_ci	unsigned char gpiodir;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pl061->lock, flags);
808c2ecf20Sopenharmony_ci	gpiodir = readb(pl061->base + GPIODIR);
818c2ecf20Sopenharmony_ci	gpiodir &= ~(BIT(offset));
828c2ecf20Sopenharmony_ci	writeb(gpiodir, pl061->base + GPIODIR);
838c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pl061->lock, flags);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return 0;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int pl061_direction_output(struct gpio_chip *gc, unsigned offset,
898c2ecf20Sopenharmony_ci		int value)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
928c2ecf20Sopenharmony_ci	unsigned long flags;
938c2ecf20Sopenharmony_ci	unsigned char gpiodir;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pl061->lock, flags);
968c2ecf20Sopenharmony_ci	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
978c2ecf20Sopenharmony_ci	gpiodir = readb(pl061->base + GPIODIR);
988c2ecf20Sopenharmony_ci	gpiodir |= BIT(offset);
998c2ecf20Sopenharmony_ci	writeb(gpiodir, pl061->base + GPIODIR);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	 * gpio value is set again, because pl061 doesn't allow to set value of
1038c2ecf20Sopenharmony_ci	 * a gpio pin before configuring it in OUT mode.
1048c2ecf20Sopenharmony_ci	 */
1058c2ecf20Sopenharmony_ci	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
1068c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pl061->lock, flags);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int pl061_get_value(struct gpio_chip *gc, unsigned offset)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return !!readb(pl061->base + (BIT(offset + 2)));
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int pl061_irq_type(struct irq_data *d, unsigned trigger)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
1288c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
1298c2ecf20Sopenharmony_ci	int offset = irqd_to_hwirq(d);
1308c2ecf20Sopenharmony_ci	unsigned long flags;
1318c2ecf20Sopenharmony_ci	u8 gpiois, gpioibe, gpioiev;
1328c2ecf20Sopenharmony_ci	u8 bit = BIT(offset);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (offset < 0 || offset >= PL061_GPIO_NR)
1358c2ecf20Sopenharmony_ci		return -EINVAL;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
1388c2ecf20Sopenharmony_ci	    (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
1398c2ecf20Sopenharmony_ci	{
1408c2ecf20Sopenharmony_ci		dev_err(gc->parent,
1418c2ecf20Sopenharmony_ci			"trying to configure line %d for both level and edge "
1428c2ecf20Sopenharmony_ci			"detection, choose one!\n",
1438c2ecf20Sopenharmony_ci			offset);
1448c2ecf20Sopenharmony_ci		return -EINVAL;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pl061->lock, flags);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	gpioiev = readb(pl061->base + GPIOIEV);
1518c2ecf20Sopenharmony_ci	gpiois = readb(pl061->base + GPIOIS);
1528c2ecf20Sopenharmony_ci	gpioibe = readb(pl061->base + GPIOIBE);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
1558c2ecf20Sopenharmony_ci		bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		/* Disable edge detection */
1588c2ecf20Sopenharmony_ci		gpioibe &= ~bit;
1598c2ecf20Sopenharmony_ci		/* Enable level detection */
1608c2ecf20Sopenharmony_ci		gpiois |= bit;
1618c2ecf20Sopenharmony_ci		/* Select polarity */
1628c2ecf20Sopenharmony_ci		if (polarity)
1638c2ecf20Sopenharmony_ci			gpioiev |= bit;
1648c2ecf20Sopenharmony_ci		else
1658c2ecf20Sopenharmony_ci			gpioiev &= ~bit;
1668c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
1678c2ecf20Sopenharmony_ci		dev_dbg(gc->parent, "line %d: IRQ on %s level\n",
1688c2ecf20Sopenharmony_ci			offset,
1698c2ecf20Sopenharmony_ci			polarity ? "HIGH" : "LOW");
1708c2ecf20Sopenharmony_ci	} else if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
1718c2ecf20Sopenharmony_ci		/* Disable level detection */
1728c2ecf20Sopenharmony_ci		gpiois &= ~bit;
1738c2ecf20Sopenharmony_ci		/* Select both edges, setting this makes GPIOEV be ignored */
1748c2ecf20Sopenharmony_ci		gpioibe |= bit;
1758c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
1768c2ecf20Sopenharmony_ci		dev_dbg(gc->parent, "line %d: IRQ on both edges\n", offset);
1778c2ecf20Sopenharmony_ci	} else if ((trigger & IRQ_TYPE_EDGE_RISING) ||
1788c2ecf20Sopenharmony_ci		   (trigger & IRQ_TYPE_EDGE_FALLING)) {
1798c2ecf20Sopenharmony_ci		bool rising = trigger & IRQ_TYPE_EDGE_RISING;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		/* Disable level detection */
1828c2ecf20Sopenharmony_ci		gpiois &= ~bit;
1838c2ecf20Sopenharmony_ci		/* Clear detection on both edges */
1848c2ecf20Sopenharmony_ci		gpioibe &= ~bit;
1858c2ecf20Sopenharmony_ci		/* Select edge */
1868c2ecf20Sopenharmony_ci		if (rising)
1878c2ecf20Sopenharmony_ci			gpioiev |= bit;
1888c2ecf20Sopenharmony_ci		else
1898c2ecf20Sopenharmony_ci			gpioiev &= ~bit;
1908c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
1918c2ecf20Sopenharmony_ci		dev_dbg(gc->parent, "line %d: IRQ on %s edge\n",
1928c2ecf20Sopenharmony_ci			offset,
1938c2ecf20Sopenharmony_ci			rising ? "RISING" : "FALLING");
1948c2ecf20Sopenharmony_ci	} else {
1958c2ecf20Sopenharmony_ci		/* No trigger: disable everything */
1968c2ecf20Sopenharmony_ci		gpiois &= ~bit;
1978c2ecf20Sopenharmony_ci		gpioibe &= ~bit;
1988c2ecf20Sopenharmony_ci		gpioiev &= ~bit;
1998c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_bad_irq);
2008c2ecf20Sopenharmony_ci		dev_warn(gc->parent, "no trigger selected for line %d\n",
2018c2ecf20Sopenharmony_ci			 offset);
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	writeb(gpiois, pl061->base + GPIOIS);
2058c2ecf20Sopenharmony_ci	writeb(gpioibe, pl061->base + GPIOIBE);
2068c2ecf20Sopenharmony_ci	writeb(gpioiev, pl061->base + GPIOIEV);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pl061->lock, flags);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return 0;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void pl061_irq_handler(struct irq_desc *desc)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	unsigned long pending;
2168c2ecf20Sopenharmony_ci	int offset;
2178c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
2188c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
2198c2ecf20Sopenharmony_ci	struct irq_chip *irqchip = irq_desc_get_chip(desc);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	chained_irq_enter(irqchip, desc);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	pending = readb(pl061->base + GPIOMIS);
2248c2ecf20Sopenharmony_ci	if (pending) {
2258c2ecf20Sopenharmony_ci		for_each_set_bit(offset, &pending, PL061_GPIO_NR)
2268c2ecf20Sopenharmony_ci			generic_handle_irq(irq_find_mapping(gc->irq.domain,
2278c2ecf20Sopenharmony_ci							    offset));
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	chained_irq_exit(irqchip, desc);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void pl061_irq_mask(struct irq_data *d)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2368c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
2378c2ecf20Sopenharmony_ci	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
2388c2ecf20Sopenharmony_ci	u8 gpioie;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	raw_spin_lock(&pl061->lock);
2418c2ecf20Sopenharmony_ci	gpioie = readb(pl061->base + GPIOIE) & ~mask;
2428c2ecf20Sopenharmony_ci	writeb(gpioie, pl061->base + GPIOIE);
2438c2ecf20Sopenharmony_ci	raw_spin_unlock(&pl061->lock);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic void pl061_irq_unmask(struct irq_data *d)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2498c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
2508c2ecf20Sopenharmony_ci	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
2518c2ecf20Sopenharmony_ci	u8 gpioie;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	raw_spin_lock(&pl061->lock);
2548c2ecf20Sopenharmony_ci	gpioie = readb(pl061->base + GPIOIE) | mask;
2558c2ecf20Sopenharmony_ci	writeb(gpioie, pl061->base + GPIOIE);
2568c2ecf20Sopenharmony_ci	raw_spin_unlock(&pl061->lock);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/**
2608c2ecf20Sopenharmony_ci * pl061_irq_ack() - ACK an edge IRQ
2618c2ecf20Sopenharmony_ci * @d: IRQ data for this IRQ
2628c2ecf20Sopenharmony_ci *
2638c2ecf20Sopenharmony_ci * This gets called from the edge IRQ handler to ACK the edge IRQ
2648c2ecf20Sopenharmony_ci * in the GPIOIC (interrupt-clear) register. For level IRQs this is
2658c2ecf20Sopenharmony_ci * not needed: these go away when the level signal goes away.
2668c2ecf20Sopenharmony_ci */
2678c2ecf20Sopenharmony_cistatic void pl061_irq_ack(struct irq_data *d)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2708c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
2718c2ecf20Sopenharmony_ci	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	raw_spin_lock(&pl061->lock);
2748c2ecf20Sopenharmony_ci	writeb(mask, pl061->base + GPIOIC);
2758c2ecf20Sopenharmony_ci	raw_spin_unlock(&pl061->lock);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2818c2ecf20Sopenharmony_ci	struct pl061 *pl061 = gpiochip_get_data(gc);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return irq_set_irq_wake(pl061->parent_irq, state);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int pl061_probe(struct amba_device *adev, const struct amba_id *id)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct device *dev = &adev->dev;
2898c2ecf20Sopenharmony_ci	struct pl061 *pl061;
2908c2ecf20Sopenharmony_ci	struct gpio_irq_chip *girq;
2918c2ecf20Sopenharmony_ci	int ret, irq;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL);
2948c2ecf20Sopenharmony_ci	if (pl061 == NULL)
2958c2ecf20Sopenharmony_ci		return -ENOMEM;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	pl061->base = devm_ioremap_resource(dev, &adev->res);
2988c2ecf20Sopenharmony_ci	if (IS_ERR(pl061->base))
2998c2ecf20Sopenharmony_ci		return PTR_ERR(pl061->base);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	raw_spin_lock_init(&pl061->lock);
3028c2ecf20Sopenharmony_ci	pl061->gc.request = gpiochip_generic_request;
3038c2ecf20Sopenharmony_ci	pl061->gc.free = gpiochip_generic_free;
3048c2ecf20Sopenharmony_ci	pl061->gc.base = -1;
3058c2ecf20Sopenharmony_ci	pl061->gc.get_direction = pl061_get_direction;
3068c2ecf20Sopenharmony_ci	pl061->gc.direction_input = pl061_direction_input;
3078c2ecf20Sopenharmony_ci	pl061->gc.direction_output = pl061_direction_output;
3088c2ecf20Sopenharmony_ci	pl061->gc.get = pl061_get_value;
3098c2ecf20Sopenharmony_ci	pl061->gc.set = pl061_set_value;
3108c2ecf20Sopenharmony_ci	pl061->gc.ngpio = PL061_GPIO_NR;
3118c2ecf20Sopenharmony_ci	pl061->gc.label = dev_name(dev);
3128c2ecf20Sopenharmony_ci	pl061->gc.parent = dev;
3138c2ecf20Sopenharmony_ci	pl061->gc.owner = THIS_MODULE;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/*
3168c2ecf20Sopenharmony_ci	 * irq_chip support
3178c2ecf20Sopenharmony_ci	 */
3188c2ecf20Sopenharmony_ci	pl061->irq_chip.name = dev_name(dev);
3198c2ecf20Sopenharmony_ci	pl061->irq_chip.irq_ack	= pl061_irq_ack;
3208c2ecf20Sopenharmony_ci	pl061->irq_chip.irq_mask = pl061_irq_mask;
3218c2ecf20Sopenharmony_ci	pl061->irq_chip.irq_unmask = pl061_irq_unmask;
3228c2ecf20Sopenharmony_ci	pl061->irq_chip.irq_set_type = pl061_irq_type;
3238c2ecf20Sopenharmony_ci	pl061->irq_chip.irq_set_wake = pl061_irq_set_wake;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	writeb(0, pl061->base + GPIOIE); /* disable irqs */
3268c2ecf20Sopenharmony_ci	irq = adev->irq[0];
3278c2ecf20Sopenharmony_ci	if (!irq)
3288c2ecf20Sopenharmony_ci		dev_warn(&adev->dev, "IRQ support disabled\n");
3298c2ecf20Sopenharmony_ci	pl061->parent_irq = irq;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	girq = &pl061->gc.irq;
3328c2ecf20Sopenharmony_ci	girq->chip = &pl061->irq_chip;
3338c2ecf20Sopenharmony_ci	girq->parent_handler = pl061_irq_handler;
3348c2ecf20Sopenharmony_ci	girq->num_parents = 1;
3358c2ecf20Sopenharmony_ci	girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
3368c2ecf20Sopenharmony_ci				     GFP_KERNEL);
3378c2ecf20Sopenharmony_ci	if (!girq->parents)
3388c2ecf20Sopenharmony_ci		return -ENOMEM;
3398c2ecf20Sopenharmony_ci	girq->parents[0] = irq;
3408c2ecf20Sopenharmony_ci	girq->default_type = IRQ_TYPE_NONE;
3418c2ecf20Sopenharmony_ci	girq->handler = handle_bad_irq;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);
3448c2ecf20Sopenharmony_ci	if (ret)
3458c2ecf20Sopenharmony_ci		return ret;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	amba_set_drvdata(adev, pl061);
3488c2ecf20Sopenharmony_ci	dev_info(dev, "PL061 GPIO chip registered\n");
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
3548c2ecf20Sopenharmony_cistatic int pl061_suspend(struct device *dev)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct pl061 *pl061 = dev_get_drvdata(dev);
3578c2ecf20Sopenharmony_ci	int offset;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	pl061->csave_regs.gpio_data = 0;
3608c2ecf20Sopenharmony_ci	pl061->csave_regs.gpio_dir = readb(pl061->base + GPIODIR);
3618c2ecf20Sopenharmony_ci	pl061->csave_regs.gpio_is = readb(pl061->base + GPIOIS);
3628c2ecf20Sopenharmony_ci	pl061->csave_regs.gpio_ibe = readb(pl061->base + GPIOIBE);
3638c2ecf20Sopenharmony_ci	pl061->csave_regs.gpio_iev = readb(pl061->base + GPIOIEV);
3648c2ecf20Sopenharmony_ci	pl061->csave_regs.gpio_ie = readb(pl061->base + GPIOIE);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	for (offset = 0; offset < PL061_GPIO_NR; offset++) {
3678c2ecf20Sopenharmony_ci		if (pl061->csave_regs.gpio_dir & (BIT(offset)))
3688c2ecf20Sopenharmony_ci			pl061->csave_regs.gpio_data |=
3698c2ecf20Sopenharmony_ci				pl061_get_value(&pl061->gc, offset) << offset;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return 0;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int pl061_resume(struct device *dev)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct pl061 *pl061 = dev_get_drvdata(dev);
3788c2ecf20Sopenharmony_ci	int offset;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	for (offset = 0; offset < PL061_GPIO_NR; offset++) {
3818c2ecf20Sopenharmony_ci		if (pl061->csave_regs.gpio_dir & (BIT(offset)))
3828c2ecf20Sopenharmony_ci			pl061_direction_output(&pl061->gc, offset,
3838c2ecf20Sopenharmony_ci					pl061->csave_regs.gpio_data &
3848c2ecf20Sopenharmony_ci					(BIT(offset)));
3858c2ecf20Sopenharmony_ci		else
3868c2ecf20Sopenharmony_ci			pl061_direction_input(&pl061->gc, offset);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
3908c2ecf20Sopenharmony_ci	writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
3918c2ecf20Sopenharmony_ci	writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
3928c2ecf20Sopenharmony_ci	writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic const struct dev_pm_ops pl061_dev_pm_ops = {
3988c2ecf20Sopenharmony_ci	.suspend = pl061_suspend,
3998c2ecf20Sopenharmony_ci	.resume = pl061_resume,
4008c2ecf20Sopenharmony_ci	.freeze = pl061_suspend,
4018c2ecf20Sopenharmony_ci	.restore = pl061_resume,
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci#endif
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic const struct amba_id pl061_ids[] = {
4068c2ecf20Sopenharmony_ci	{
4078c2ecf20Sopenharmony_ci		.id	= 0x00041061,
4088c2ecf20Sopenharmony_ci		.mask	= 0x000fffff,
4098c2ecf20Sopenharmony_ci	},
4108c2ecf20Sopenharmony_ci	{ 0, 0 },
4118c2ecf20Sopenharmony_ci};
4128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(amba, pl061_ids);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic struct amba_driver pl061_gpio_driver = {
4158c2ecf20Sopenharmony_ci	.drv = {
4168c2ecf20Sopenharmony_ci		.name	= "pl061_gpio",
4178c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4188c2ecf20Sopenharmony_ci		.pm	= &pl061_dev_pm_ops,
4198c2ecf20Sopenharmony_ci#endif
4208c2ecf20Sopenharmony_ci	},
4218c2ecf20Sopenharmony_ci	.id_table	= pl061_ids,
4228c2ecf20Sopenharmony_ci	.probe		= pl061_probe,
4238c2ecf20Sopenharmony_ci};
4248c2ecf20Sopenharmony_cimodule_amba_driver(pl061_gpio_driver);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
427