xref: /openbmc/linux/drivers/gpio/gpio-mxc.c (revision 442b2494)
1d37a65bbSShawn Guo /*
2d37a65bbSShawn Guo  * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
3d37a65bbSShawn Guo  * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
4d37a65bbSShawn Guo  *
5d37a65bbSShawn Guo  * Based on code from Freescale,
6d37a65bbSShawn Guo  * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
7d37a65bbSShawn Guo  *
8d37a65bbSShawn Guo  * This program is free software; you can redistribute it and/or
9d37a65bbSShawn Guo  * modify it under the terms of the GNU General Public License
10d37a65bbSShawn Guo  * as published by the Free Software Foundation; either version 2
11d37a65bbSShawn Guo  * of the License, or (at your option) any later version.
12d37a65bbSShawn Guo  * This program is distributed in the hope that it will be useful,
13d37a65bbSShawn Guo  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14d37a65bbSShawn Guo  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15d37a65bbSShawn Guo  * GNU General Public License for more details.
16d37a65bbSShawn Guo  *
17d37a65bbSShawn Guo  * You should have received a copy of the GNU General Public License
18d37a65bbSShawn Guo  * along with this program; if not, write to the Free Software
19d37a65bbSShawn Guo  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20d37a65bbSShawn Guo  */
21d37a65bbSShawn Guo 
2218f92b19SFabio Estevam #include <linux/err.h>
23d37a65bbSShawn Guo #include <linux/init.h>
24d37a65bbSShawn Guo #include <linux/interrupt.h>
25d37a65bbSShawn Guo #include <linux/io.h>
26d37a65bbSShawn Guo #include <linux/irq.h>
271ab7ef15SShawn Guo #include <linux/irqdomain.h>
28de88cbb7SCatalin Marinas #include <linux/irqchip/chained_irq.h>
29d37a65bbSShawn Guo #include <linux/gpio.h>
30b78d8e59SShawn Guo #include <linux/platform_device.h>
31b78d8e59SShawn Guo #include <linux/slab.h>
322ce420daSShawn Guo #include <linux/basic_mmio_gpio.h>
338937cb60SShawn Guo #include <linux/of.h>
348937cb60SShawn Guo #include <linux/of_device.h>
35bb207ef1SPaul Gortmaker #include <linux/module.h>
36d37a65bbSShawn Guo #include <asm-generic/bug.h>
37d37a65bbSShawn Guo 
38e7fc6ae7SShawn Guo enum mxc_gpio_hwtype {
39e7fc6ae7SShawn Guo 	IMX1_GPIO,	/* runs on i.mx1 */
40e7fc6ae7SShawn Guo 	IMX21_GPIO,	/* runs on i.mx21 and i.mx27 */
41aeb27748SBenoît Thébaudeau 	IMX31_GPIO,	/* runs on i.mx31 */
42aeb27748SBenoît Thébaudeau 	IMX35_GPIO,	/* runs on all other i.mx */
43e7fc6ae7SShawn Guo };
44e7fc6ae7SShawn Guo 
45e7fc6ae7SShawn Guo /* device type dependent stuff */
46e7fc6ae7SShawn Guo struct mxc_gpio_hwdata {
47e7fc6ae7SShawn Guo 	unsigned dr_reg;
48e7fc6ae7SShawn Guo 	unsigned gdir_reg;
49e7fc6ae7SShawn Guo 	unsigned psr_reg;
50e7fc6ae7SShawn Guo 	unsigned icr1_reg;
51e7fc6ae7SShawn Guo 	unsigned icr2_reg;
52e7fc6ae7SShawn Guo 	unsigned imr_reg;
53e7fc6ae7SShawn Guo 	unsigned isr_reg;
54aeb27748SBenoît Thébaudeau 	int edge_sel_reg;
55e7fc6ae7SShawn Guo 	unsigned low_level;
56e7fc6ae7SShawn Guo 	unsigned high_level;
57e7fc6ae7SShawn Guo 	unsigned rise_edge;
58e7fc6ae7SShawn Guo 	unsigned fall_edge;
59e7fc6ae7SShawn Guo };
60e7fc6ae7SShawn Guo 
61b78d8e59SShawn Guo struct mxc_gpio_port {
62b78d8e59SShawn Guo 	struct list_head node;
63b78d8e59SShawn Guo 	void __iomem *base;
64b78d8e59SShawn Guo 	int irq;
65b78d8e59SShawn Guo 	int irq_high;
661ab7ef15SShawn Guo 	struct irq_domain *domain;
672ce420daSShawn Guo 	struct bgpio_chip bgc;
68b78d8e59SShawn Guo 	u32 both_edges;
69b78d8e59SShawn Guo };
70b78d8e59SShawn Guo 
71e7fc6ae7SShawn Guo static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
72e7fc6ae7SShawn Guo 	.dr_reg		= 0x1c,
73e7fc6ae7SShawn Guo 	.gdir_reg	= 0x00,
74e7fc6ae7SShawn Guo 	.psr_reg	= 0x24,
75e7fc6ae7SShawn Guo 	.icr1_reg	= 0x28,
76e7fc6ae7SShawn Guo 	.icr2_reg	= 0x2c,
77e7fc6ae7SShawn Guo 	.imr_reg	= 0x30,
78e7fc6ae7SShawn Guo 	.isr_reg	= 0x34,
79aeb27748SBenoît Thébaudeau 	.edge_sel_reg	= -EINVAL,
80e7fc6ae7SShawn Guo 	.low_level	= 0x03,
81e7fc6ae7SShawn Guo 	.high_level	= 0x02,
82e7fc6ae7SShawn Guo 	.rise_edge	= 0x00,
83e7fc6ae7SShawn Guo 	.fall_edge	= 0x01,
84e7fc6ae7SShawn Guo };
85e7fc6ae7SShawn Guo 
86e7fc6ae7SShawn Guo static struct mxc_gpio_hwdata imx31_gpio_hwdata = {
87e7fc6ae7SShawn Guo 	.dr_reg		= 0x00,
88e7fc6ae7SShawn Guo 	.gdir_reg	= 0x04,
89e7fc6ae7SShawn Guo 	.psr_reg	= 0x08,
90e7fc6ae7SShawn Guo 	.icr1_reg	= 0x0c,
91e7fc6ae7SShawn Guo 	.icr2_reg	= 0x10,
92e7fc6ae7SShawn Guo 	.imr_reg	= 0x14,
93e7fc6ae7SShawn Guo 	.isr_reg	= 0x18,
94aeb27748SBenoît Thébaudeau 	.edge_sel_reg	= -EINVAL,
95aeb27748SBenoît Thébaudeau 	.low_level	= 0x00,
96aeb27748SBenoît Thébaudeau 	.high_level	= 0x01,
97aeb27748SBenoît Thébaudeau 	.rise_edge	= 0x02,
98aeb27748SBenoît Thébaudeau 	.fall_edge	= 0x03,
99aeb27748SBenoît Thébaudeau };
100aeb27748SBenoît Thébaudeau 
101aeb27748SBenoît Thébaudeau static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
102aeb27748SBenoît Thébaudeau 	.dr_reg		= 0x00,
103aeb27748SBenoît Thébaudeau 	.gdir_reg	= 0x04,
104aeb27748SBenoît Thébaudeau 	.psr_reg	= 0x08,
105aeb27748SBenoît Thébaudeau 	.icr1_reg	= 0x0c,
106aeb27748SBenoît Thébaudeau 	.icr2_reg	= 0x10,
107aeb27748SBenoît Thébaudeau 	.imr_reg	= 0x14,
108aeb27748SBenoît Thébaudeau 	.isr_reg	= 0x18,
109aeb27748SBenoît Thébaudeau 	.edge_sel_reg	= 0x1c,
110e7fc6ae7SShawn Guo 	.low_level	= 0x00,
111e7fc6ae7SShawn Guo 	.high_level	= 0x01,
112e7fc6ae7SShawn Guo 	.rise_edge	= 0x02,
113e7fc6ae7SShawn Guo 	.fall_edge	= 0x03,
114e7fc6ae7SShawn Guo };
115e7fc6ae7SShawn Guo 
116e7fc6ae7SShawn Guo static enum mxc_gpio_hwtype mxc_gpio_hwtype;
117e7fc6ae7SShawn Guo static struct mxc_gpio_hwdata *mxc_gpio_hwdata;
118e7fc6ae7SShawn Guo 
119e7fc6ae7SShawn Guo #define GPIO_DR			(mxc_gpio_hwdata->dr_reg)
120e7fc6ae7SShawn Guo #define GPIO_GDIR		(mxc_gpio_hwdata->gdir_reg)
121e7fc6ae7SShawn Guo #define GPIO_PSR		(mxc_gpio_hwdata->psr_reg)
122e7fc6ae7SShawn Guo #define GPIO_ICR1		(mxc_gpio_hwdata->icr1_reg)
123e7fc6ae7SShawn Guo #define GPIO_ICR2		(mxc_gpio_hwdata->icr2_reg)
124e7fc6ae7SShawn Guo #define GPIO_IMR		(mxc_gpio_hwdata->imr_reg)
125e7fc6ae7SShawn Guo #define GPIO_ISR		(mxc_gpio_hwdata->isr_reg)
126aeb27748SBenoît Thébaudeau #define GPIO_EDGE_SEL		(mxc_gpio_hwdata->edge_sel_reg)
127e7fc6ae7SShawn Guo 
128e7fc6ae7SShawn Guo #define GPIO_INT_LOW_LEV	(mxc_gpio_hwdata->low_level)
129e7fc6ae7SShawn Guo #define GPIO_INT_HIGH_LEV	(mxc_gpio_hwdata->high_level)
130e7fc6ae7SShawn Guo #define GPIO_INT_RISE_EDGE	(mxc_gpio_hwdata->rise_edge)
131e7fc6ae7SShawn Guo #define GPIO_INT_FALL_EDGE	(mxc_gpio_hwdata->fall_edge)
132aeb27748SBenoît Thébaudeau #define GPIO_INT_BOTH_EDGES	0x4
133e7fc6ae7SShawn Guo 
134e7fc6ae7SShawn Guo static struct platform_device_id mxc_gpio_devtype[] = {
135e7fc6ae7SShawn Guo 	{
136e7fc6ae7SShawn Guo 		.name = "imx1-gpio",
137e7fc6ae7SShawn Guo 		.driver_data = IMX1_GPIO,
138e7fc6ae7SShawn Guo 	}, {
139e7fc6ae7SShawn Guo 		.name = "imx21-gpio",
140e7fc6ae7SShawn Guo 		.driver_data = IMX21_GPIO,
141e7fc6ae7SShawn Guo 	}, {
142e7fc6ae7SShawn Guo 		.name = "imx31-gpio",
143e7fc6ae7SShawn Guo 		.driver_data = IMX31_GPIO,
144e7fc6ae7SShawn Guo 	}, {
145aeb27748SBenoît Thébaudeau 		.name = "imx35-gpio",
146aeb27748SBenoît Thébaudeau 		.driver_data = IMX35_GPIO,
147aeb27748SBenoît Thébaudeau 	}, {
148e7fc6ae7SShawn Guo 		/* sentinel */
149e7fc6ae7SShawn Guo 	}
150e7fc6ae7SShawn Guo };
151e7fc6ae7SShawn Guo 
1528937cb60SShawn Guo static const struct of_device_id mxc_gpio_dt_ids[] = {
1538937cb60SShawn Guo 	{ .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
1548937cb60SShawn Guo 	{ .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
1558937cb60SShawn Guo 	{ .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
156aeb27748SBenoît Thébaudeau 	{ .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
1578937cb60SShawn Guo 	{ /* sentinel */ }
1588937cb60SShawn Guo };
1598937cb60SShawn Guo 
160b78d8e59SShawn Guo /*
161b78d8e59SShawn Guo  * MX2 has one interrupt *for all* gpio ports. The list is used
162b78d8e59SShawn Guo  * to save the references to all ports, so that mx2_gpio_irq_handler
163b78d8e59SShawn Guo  * can walk through all interrupt status registers.
164b78d8e59SShawn Guo  */
165b78d8e59SShawn Guo static LIST_HEAD(mxc_gpio_ports);
166d37a65bbSShawn Guo 
167d37a65bbSShawn Guo /* Note: This driver assumes 32 GPIOs are handled in one register */
168d37a65bbSShawn Guo 
169d37a65bbSShawn Guo static int gpio_set_irq_type(struct irq_data *d, u32 type)
170d37a65bbSShawn Guo {
171e4ea9333SShawn Guo 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
172e4ea9333SShawn Guo 	struct mxc_gpio_port *port = gc->private;
173d37a65bbSShawn Guo 	u32 bit, val;
1741ab7ef15SShawn Guo 	u32 gpio_idx = d->hwirq;
1751ab7ef15SShawn Guo 	u32 gpio = port->bgc.gc.base + gpio_idx;
176d37a65bbSShawn Guo 	int edge;
177d37a65bbSShawn Guo 	void __iomem *reg = port->base;
178d37a65bbSShawn Guo 
1791ab7ef15SShawn Guo 	port->both_edges &= ~(1 << gpio_idx);
180d37a65bbSShawn Guo 	switch (type) {
181d37a65bbSShawn Guo 	case IRQ_TYPE_EDGE_RISING:
182d37a65bbSShawn Guo 		edge = GPIO_INT_RISE_EDGE;
183d37a65bbSShawn Guo 		break;
184d37a65bbSShawn Guo 	case IRQ_TYPE_EDGE_FALLING:
185d37a65bbSShawn Guo 		edge = GPIO_INT_FALL_EDGE;
186d37a65bbSShawn Guo 		break;
187d37a65bbSShawn Guo 	case IRQ_TYPE_EDGE_BOTH:
188aeb27748SBenoît Thébaudeau 		if (GPIO_EDGE_SEL >= 0) {
189aeb27748SBenoît Thébaudeau 			edge = GPIO_INT_BOTH_EDGES;
190aeb27748SBenoît Thébaudeau 		} else {
1915523f86bSShawn Guo 			val = gpio_get_value(gpio);
192d37a65bbSShawn Guo 			if (val) {
193d37a65bbSShawn Guo 				edge = GPIO_INT_LOW_LEV;
194d37a65bbSShawn Guo 				pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
195d37a65bbSShawn Guo 			} else {
196d37a65bbSShawn Guo 				edge = GPIO_INT_HIGH_LEV;
197d37a65bbSShawn Guo 				pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
198d37a65bbSShawn Guo 			}
1991ab7ef15SShawn Guo 			port->both_edges |= 1 << gpio_idx;
200aeb27748SBenoît Thébaudeau 		}
201d37a65bbSShawn Guo 		break;
202d37a65bbSShawn Guo 	case IRQ_TYPE_LEVEL_LOW:
203d37a65bbSShawn Guo 		edge = GPIO_INT_LOW_LEV;
204d37a65bbSShawn Guo 		break;
205d37a65bbSShawn Guo 	case IRQ_TYPE_LEVEL_HIGH:
206d37a65bbSShawn Guo 		edge = GPIO_INT_HIGH_LEV;
207d37a65bbSShawn Guo 		break;
208d37a65bbSShawn Guo 	default:
209d37a65bbSShawn Guo 		return -EINVAL;
210d37a65bbSShawn Guo 	}
211d37a65bbSShawn Guo 
212aeb27748SBenoît Thébaudeau 	if (GPIO_EDGE_SEL >= 0) {
213aeb27748SBenoît Thébaudeau 		val = readl(port->base + GPIO_EDGE_SEL);
214aeb27748SBenoît Thébaudeau 		if (edge == GPIO_INT_BOTH_EDGES)
215f948ad07SLinus Torvalds 			writel(val | (1 << gpio_idx),
216aeb27748SBenoît Thébaudeau 				port->base + GPIO_EDGE_SEL);
217aeb27748SBenoît Thébaudeau 		else
218f948ad07SLinus Torvalds 			writel(val & ~(1 << gpio_idx),
219aeb27748SBenoît Thébaudeau 				port->base + GPIO_EDGE_SEL);
220aeb27748SBenoît Thébaudeau 	}
221aeb27748SBenoît Thébaudeau 
222aeb27748SBenoît Thébaudeau 	if (edge != GPIO_INT_BOTH_EDGES) {
223f948ad07SLinus Torvalds 		reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
2241ab7ef15SShawn Guo 		bit = gpio_idx & 0xf;
225b78d8e59SShawn Guo 		val = readl(reg) & ~(0x3 << (bit << 1));
226b78d8e59SShawn Guo 		writel(val | (edge << (bit << 1)), reg);
227aeb27748SBenoît Thébaudeau 	}
228aeb27748SBenoît Thébaudeau 
2291ab7ef15SShawn Guo 	writel(1 << gpio_idx, port->base + GPIO_ISR);
230d37a65bbSShawn Guo 
231d37a65bbSShawn Guo 	return 0;
232d37a65bbSShawn Guo }
233d37a65bbSShawn Guo 
234d37a65bbSShawn Guo static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
235d37a65bbSShawn Guo {
236d37a65bbSShawn Guo 	void __iomem *reg = port->base;
237d37a65bbSShawn Guo 	u32 bit, val;
238d37a65bbSShawn Guo 	int edge;
239d37a65bbSShawn Guo 
240d37a65bbSShawn Guo 	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
241d37a65bbSShawn Guo 	bit = gpio & 0xf;
242b78d8e59SShawn Guo 	val = readl(reg);
243d37a65bbSShawn Guo 	edge = (val >> (bit << 1)) & 3;
244d37a65bbSShawn Guo 	val &= ~(0x3 << (bit << 1));
245d37a65bbSShawn Guo 	if (edge == GPIO_INT_HIGH_LEV) {
246d37a65bbSShawn Guo 		edge = GPIO_INT_LOW_LEV;
247d37a65bbSShawn Guo 		pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
248d37a65bbSShawn Guo 	} else if (edge == GPIO_INT_LOW_LEV) {
249d37a65bbSShawn Guo 		edge = GPIO_INT_HIGH_LEV;
250d37a65bbSShawn Guo 		pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
251d37a65bbSShawn Guo 	} else {
252d37a65bbSShawn Guo 		pr_err("mxc: invalid configuration for GPIO %d: %x\n",
253d37a65bbSShawn Guo 		       gpio, edge);
254d37a65bbSShawn Guo 		return;
255d37a65bbSShawn Guo 	}
256b78d8e59SShawn Guo 	writel(val | (edge << (bit << 1)), reg);
257d37a65bbSShawn Guo }
258d37a65bbSShawn Guo 
259d37a65bbSShawn Guo /* handle 32 interrupts in one status register */
260d37a65bbSShawn Guo static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
261d37a65bbSShawn Guo {
262d37a65bbSShawn Guo 	while (irq_stat != 0) {
263d37a65bbSShawn Guo 		int irqoffset = fls(irq_stat) - 1;
264d37a65bbSShawn Guo 
265d37a65bbSShawn Guo 		if (port->both_edges & (1 << irqoffset))
266d37a65bbSShawn Guo 			mxc_flip_edge(port, irqoffset);
267d37a65bbSShawn Guo 
2681ab7ef15SShawn Guo 		generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
269d37a65bbSShawn Guo 
270d37a65bbSShawn Guo 		irq_stat &= ~(1 << irqoffset);
271d37a65bbSShawn Guo 	}
272d37a65bbSShawn Guo }
273d37a65bbSShawn Guo 
274d37a65bbSShawn Guo /* MX1 and MX3 has one interrupt *per* gpio port */
275d37a65bbSShawn Guo static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
276d37a65bbSShawn Guo {
277d37a65bbSShawn Guo 	u32 irq_stat;
278d37a65bbSShawn Guo 	struct mxc_gpio_port *port = irq_get_handler_data(irq);
2790e44b6ecSShawn Guo 	struct irq_chip *chip = irq_get_chip(irq);
2800e44b6ecSShawn Guo 
2810e44b6ecSShawn Guo 	chained_irq_enter(chip, desc);
282d37a65bbSShawn Guo 
283b78d8e59SShawn Guo 	irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
284d37a65bbSShawn Guo 
285d37a65bbSShawn Guo 	mxc_gpio_irq_handler(port, irq_stat);
2860e44b6ecSShawn Guo 
2870e44b6ecSShawn Guo 	chained_irq_exit(chip, desc);
288d37a65bbSShawn Guo }
289d37a65bbSShawn Guo 
290d37a65bbSShawn Guo /* MX2 has one interrupt *for all* gpio ports */
291d37a65bbSShawn Guo static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
292d37a65bbSShawn Guo {
293d37a65bbSShawn Guo 	u32 irq_msk, irq_stat;
294b78d8e59SShawn Guo 	struct mxc_gpio_port *port;
295c0e811d9SUwe Kleine-König 	struct irq_chip *chip = irq_get_chip(irq);
296c0e811d9SUwe Kleine-König 
297c0e811d9SUwe Kleine-König 	chained_irq_enter(chip, desc);
298d37a65bbSShawn Guo 
299d37a65bbSShawn Guo 	/* walk through all interrupt status registers */
300b78d8e59SShawn Guo 	list_for_each_entry(port, &mxc_gpio_ports, node) {
301b78d8e59SShawn Guo 		irq_msk = readl(port->base + GPIO_IMR);
302d37a65bbSShawn Guo 		if (!irq_msk)
303d37a65bbSShawn Guo 			continue;
304d37a65bbSShawn Guo 
305b78d8e59SShawn Guo 		irq_stat = readl(port->base + GPIO_ISR) & irq_msk;
306d37a65bbSShawn Guo 		if (irq_stat)
307b78d8e59SShawn Guo 			mxc_gpio_irq_handler(port, irq_stat);
308d37a65bbSShawn Guo 	}
309c0e811d9SUwe Kleine-König 	chained_irq_exit(chip, desc);
310d37a65bbSShawn Guo }
311d37a65bbSShawn Guo 
312d37a65bbSShawn Guo /*
313d37a65bbSShawn Guo  * Set interrupt number "irq" in the GPIO as a wake-up source.
314d37a65bbSShawn Guo  * While system is running, all registered GPIO interrupts need to have
315d37a65bbSShawn Guo  * wake-up enabled. When system is suspended, only selected GPIO interrupts
316d37a65bbSShawn Guo  * need to have wake-up enabled.
317d37a65bbSShawn Guo  * @param  irq          interrupt source number
318d37a65bbSShawn Guo  * @param  enable       enable as wake-up if equal to non-zero
319d37a65bbSShawn Guo  * @return       This function returns 0 on success.
320d37a65bbSShawn Guo  */
321d37a65bbSShawn Guo static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
322d37a65bbSShawn Guo {
323e4ea9333SShawn Guo 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
324e4ea9333SShawn Guo 	struct mxc_gpio_port *port = gc->private;
3251ab7ef15SShawn Guo 	u32 gpio_idx = d->hwirq;
326d37a65bbSShawn Guo 
327d37a65bbSShawn Guo 	if (enable) {
328d37a65bbSShawn Guo 		if (port->irq_high && (gpio_idx >= 16))
329d37a65bbSShawn Guo 			enable_irq_wake(port->irq_high);
330d37a65bbSShawn Guo 		else
331d37a65bbSShawn Guo 			enable_irq_wake(port->irq);
332d37a65bbSShawn Guo 	} else {
333d37a65bbSShawn Guo 		if (port->irq_high && (gpio_idx >= 16))
334d37a65bbSShawn Guo 			disable_irq_wake(port->irq_high);
335d37a65bbSShawn Guo 		else
336d37a65bbSShawn Guo 			disable_irq_wake(port->irq);
337d37a65bbSShawn Guo 	}
338d37a65bbSShawn Guo 
339d37a65bbSShawn Guo 	return 0;
340d37a65bbSShawn Guo }
341d37a65bbSShawn Guo 
3421ab7ef15SShawn Guo static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
343e4ea9333SShawn Guo {
344e4ea9333SShawn Guo 	struct irq_chip_generic *gc;
345e4ea9333SShawn Guo 	struct irq_chip_type *ct;
346d37a65bbSShawn Guo 
3471ab7ef15SShawn Guo 	gc = irq_alloc_generic_chip("gpio-mxc", 1, irq_base,
348e4ea9333SShawn Guo 				    port->base, handle_level_irq);
349e4ea9333SShawn Guo 	gc->private = port;
350e4ea9333SShawn Guo 
351e4ea9333SShawn Guo 	ct = gc->chip_types;
352591567a5SShawn Guo 	ct->chip.irq_ack = irq_gc_ack_set_bit;
353e4ea9333SShawn Guo 	ct->chip.irq_mask = irq_gc_mask_clr_bit;
354e4ea9333SShawn Guo 	ct->chip.irq_unmask = irq_gc_mask_set_bit;
355e4ea9333SShawn Guo 	ct->chip.irq_set_type = gpio_set_irq_type;
356591567a5SShawn Guo 	ct->chip.irq_set_wake = gpio_set_wake_irq;
357e4ea9333SShawn Guo 	ct->regs.ack = GPIO_ISR;
358e4ea9333SShawn Guo 	ct->regs.mask = GPIO_IMR;
359e4ea9333SShawn Guo 
360e4ea9333SShawn Guo 	irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK,
361e4ea9333SShawn Guo 			       IRQ_NOREQUEST, 0);
362e4ea9333SShawn Guo }
363d37a65bbSShawn Guo 
3643836309dSBill Pemberton static void mxc_gpio_get_hw(struct platform_device *pdev)
365e7fc6ae7SShawn Guo {
3668937cb60SShawn Guo 	const struct of_device_id *of_id =
3678937cb60SShawn Guo 			of_match_device(mxc_gpio_dt_ids, &pdev->dev);
3688937cb60SShawn Guo 	enum mxc_gpio_hwtype hwtype;
3698937cb60SShawn Guo 
3708937cb60SShawn Guo 	if (of_id)
3718937cb60SShawn Guo 		pdev->id_entry = of_id->data;
3728937cb60SShawn Guo 	hwtype = pdev->id_entry->driver_data;
373e7fc6ae7SShawn Guo 
374e7fc6ae7SShawn Guo 	if (mxc_gpio_hwtype) {
375e7fc6ae7SShawn Guo 		/*
376e7fc6ae7SShawn Guo 		 * The driver works with a reasonable presupposition,
377e7fc6ae7SShawn Guo 		 * that is all gpio ports must be the same type when
378e7fc6ae7SShawn Guo 		 * running on one soc.
379e7fc6ae7SShawn Guo 		 */
380e7fc6ae7SShawn Guo 		BUG_ON(mxc_gpio_hwtype != hwtype);
381e7fc6ae7SShawn Guo 		return;
382e7fc6ae7SShawn Guo 	}
383e7fc6ae7SShawn Guo 
384aeb27748SBenoît Thébaudeau 	if (hwtype == IMX35_GPIO)
385aeb27748SBenoît Thébaudeau 		mxc_gpio_hwdata = &imx35_gpio_hwdata;
386aeb27748SBenoît Thébaudeau 	else if (hwtype == IMX31_GPIO)
387e7fc6ae7SShawn Guo 		mxc_gpio_hwdata = &imx31_gpio_hwdata;
388e7fc6ae7SShawn Guo 	else
389e7fc6ae7SShawn Guo 		mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;
390e7fc6ae7SShawn Guo 
391e7fc6ae7SShawn Guo 	mxc_gpio_hwtype = hwtype;
392e7fc6ae7SShawn Guo }
393e7fc6ae7SShawn Guo 
39409ad8039SShawn Guo static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
39509ad8039SShawn Guo {
39609ad8039SShawn Guo 	struct bgpio_chip *bgc = to_bgpio_chip(gc);
39709ad8039SShawn Guo 	struct mxc_gpio_port *port =
39809ad8039SShawn Guo 		container_of(bgc, struct mxc_gpio_port, bgc);
39909ad8039SShawn Guo 
4001ab7ef15SShawn Guo 	return irq_find_mapping(port->domain, offset);
40109ad8039SShawn Guo }
40209ad8039SShawn Guo 
4033836309dSBill Pemberton static int mxc_gpio_probe(struct platform_device *pdev)
404d37a65bbSShawn Guo {
4058937cb60SShawn Guo 	struct device_node *np = pdev->dev.of_node;
406b78d8e59SShawn Guo 	struct mxc_gpio_port *port;
407b78d8e59SShawn Guo 	struct resource *iores;
4081ab7ef15SShawn Guo 	int irq_base;
409e4ea9333SShawn Guo 	int err;
410d37a65bbSShawn Guo 
411e7fc6ae7SShawn Guo 	mxc_gpio_get_hw(pdev);
412e7fc6ae7SShawn Guo 
4138cd73e4eSFabio Estevam 	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
414b78d8e59SShawn Guo 	if (!port)
415b78d8e59SShawn Guo 		return -ENOMEM;
416d37a65bbSShawn Guo 
417b78d8e59SShawn Guo 	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4188cd73e4eSFabio Estevam 	port->base = devm_ioremap_resource(&pdev->dev, iores);
4198cd73e4eSFabio Estevam 	if (IS_ERR(port->base))
4208cd73e4eSFabio Estevam 		return PTR_ERR(port->base);
421b78d8e59SShawn Guo 
422b78d8e59SShawn Guo 	port->irq_high = platform_get_irq(pdev, 1);
423b78d8e59SShawn Guo 	port->irq = platform_get_irq(pdev, 0);
4248cd73e4eSFabio Estevam 	if (port->irq < 0)
4255ea80e49SSachin Kamat 		return port->irq;
426b78d8e59SShawn Guo 
427d37a65bbSShawn Guo 	/* disable the interrupt and clear the status */
428b78d8e59SShawn Guo 	writel(0, port->base + GPIO_IMR);
429b78d8e59SShawn Guo 	writel(~0, port->base + GPIO_ISR);
430d37a65bbSShawn Guo 
431e7fc6ae7SShawn Guo 	if (mxc_gpio_hwtype == IMX21_GPIO) {
43233a4e985SUwe Kleine-König 		/*
43333a4e985SUwe Kleine-König 		 * Setup one handler for all GPIO interrupts. Actually setting
43433a4e985SUwe Kleine-König 		 * the handler is needed only once, but doing it for every port
43533a4e985SUwe Kleine-König 		 * is more robust and easier.
43633a4e985SUwe Kleine-König 		 */
43733a4e985SUwe Kleine-König 		irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
438b78d8e59SShawn Guo 	} else {
439b78d8e59SShawn Guo 		/* setup one handler for each entry */
440b78d8e59SShawn Guo 		irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
441b78d8e59SShawn Guo 		irq_set_handler_data(port->irq, port);
442b78d8e59SShawn Guo 		if (port->irq_high > 0) {
443b78d8e59SShawn Guo 			/* setup handler for GPIO 16 to 31 */
444b78d8e59SShawn Guo 			irq_set_chained_handler(port->irq_high,
445b78d8e59SShawn Guo 						mx3_gpio_irq_handler);
446b78d8e59SShawn Guo 			irq_set_handler_data(port->irq_high, port);
447b78d8e59SShawn Guo 		}
448d37a65bbSShawn Guo 	}
449d37a65bbSShawn Guo 
4502ce420daSShawn Guo 	err = bgpio_init(&port->bgc, &pdev->dev, 4,
4512ce420daSShawn Guo 			 port->base + GPIO_PSR,
4522ce420daSShawn Guo 			 port->base + GPIO_DR, NULL,
453442b2494SVladimir Zapolskiy 			 port->base + GPIO_GDIR, NULL,
454442b2494SVladimir Zapolskiy 			 BGPIOF_READ_OUTPUT_REG_SET);
455b78d8e59SShawn Guo 	if (err)
4568cd73e4eSFabio Estevam 		goto out_bgio;
457b78d8e59SShawn Guo 
45809ad8039SShawn Guo 	port->bgc.gc.to_irq = mxc_gpio_to_irq;
4597e6086d9SShawn Guo 	port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
4607e6086d9SShawn Guo 					     pdev->id * 32;
4612ce420daSShawn Guo 
4622ce420daSShawn Guo 	err = gpiochip_add(&port->bgc.gc);
4632ce420daSShawn Guo 	if (err)
4642ce420daSShawn Guo 		goto out_bgpio_remove;
4652ce420daSShawn Guo 
4661ab7ef15SShawn Guo 	irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
4671ab7ef15SShawn Guo 	if (irq_base < 0) {
4681ab7ef15SShawn Guo 		err = irq_base;
4691ab7ef15SShawn Guo 		goto out_gpiochip_remove;
4701ab7ef15SShawn Guo 	}
4711ab7ef15SShawn Guo 
4721ab7ef15SShawn Guo 	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
4731ab7ef15SShawn Guo 					     &irq_domain_simple_ops, NULL);
4741ab7ef15SShawn Guo 	if (!port->domain) {
4751ab7ef15SShawn Guo 		err = -ENODEV;
4761ab7ef15SShawn Guo 		goto out_irqdesc_free;
4771ab7ef15SShawn Guo 	}
4788937cb60SShawn Guo 
4798937cb60SShawn Guo 	/* gpio-mxc can be a generic irq chip */
4801ab7ef15SShawn Guo 	mxc_gpio_init_gc(port, irq_base);
4818937cb60SShawn Guo 
482b78d8e59SShawn Guo 	list_add_tail(&port->node, &mxc_gpio_ports);
483b78d8e59SShawn Guo 
484d37a65bbSShawn Guo 	return 0;
485b78d8e59SShawn Guo 
4861ab7ef15SShawn Guo out_irqdesc_free:
4871ab7ef15SShawn Guo 	irq_free_descs(irq_base, 32);
4881ab7ef15SShawn Guo out_gpiochip_remove:
4899f5132aeSabdoulaye berthe 	gpiochip_remove(&port->bgc.gc);
4902ce420daSShawn Guo out_bgpio_remove:
4912ce420daSShawn Guo 	bgpio_remove(&port->bgc);
4928cd73e4eSFabio Estevam out_bgio:
493b78d8e59SShawn Guo 	dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
494b78d8e59SShawn Guo 	return err;
495d37a65bbSShawn Guo }
496b78d8e59SShawn Guo 
497b78d8e59SShawn Guo static struct platform_driver mxc_gpio_driver = {
498b78d8e59SShawn Guo 	.driver		= {
499b78d8e59SShawn Guo 		.name	= "gpio-mxc",
5008937cb60SShawn Guo 		.of_match_table = mxc_gpio_dt_ids,
501b78d8e59SShawn Guo 	},
502b78d8e59SShawn Guo 	.probe		= mxc_gpio_probe,
503e7fc6ae7SShawn Guo 	.id_table	= mxc_gpio_devtype,
504b78d8e59SShawn Guo };
505b78d8e59SShawn Guo 
506b78d8e59SShawn Guo static int __init gpio_mxc_init(void)
507b78d8e59SShawn Guo {
508b78d8e59SShawn Guo 	return platform_driver_register(&mxc_gpio_driver);
509b78d8e59SShawn Guo }
510b78d8e59SShawn Guo postcore_initcall(gpio_mxc_init);
511b78d8e59SShawn Guo 
512b78d8e59SShawn Guo MODULE_AUTHOR("Freescale Semiconductor, "
513b78d8e59SShawn Guo 	      "Daniel Mack <danielncaiaq.de>, "
514b78d8e59SShawn Guo 	      "Juergen Beisert <kernel@pengutronix.de>");
515b78d8e59SShawn Guo MODULE_DESCRIPTION("Freescale MXC GPIO");
516b78d8e59SShawn Guo MODULE_LICENSE("GPL");
517