xref: /openbmc/linux/drivers/gpio/gpio-pl061.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c103de24SGrant Likely /*
3c103de24SGrant Likely  * Copyright (C) 2008, 2009 Provigent Ltd.
4c103de24SGrant Likely  *
5ef3e7100SPaul Gortmaker  * Author: Baruch Siach <baruch@tkos.co.il>
6ef3e7100SPaul Gortmaker  *
7c103de24SGrant Likely  * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)
8c103de24SGrant Likely  *
9c103de24SGrant Likely  * Data sheet: ARM DDI 0190B, September 2000
10c103de24SGrant Likely  */
11*5b937a83SAndy Shevchenko #include <linux/amba/bus.h>
12*5b937a83SAndy Shevchenko #include <linux/bitops.h>
13*5b937a83SAndy Shevchenko #include <linux/device.h>
14c103de24SGrant Likely #include <linux/errno.h>
15*5b937a83SAndy Shevchenko #include <linux/gpio/driver.h>
16ef3e7100SPaul Gortmaker #include <linux/init.h>
17*5b937a83SAndy Shevchenko #include <linux/interrupt.h>
18c103de24SGrant Likely #include <linux/io.h>
19c103de24SGrant Likely #include <linux/ioport.h>
20c103de24SGrant Likely #include <linux/irq.h>
21de88cbb7SCatalin Marinas #include <linux/irqchip/chained_irq.h>
2261684440SRob Herring #include <linux/module.h>
2339b70ee0SHaojian Zhuang #include <linux/pinctrl/consumer.h>
24e198a8deSDeepak Sikri #include <linux/pm.h>
25*5b937a83SAndy Shevchenko #include <linux/seq_file.h>
26*5b937a83SAndy Shevchenko #include <linux/slab.h>
27*5b937a83SAndy Shevchenko #include <linux/spinlock.h>
28c103de24SGrant Likely 
29c103de24SGrant Likely #define GPIODIR 0x400
30c103de24SGrant Likely #define GPIOIS  0x404
31c103de24SGrant Likely #define GPIOIBE 0x408
32c103de24SGrant Likely #define GPIOIEV 0x40C
33c103de24SGrant Likely #define GPIOIE  0x410
34c103de24SGrant Likely #define GPIORIS 0x414
35c103de24SGrant Likely #define GPIOMIS 0x418
36c103de24SGrant Likely #define GPIOIC  0x41C
37c103de24SGrant Likely 
38c103de24SGrant Likely #define PL061_GPIO_NR	8
39c103de24SGrant Likely 
40e198a8deSDeepak Sikri #ifdef CONFIG_PM
41e198a8deSDeepak Sikri struct pl061_context_save_regs {
42e198a8deSDeepak Sikri 	u8 gpio_data;
43e198a8deSDeepak Sikri 	u8 gpio_dir;
44e198a8deSDeepak Sikri 	u8 gpio_is;
45e198a8deSDeepak Sikri 	u8 gpio_ibe;
46e198a8deSDeepak Sikri 	u8 gpio_iev;
47e198a8deSDeepak Sikri 	u8 gpio_ie;
48e198a8deSDeepak Sikri };
49e198a8deSDeepak Sikri #endif
50c103de24SGrant Likely 
51538f76c5SLinus Walleij struct pl061 {
5299b9b45dSJulia Cartwright 	raw_spinlock_t		lock;
53c103de24SGrant Likely 
54c103de24SGrant Likely 	void __iomem		*base;
55c103de24SGrant Likely 	struct gpio_chip	gc;
569c18be8eSLinus Walleij 	int			parent_irq;
57e198a8deSDeepak Sikri 
58e198a8deSDeepak Sikri #ifdef CONFIG_PM
59e198a8deSDeepak Sikri 	struct pl061_context_save_regs csave_regs;
60e198a8deSDeepak Sikri #endif
61c103de24SGrant Likely };
62c103de24SGrant Likely 
pl061_get_direction(struct gpio_chip * gc,unsigned offset)633484f1beSLinus Walleij static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
643484f1beSLinus Walleij {
652796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
663484f1beSLinus Walleij 
67e42615ecSMatti Vaittinen 	if (readb(pl061->base + GPIODIR) & BIT(offset))
68e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
69e42615ecSMatti Vaittinen 
70e42615ecSMatti Vaittinen 	return GPIO_LINE_DIRECTION_IN;
713484f1beSLinus Walleij }
723484f1beSLinus Walleij 
pl061_direction_input(struct gpio_chip * gc,unsigned offset)73c103de24SGrant Likely static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
74c103de24SGrant Likely {
752796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
76c103de24SGrant Likely 	unsigned long flags;
77c103de24SGrant Likely 	unsigned char gpiodir;
78c103de24SGrant Likely 
7999b9b45dSJulia Cartwright 	raw_spin_lock_irqsave(&pl061->lock, flags);
802796325fSLinus Walleij 	gpiodir = readb(pl061->base + GPIODIR);
81bea41504SJavier Martinez Canillas 	gpiodir &= ~(BIT(offset));
822796325fSLinus Walleij 	writeb(gpiodir, pl061->base + GPIODIR);
8399b9b45dSJulia Cartwright 	raw_spin_unlock_irqrestore(&pl061->lock, flags);
84c103de24SGrant Likely 
85c103de24SGrant Likely 	return 0;
86c103de24SGrant Likely }
87c103de24SGrant Likely 
pl061_direction_output(struct gpio_chip * gc,unsigned offset,int value)88c103de24SGrant Likely static int pl061_direction_output(struct gpio_chip *gc, unsigned offset,
89c103de24SGrant Likely 		int value)
90c103de24SGrant Likely {
912796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
92c103de24SGrant Likely 	unsigned long flags;
93c103de24SGrant Likely 	unsigned char gpiodir;
94c103de24SGrant Likely 
9599b9b45dSJulia Cartwright 	raw_spin_lock_irqsave(&pl061->lock, flags);
962796325fSLinus Walleij 	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
972796325fSLinus Walleij 	gpiodir = readb(pl061->base + GPIODIR);
98bea41504SJavier Martinez Canillas 	gpiodir |= BIT(offset);
992796325fSLinus Walleij 	writeb(gpiodir, pl061->base + GPIODIR);
100c103de24SGrant Likely 
101c103de24SGrant Likely 	/*
102c103de24SGrant Likely 	 * gpio value is set again, because pl061 doesn't allow to set value of
103c103de24SGrant Likely 	 * a gpio pin before configuring it in OUT mode.
104c103de24SGrant Likely 	 */
1052796325fSLinus Walleij 	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
10699b9b45dSJulia Cartwright 	raw_spin_unlock_irqrestore(&pl061->lock, flags);
107c103de24SGrant Likely 
108c103de24SGrant Likely 	return 0;
109c103de24SGrant Likely }
110c103de24SGrant Likely 
pl061_get_value(struct gpio_chip * gc,unsigned offset)111c103de24SGrant Likely static int pl061_get_value(struct gpio_chip *gc, unsigned offset)
112c103de24SGrant Likely {
1132796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
114c103de24SGrant Likely 
1152796325fSLinus Walleij 	return !!readb(pl061->base + (BIT(offset + 2)));
116c103de24SGrant Likely }
117c103de24SGrant Likely 
pl061_set_value(struct gpio_chip * gc,unsigned offset,int value)118c103de24SGrant Likely static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
119c103de24SGrant Likely {
1202796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
121c103de24SGrant Likely 
1222796325fSLinus Walleij 	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
123c103de24SGrant Likely }
124c103de24SGrant Likely 
pl061_irq_type(struct irq_data * d,unsigned trigger)125c103de24SGrant Likely static int pl061_irq_type(struct irq_data *d, unsigned trigger)
126c103de24SGrant Likely {
1278d5b24bdSLinus Walleij 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
1282796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
129f1f70479SHaojian Zhuang 	int offset = irqd_to_hwirq(d);
130c103de24SGrant Likely 	unsigned long flags;
131c103de24SGrant Likely 	u8 gpiois, gpioibe, gpioiev;
132438a2c9aSLinus Walleij 	u8 bit = BIT(offset);
133c103de24SGrant Likely 
134c103de24SGrant Likely 	if (offset < 0 || offset >= PL061_GPIO_NR)
135c103de24SGrant Likely 		return -EINVAL;
136c103de24SGrant Likely 
1371dbf7f29SLinus Walleij 	if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
1381dbf7f29SLinus Walleij 	    (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
1391dbf7f29SLinus Walleij 	{
14058383c78SLinus Walleij 		dev_err(gc->parent,
1411dbf7f29SLinus Walleij 			"trying to configure line %d for both level and edge "
1421dbf7f29SLinus Walleij 			"detection, choose one!\n",
1431dbf7f29SLinus Walleij 			offset);
1441dbf7f29SLinus Walleij 		return -EINVAL;
1451dbf7f29SLinus Walleij 	}
1461dbf7f29SLinus Walleij 
14721d4de14SDan Carpenter 
14899b9b45dSJulia Cartwright 	raw_spin_lock_irqsave(&pl061->lock, flags);
14921d4de14SDan Carpenter 
1502796325fSLinus Walleij 	gpioiev = readb(pl061->base + GPIOIEV);
1512796325fSLinus Walleij 	gpiois = readb(pl061->base + GPIOIS);
1522796325fSLinus Walleij 	gpioibe = readb(pl061->base + GPIOIBE);
15321d4de14SDan Carpenter 
154438a2c9aSLinus Walleij 	if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
1551dbf7f29SLinus Walleij 		bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
1561dbf7f29SLinus Walleij 
1571dbf7f29SLinus Walleij 		/* Disable edge detection */
1581dbf7f29SLinus Walleij 		gpioibe &= ~bit;
1591dbf7f29SLinus Walleij 		/* Enable level detection */
160438a2c9aSLinus Walleij 		gpiois |= bit;
1611dbf7f29SLinus Walleij 		/* Select polarity */
1621dbf7f29SLinus Walleij 		if (polarity)
163438a2c9aSLinus Walleij 			gpioiev |= bit;
164438a2c9aSLinus Walleij 		else
165438a2c9aSLinus Walleij 			gpioiev &= ~bit;
16626ba9cd4SLinus Walleij 		irq_set_handler_locked(d, handle_level_irq);
16758383c78SLinus Walleij 		dev_dbg(gc->parent, "line %d: IRQ on %s level\n",
1681dbf7f29SLinus Walleij 			offset,
1691dbf7f29SLinus Walleij 			polarity ? "HIGH" : "LOW");
1701dbf7f29SLinus Walleij 	} else if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
1711dbf7f29SLinus Walleij 		/* Disable level detection */
1721dbf7f29SLinus Walleij 		gpiois &= ~bit;
1731dbf7f29SLinus Walleij 		/* Select both edges, setting this makes GPIOEV be ignored */
174438a2c9aSLinus Walleij 		gpioibe |= bit;
17526ba9cd4SLinus Walleij 		irq_set_handler_locked(d, handle_edge_irq);
17658383c78SLinus Walleij 		dev_dbg(gc->parent, "line %d: IRQ on both edges\n", offset);
1771dbf7f29SLinus Walleij 	} else if ((trigger & IRQ_TYPE_EDGE_RISING) ||
1781dbf7f29SLinus Walleij 		   (trigger & IRQ_TYPE_EDGE_FALLING)) {
1791dbf7f29SLinus Walleij 		bool rising = trigger & IRQ_TYPE_EDGE_RISING;
1801dbf7f29SLinus Walleij 
1811dbf7f29SLinus Walleij 		/* Disable level detection */
1821dbf7f29SLinus Walleij 		gpiois &= ~bit;
1831dbf7f29SLinus Walleij 		/* Clear detection on both edges */
184438a2c9aSLinus Walleij 		gpioibe &= ~bit;
1851dbf7f29SLinus Walleij 		/* Select edge */
1861dbf7f29SLinus Walleij 		if (rising)
187438a2c9aSLinus Walleij 			gpioiev |= bit;
1881dbf7f29SLinus Walleij 		else
189438a2c9aSLinus Walleij 			gpioiev &= ~bit;
19026ba9cd4SLinus Walleij 		irq_set_handler_locked(d, handle_edge_irq);
19158383c78SLinus Walleij 		dev_dbg(gc->parent, "line %d: IRQ on %s edge\n",
1921dbf7f29SLinus Walleij 			offset,
1931dbf7f29SLinus Walleij 			rising ? "RISING" : "FALLING");
1941dbf7f29SLinus Walleij 	} else {
1951dbf7f29SLinus Walleij 		/* No trigger: disable everything */
1961dbf7f29SLinus Walleij 		gpiois &= ~bit;
1971dbf7f29SLinus Walleij 		gpioibe &= ~bit;
1981dbf7f29SLinus Walleij 		gpioiev &= ~bit;
19926ba9cd4SLinus Walleij 		irq_set_handler_locked(d, handle_bad_irq);
20058383c78SLinus Walleij 		dev_warn(gc->parent, "no trigger selected for line %d\n",
2011dbf7f29SLinus Walleij 			 offset);
202438a2c9aSLinus Walleij 	}
203438a2c9aSLinus Walleij 
2042796325fSLinus Walleij 	writeb(gpiois, pl061->base + GPIOIS);
2052796325fSLinus Walleij 	writeb(gpioibe, pl061->base + GPIOIBE);
2062796325fSLinus Walleij 	writeb(gpioiev, pl061->base + GPIOIEV);
207c103de24SGrant Likely 
20899b9b45dSJulia Cartwright 	raw_spin_unlock_irqrestore(&pl061->lock, flags);
209c103de24SGrant Likely 
210c103de24SGrant Likely 	return 0;
211c103de24SGrant Likely }
212c103de24SGrant Likely 
pl061_irq_handler(struct irq_desc * desc)213bd0b9ac4SThomas Gleixner static void pl061_irq_handler(struct irq_desc *desc)
214c103de24SGrant Likely {
215c103de24SGrant Likely 	unsigned long pending;
216c103de24SGrant Likely 	int offset;
2178d5b24bdSLinus Walleij 	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
2182796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
219dece904dSRob Herring 	struct irq_chip *irqchip = irq_desc_get_chip(desc);
220c103de24SGrant Likely 
221dece904dSRob Herring 	chained_irq_enter(irqchip, desc);
222c103de24SGrant Likely 
2232796325fSLinus Walleij 	pending = readb(pl061->base + GPIOMIS);
2242de0dbc5SRob Herring 	if (pending) {
225c103de24SGrant Likely 		for_each_set_bit(offset, &pending, PL061_GPIO_NR)
226dbd1c54fSMarc Zyngier 			generic_handle_domain_irq(gc->irq.domain,
227dbd1c54fSMarc Zyngier 						  offset);
228c103de24SGrant Likely 	}
2292de0dbc5SRob Herring 
230dece904dSRob Herring 	chained_irq_exit(irqchip, desc);
231c103de24SGrant Likely }
232c103de24SGrant Likely 
pl061_irq_mask(struct irq_data * d)233f1f70479SHaojian Zhuang static void pl061_irq_mask(struct irq_data *d)
2343ab52475SRob Herring {
2358d5b24bdSLinus Walleij 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2362796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
237bea41504SJavier Martinez Canillas 	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
238f1f70479SHaojian Zhuang 	u8 gpioie;
2393ab52475SRob Herring 
24099b9b45dSJulia Cartwright 	raw_spin_lock(&pl061->lock);
2412796325fSLinus Walleij 	gpioie = readb(pl061->base + GPIOIE) & ~mask;
2422796325fSLinus Walleij 	writeb(gpioie, pl061->base + GPIOIE);
24399b9b45dSJulia Cartwright 	raw_spin_unlock(&pl061->lock);
24415d8c14aSMarc Zyngier 
24515d8c14aSMarc Zyngier 	gpiochip_disable_irq(gc, d->hwirq);
246c103de24SGrant Likely }
247c103de24SGrant Likely 
pl061_irq_unmask(struct irq_data * d)248f1f70479SHaojian Zhuang static void pl061_irq_unmask(struct irq_data *d)
249f1f70479SHaojian Zhuang {
2508d5b24bdSLinus Walleij 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2512796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
252bea41504SJavier Martinez Canillas 	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
253f1f70479SHaojian Zhuang 	u8 gpioie;
254f1f70479SHaojian Zhuang 
25515d8c14aSMarc Zyngier 	gpiochip_enable_irq(gc, d->hwirq);
25615d8c14aSMarc Zyngier 
25799b9b45dSJulia Cartwright 	raw_spin_lock(&pl061->lock);
2582796325fSLinus Walleij 	gpioie = readb(pl061->base + GPIOIE) | mask;
2592796325fSLinus Walleij 	writeb(gpioie, pl061->base + GPIOIE);
26099b9b45dSJulia Cartwright 	raw_spin_unlock(&pl061->lock);
261f1f70479SHaojian Zhuang }
262f1f70479SHaojian Zhuang 
26326ba9cd4SLinus Walleij /**
26426ba9cd4SLinus Walleij  * pl061_irq_ack() - ACK an edge IRQ
26526ba9cd4SLinus Walleij  * @d: IRQ data for this IRQ
26626ba9cd4SLinus Walleij  *
26726ba9cd4SLinus Walleij  * This gets called from the edge IRQ handler to ACK the edge IRQ
26826ba9cd4SLinus Walleij  * in the GPIOIC (interrupt-clear) register. For level IRQs this is
26926ba9cd4SLinus Walleij  * not needed: these go away when the level signal goes away.
27026ba9cd4SLinus Walleij  */
pl061_irq_ack(struct irq_data * d)27126ba9cd4SLinus Walleij static void pl061_irq_ack(struct irq_data *d)
27226ba9cd4SLinus Walleij {
27326ba9cd4SLinus Walleij 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2742796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
27526ba9cd4SLinus Walleij 	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
27626ba9cd4SLinus Walleij 
27799b9b45dSJulia Cartwright 	raw_spin_lock(&pl061->lock);
2782796325fSLinus Walleij 	writeb(mask, pl061->base + GPIOIC);
27999b9b45dSJulia Cartwright 	raw_spin_unlock(&pl061->lock);
28026ba9cd4SLinus Walleij }
28126ba9cd4SLinus Walleij 
pl061_irq_set_wake(struct irq_data * d,unsigned int state)2822f46205bSSudeep Holla static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
2832f46205bSSudeep Holla {
2842f46205bSSudeep Holla 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2852796325fSLinus Walleij 	struct pl061 *pl061 = gpiochip_get_data(gc);
2862f46205bSSudeep Holla 
2872796325fSLinus Walleij 	return irq_set_irq_wake(pl061->parent_irq, state);
2882f46205bSSudeep Holla }
2892f46205bSSudeep Holla 
pl061_irq_print_chip(struct irq_data * data,struct seq_file * p)29015d8c14aSMarc Zyngier static void pl061_irq_print_chip(struct irq_data *data, struct seq_file *p)
29115d8c14aSMarc Zyngier {
29215d8c14aSMarc Zyngier 	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
29315d8c14aSMarc Zyngier 
29415d8c14aSMarc Zyngier 	seq_printf(p, dev_name(gc->parent));
29515d8c14aSMarc Zyngier }
29615d8c14aSMarc Zyngier 
29715d8c14aSMarc Zyngier static const struct irq_chip pl061_irq_chip = {
29815d8c14aSMarc Zyngier 	.irq_ack		= pl061_irq_ack,
29915d8c14aSMarc Zyngier 	.irq_mask		= pl061_irq_mask,
30015d8c14aSMarc Zyngier 	.irq_unmask		= pl061_irq_unmask,
30115d8c14aSMarc Zyngier 	.irq_set_type		= pl061_irq_type,
30215d8c14aSMarc Zyngier 	.irq_set_wake		= pl061_irq_set_wake,
30315d8c14aSMarc Zyngier 	.irq_print_chip		= pl061_irq_print_chip,
30415d8c14aSMarc Zyngier 	.flags			= IRQCHIP_IMMUTABLE,
30515d8c14aSMarc Zyngier 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
30615d8c14aSMarc Zyngier };
30715d8c14aSMarc Zyngier 
pl061_probe(struct amba_device * adev,const struct amba_id * id)3088944df72STobias Klauser static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
309c103de24SGrant Likely {
3108944df72STobias Klauser 	struct device *dev = &adev->dev;
3112796325fSLinus Walleij 	struct pl061 *pl061;
31204ce935cSLinus Walleij 	struct gpio_irq_chip *girq;
3136da7b0ddSLinus Walleij 	int ret, irq;
314c103de24SGrant Likely 
3152796325fSLinus Walleij 	pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL);
3162796325fSLinus Walleij 	if (pl061 == NULL)
317c103de24SGrant Likely 		return -ENOMEM;
318c103de24SGrant Likely 
3192796325fSLinus Walleij 	pl061->base = devm_ioremap_resource(dev, &adev->res);
3202796325fSLinus Walleij 	if (IS_ERR(pl061->base))
3212796325fSLinus Walleij 		return PTR_ERR(pl061->base);
322c103de24SGrant Likely 
32399b9b45dSJulia Cartwright 	raw_spin_lock_init(&pl061->lock);
3242796325fSLinus Walleij 	pl061->gc.request = gpiochip_generic_request;
3252796325fSLinus Walleij 	pl061->gc.free = gpiochip_generic_free;
3266da7b0ddSLinus Walleij 	pl061->gc.base = -1;
3272796325fSLinus Walleij 	pl061->gc.get_direction = pl061_get_direction;
3282796325fSLinus Walleij 	pl061->gc.direction_input = pl061_direction_input;
3292796325fSLinus Walleij 	pl061->gc.direction_output = pl061_direction_output;
3302796325fSLinus Walleij 	pl061->gc.get = pl061_get_value;
3312796325fSLinus Walleij 	pl061->gc.set = pl061_set_value;
3322796325fSLinus Walleij 	pl061->gc.ngpio = PL061_GPIO_NR;
3332796325fSLinus Walleij 	pl061->gc.label = dev_name(dev);
3342796325fSLinus Walleij 	pl061->gc.parent = dev;
3352796325fSLinus Walleij 	pl061->gc.owner = THIS_MODULE;
336c103de24SGrant Likely 
337c103de24SGrant Likely 	/*
338c103de24SGrant Likely 	 * irq_chip support
339c103de24SGrant Likely 	 */
3402796325fSLinus Walleij 	writeb(0, pl061->base + GPIOIE); /* disable irqs */
3418944df72STobias Klauser 	irq = adev->irq[0];
3421a555713SAlexander Sverdlin 	if (!irq)
3431a555713SAlexander Sverdlin 		dev_warn(&adev->dev, "IRQ support disabled\n");
3442796325fSLinus Walleij 	pl061->parent_irq = irq;
3458944df72STobias Klauser 
34604ce935cSLinus Walleij 	girq = &pl061->gc.irq;
34715d8c14aSMarc Zyngier 	gpio_irq_chip_set_chip(girq, &pl061_irq_chip);
34804ce935cSLinus Walleij 	girq->parent_handler = pl061_irq_handler;
34904ce935cSLinus Walleij 	girq->num_parents = 1;
35004ce935cSLinus Walleij 	girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
35104ce935cSLinus Walleij 				     GFP_KERNEL);
35204ce935cSLinus Walleij 	if (!girq->parents)
35304ce935cSLinus Walleij 		return -ENOMEM;
35404ce935cSLinus Walleij 	girq->parents[0] = irq;
35504ce935cSLinus Walleij 	girq->default_type = IRQ_TYPE_NONE;
35604ce935cSLinus Walleij 	girq->handler = handle_bad_irq;
35704ce935cSLinus Walleij 
35804ce935cSLinus Walleij 	ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);
35904ce935cSLinus Walleij 	if (ret)
3608d5b24bdSLinus Walleij 		return ret;
3612ba3154dSLinus Walleij 
3622796325fSLinus Walleij 	amba_set_drvdata(adev, pl061);
3634d19adddSEnrico Weigelt 	dev_info(dev, "PL061 GPIO chip registered\n");
364e198a8deSDeepak Sikri 
365c103de24SGrant Likely 	return 0;
366c103de24SGrant Likely }
367c103de24SGrant Likely 
368e198a8deSDeepak Sikri #ifdef CONFIG_PM
pl061_suspend(struct device * dev)369e198a8deSDeepak Sikri static int pl061_suspend(struct device *dev)
370e198a8deSDeepak Sikri {
3712796325fSLinus Walleij 	struct pl061 *pl061 = dev_get_drvdata(dev);
372e198a8deSDeepak Sikri 	int offset;
373e198a8deSDeepak Sikri 
3742796325fSLinus Walleij 	pl061->csave_regs.gpio_data = 0;
3752796325fSLinus Walleij 	pl061->csave_regs.gpio_dir = readb(pl061->base + GPIODIR);
3762796325fSLinus Walleij 	pl061->csave_regs.gpio_is = readb(pl061->base + GPIOIS);
3772796325fSLinus Walleij 	pl061->csave_regs.gpio_ibe = readb(pl061->base + GPIOIBE);
3782796325fSLinus Walleij 	pl061->csave_regs.gpio_iev = readb(pl061->base + GPIOIEV);
3792796325fSLinus Walleij 	pl061->csave_regs.gpio_ie = readb(pl061->base + GPIOIE);
380e198a8deSDeepak Sikri 
381e198a8deSDeepak Sikri 	for (offset = 0; offset < PL061_GPIO_NR; offset++) {
3822796325fSLinus Walleij 		if (pl061->csave_regs.gpio_dir & (BIT(offset)))
3832796325fSLinus Walleij 			pl061->csave_regs.gpio_data |=
3842796325fSLinus Walleij 				pl061_get_value(&pl061->gc, offset) << offset;
385e198a8deSDeepak Sikri 	}
386e198a8deSDeepak Sikri 
387e198a8deSDeepak Sikri 	return 0;
388e198a8deSDeepak Sikri }
389e198a8deSDeepak Sikri 
pl061_resume(struct device * dev)390e198a8deSDeepak Sikri static int pl061_resume(struct device *dev)
391e198a8deSDeepak Sikri {
3922796325fSLinus Walleij 	struct pl061 *pl061 = dev_get_drvdata(dev);
393e198a8deSDeepak Sikri 	int offset;
394e198a8deSDeepak Sikri 
395e198a8deSDeepak Sikri 	for (offset = 0; offset < PL061_GPIO_NR; offset++) {
3962796325fSLinus Walleij 		if (pl061->csave_regs.gpio_dir & (BIT(offset)))
3972796325fSLinus Walleij 			pl061_direction_output(&pl061->gc, offset,
3982796325fSLinus Walleij 					pl061->csave_regs.gpio_data &
399bea41504SJavier Martinez Canillas 					(BIT(offset)));
400e198a8deSDeepak Sikri 		else
4012796325fSLinus Walleij 			pl061_direction_input(&pl061->gc, offset);
402e198a8deSDeepak Sikri 	}
403e198a8deSDeepak Sikri 
4042796325fSLinus Walleij 	writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
4052796325fSLinus Walleij 	writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
4062796325fSLinus Walleij 	writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
4072796325fSLinus Walleij 	writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);
408e198a8deSDeepak Sikri 
409e198a8deSDeepak Sikri 	return 0;
410e198a8deSDeepak Sikri }
411e198a8deSDeepak Sikri 
4126e33acedSViresh Kumar static const struct dev_pm_ops pl061_dev_pm_ops = {
4136e33acedSViresh Kumar 	.suspend = pl061_suspend,
4146e33acedSViresh Kumar 	.resume = pl061_resume,
4156e33acedSViresh Kumar 	.freeze = pl061_suspend,
4166e33acedSViresh Kumar 	.restore = pl061_resume,
4176e33acedSViresh Kumar };
418e198a8deSDeepak Sikri #endif
419e198a8deSDeepak Sikri 
42072c7c78eSArvind Yadav static const struct amba_id pl061_ids[] = {
421c103de24SGrant Likely 	{
422c103de24SGrant Likely 		.id	= 0x00041061,
423c103de24SGrant Likely 		.mask	= 0x000fffff,
424c103de24SGrant Likely 	},
425c103de24SGrant Likely 	{ 0, 0 },
426c103de24SGrant Likely };
42761684440SRob Herring MODULE_DEVICE_TABLE(amba, pl061_ids);
428c103de24SGrant Likely 
429c103de24SGrant Likely static struct amba_driver pl061_gpio_driver = {
430c103de24SGrant Likely 	.drv = {
431c103de24SGrant Likely 		.name	= "pl061_gpio",
432e198a8deSDeepak Sikri #ifdef CONFIG_PM
433e198a8deSDeepak Sikri 		.pm	= &pl061_dev_pm_ops,
434e198a8deSDeepak Sikri #endif
435c103de24SGrant Likely 	},
436c103de24SGrant Likely 	.id_table	= pl061_ids,
437c103de24SGrant Likely 	.probe		= pl061_probe,
438c103de24SGrant Likely };
43961684440SRob Herring module_amba_driver(pl061_gpio_driver);
440c103de24SGrant Likely 
44161684440SRob Herring MODULE_LICENSE("GPL v2");
442