xref: /openbmc/linux/drivers/gpio/gpio-mxc.c (revision 9e0fa5d8)
1014e420dSFabio Estevam // SPDX-License-Identifier: GPL-2.0+
2014e420dSFabio Estevam //
3014e420dSFabio Estevam // MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
4014e420dSFabio Estevam // Copyright 2008 Juergen Beisert, kernel@pengutronix.de
5014e420dSFabio Estevam //
6014e420dSFabio Estevam // Based on code from Freescale Semiconductor,
7014e420dSFabio Estevam // Authors: Daniel Mack, Juergen Beisert.
8014e420dSFabio Estevam // Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
9d37a65bbSShawn Guo 
102808801aSAnson Huang #include <linux/clk.h>
1118f92b19SFabio Estevam #include <linux/err.h>
12d37a65bbSShawn Guo #include <linux/init.h>
13d37a65bbSShawn Guo #include <linux/interrupt.h>
14d37a65bbSShawn Guo #include <linux/io.h>
15d37a65bbSShawn Guo #include <linux/irq.h>
161ab7ef15SShawn Guo #include <linux/irqdomain.h>
17de88cbb7SCatalin Marinas #include <linux/irqchip/chained_irq.h>
1812d16b39SAnson Huang #include <linux/module.h>
19b78d8e59SShawn Guo #include <linux/platform_device.h>
203283d820SShenwei Wang #include <linux/pm_runtime.h>
21b78d8e59SShawn Guo #include <linux/slab.h>
22e5464277SMarek Vasut #include <linux/spinlock.h>
231a5287a3SAnson Huang #include <linux/syscore_ops.h>
240f4630f3SLinus Walleij #include <linux/gpio/driver.h>
258937cb60SShawn Guo #include <linux/of.h>
2616c3bd35SChristoph Hellwig #include <linux/bug.h>
27d37a65bbSShawn Guo 
28f60c9eacSShenwei Wang #define IMX_SCU_WAKEUP_OFF		0
29f60c9eacSShenwei Wang #define IMX_SCU_WAKEUP_LOW_LVL		4
30f60c9eacSShenwei Wang #define IMX_SCU_WAKEUP_FALL_EDGE	5
31f60c9eacSShenwei Wang #define IMX_SCU_WAKEUP_RISE_EDGE	6
32f60c9eacSShenwei Wang #define IMX_SCU_WAKEUP_HIGH_LVL		7
33f60c9eacSShenwei Wang 
34e7fc6ae7SShawn Guo /* device type dependent stuff */
35e7fc6ae7SShawn Guo struct mxc_gpio_hwdata {
36e7fc6ae7SShawn Guo 	unsigned dr_reg;
37e7fc6ae7SShawn Guo 	unsigned gdir_reg;
38e7fc6ae7SShawn Guo 	unsigned psr_reg;
39e7fc6ae7SShawn Guo 	unsigned icr1_reg;
40e7fc6ae7SShawn Guo 	unsigned icr2_reg;
41e7fc6ae7SShawn Guo 	unsigned imr_reg;
42e7fc6ae7SShawn Guo 	unsigned isr_reg;
43aeb27748SBenoît Thébaudeau 	int edge_sel_reg;
44e7fc6ae7SShawn Guo 	unsigned low_level;
45e7fc6ae7SShawn Guo 	unsigned high_level;
46e7fc6ae7SShawn Guo 	unsigned rise_edge;
47e7fc6ae7SShawn Guo 	unsigned fall_edge;
48e7fc6ae7SShawn Guo };
49e7fc6ae7SShawn Guo 
50c19fdaeeSAnson Huang struct mxc_gpio_reg_saved {
51c19fdaeeSAnson Huang 	u32 icr1;
52c19fdaeeSAnson Huang 	u32 icr2;
53c19fdaeeSAnson Huang 	u32 imr;
54c19fdaeeSAnson Huang 	u32 gdir;
55c19fdaeeSAnson Huang 	u32 edge_sel;
56c19fdaeeSAnson Huang 	u32 dr;
57c19fdaeeSAnson Huang };
58c19fdaeeSAnson Huang 
59b78d8e59SShawn Guo struct mxc_gpio_port {
60b78d8e59SShawn Guo 	struct list_head node;
61b78d8e59SShawn Guo 	void __iomem *base;
622808801aSAnson Huang 	struct clk *clk;
63b78d8e59SShawn Guo 	int irq;
64b78d8e59SShawn Guo 	int irq_high;
655f6d1998SShenwei Wang 	void (*mx_irq_handler)(struct irq_desc *desc);
661ab7ef15SShawn Guo 	struct irq_domain *domain;
670f4630f3SLinus Walleij 	struct gpio_chip gc;
68db5270acSBartosz Golaszewski 	struct device *dev;
69b78d8e59SShawn Guo 	u32 both_edges;
70c19fdaeeSAnson Huang 	struct mxc_gpio_reg_saved gpio_saved_reg;
71c19fdaeeSAnson Huang 	bool power_off;
72f60c9eacSShenwei Wang 	u32 wakeup_pads;
73f60c9eacSShenwei Wang 	bool is_pad_wakeup;
74f60c9eacSShenwei Wang 	u32 pad_type[32];
750f2c7af4SFabio Estevam 	const struct mxc_gpio_hwdata *hwdata;
76b78d8e59SShawn Guo };
77b78d8e59SShawn Guo 
78e7fc6ae7SShawn Guo static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
79e7fc6ae7SShawn Guo 	.dr_reg		= 0x1c,
80e7fc6ae7SShawn Guo 	.gdir_reg	= 0x00,
81e7fc6ae7SShawn Guo 	.psr_reg	= 0x24,
82e7fc6ae7SShawn Guo 	.icr1_reg	= 0x28,
83e7fc6ae7SShawn Guo 	.icr2_reg	= 0x2c,
84e7fc6ae7SShawn Guo 	.imr_reg	= 0x30,
85e7fc6ae7SShawn Guo 	.isr_reg	= 0x34,
86aeb27748SBenoît Thébaudeau 	.edge_sel_reg	= -EINVAL,
87e7fc6ae7SShawn Guo 	.low_level	= 0x03,
88e7fc6ae7SShawn Guo 	.high_level	= 0x02,
89e7fc6ae7SShawn Guo 	.rise_edge	= 0x00,
90e7fc6ae7SShawn Guo 	.fall_edge	= 0x01,
91e7fc6ae7SShawn Guo };
92e7fc6ae7SShawn Guo 
93e7fc6ae7SShawn Guo static struct mxc_gpio_hwdata imx31_gpio_hwdata = {
94e7fc6ae7SShawn Guo 	.dr_reg		= 0x00,
95e7fc6ae7SShawn Guo 	.gdir_reg	= 0x04,
96e7fc6ae7SShawn Guo 	.psr_reg	= 0x08,
97e7fc6ae7SShawn Guo 	.icr1_reg	= 0x0c,
98e7fc6ae7SShawn Guo 	.icr2_reg	= 0x10,
99e7fc6ae7SShawn Guo 	.imr_reg	= 0x14,
100e7fc6ae7SShawn Guo 	.isr_reg	= 0x18,
101aeb27748SBenoît Thébaudeau 	.edge_sel_reg	= -EINVAL,
102aeb27748SBenoît Thébaudeau 	.low_level	= 0x00,
103aeb27748SBenoît Thébaudeau 	.high_level	= 0x01,
104aeb27748SBenoît Thébaudeau 	.rise_edge	= 0x02,
105aeb27748SBenoît Thébaudeau 	.fall_edge	= 0x03,
106aeb27748SBenoît Thébaudeau };
107aeb27748SBenoît Thébaudeau 
108aeb27748SBenoît Thébaudeau static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
109aeb27748SBenoît Thébaudeau 	.dr_reg		= 0x00,
110aeb27748SBenoît Thébaudeau 	.gdir_reg	= 0x04,
111aeb27748SBenoît Thébaudeau 	.psr_reg	= 0x08,
112aeb27748SBenoît Thébaudeau 	.icr1_reg	= 0x0c,
113aeb27748SBenoît Thébaudeau 	.icr2_reg	= 0x10,
114aeb27748SBenoît Thébaudeau 	.imr_reg	= 0x14,
115aeb27748SBenoît Thébaudeau 	.isr_reg	= 0x18,
116aeb27748SBenoît Thébaudeau 	.edge_sel_reg	= 0x1c,
117e7fc6ae7SShawn Guo 	.low_level	= 0x00,
118e7fc6ae7SShawn Guo 	.high_level	= 0x01,
119e7fc6ae7SShawn Guo 	.rise_edge	= 0x02,
120e7fc6ae7SShawn Guo 	.fall_edge	= 0x03,
121e7fc6ae7SShawn Guo };
122e7fc6ae7SShawn Guo 
1230f2c7af4SFabio Estevam #define GPIO_DR			(port->hwdata->dr_reg)
1240f2c7af4SFabio Estevam #define GPIO_GDIR		(port->hwdata->gdir_reg)
1250f2c7af4SFabio Estevam #define GPIO_PSR		(port->hwdata->psr_reg)
1260f2c7af4SFabio Estevam #define GPIO_ICR1		(port->hwdata->icr1_reg)
1270f2c7af4SFabio Estevam #define GPIO_ICR2		(port->hwdata->icr2_reg)
1280f2c7af4SFabio Estevam #define GPIO_IMR		(port->hwdata->imr_reg)
1290f2c7af4SFabio Estevam #define GPIO_ISR		(port->hwdata->isr_reg)
1300f2c7af4SFabio Estevam #define GPIO_EDGE_SEL		(port->hwdata->edge_sel_reg)
131e7fc6ae7SShawn Guo 
1320f2c7af4SFabio Estevam #define GPIO_INT_LOW_LEV	(port->hwdata->low_level)
1330f2c7af4SFabio Estevam #define GPIO_INT_HIGH_LEV	(port->hwdata->high_level)
1340f2c7af4SFabio Estevam #define GPIO_INT_RISE_EDGE	(port->hwdata->rise_edge)
1350f2c7af4SFabio Estevam #define GPIO_INT_FALL_EDGE	(port->hwdata->fall_edge)
136aeb27748SBenoît Thébaudeau #define GPIO_INT_BOTH_EDGES	0x4
137e7fc6ae7SShawn Guo 
1388937cb60SShawn Guo static const struct of_device_id mxc_gpio_dt_ids[] = {
1390f2c7af4SFabio Estevam 	{ .compatible = "fsl,imx1-gpio", .data =  &imx1_imx21_gpio_hwdata },
1400f2c7af4SFabio Estevam 	{ .compatible = "fsl,imx21-gpio", .data = &imx1_imx21_gpio_hwdata },
1410f2c7af4SFabio Estevam 	{ .compatible = "fsl,imx31-gpio", .data = &imx31_gpio_hwdata },
1420f2c7af4SFabio Estevam 	{ .compatible = "fsl,imx35-gpio", .data = &imx35_gpio_hwdata },
1430f2c7af4SFabio Estevam 	{ .compatible = "fsl,imx7d-gpio", .data = &imx35_gpio_hwdata },
144f60c9eacSShenwei Wang 	{ .compatible = "fsl,imx8dxl-gpio", .data = &imx35_gpio_hwdata },
145f60c9eacSShenwei Wang 	{ .compatible = "fsl,imx8qm-gpio", .data = &imx35_gpio_hwdata },
146f60c9eacSShenwei Wang 	{ .compatible = "fsl,imx8qxp-gpio", .data = &imx35_gpio_hwdata },
1478937cb60SShawn Guo 	{ /* sentinel */ }
1488937cb60SShawn Guo };
14912d16b39SAnson Huang MODULE_DEVICE_TABLE(of, mxc_gpio_dt_ids);
1508937cb60SShawn Guo 
151b78d8e59SShawn Guo /*
152b78d8e59SShawn Guo  * MX2 has one interrupt *for all* gpio ports. The list is used
153b78d8e59SShawn Guo  * to save the references to all ports, so that mx2_gpio_irq_handler
154b78d8e59SShawn Guo  * can walk through all interrupt status registers.
155b78d8e59SShawn Guo  */
156b78d8e59SShawn Guo static LIST_HEAD(mxc_gpio_ports);
157d37a65bbSShawn Guo 
158d37a65bbSShawn Guo /* Note: This driver assumes 32 GPIOs are handled in one register */
159d37a65bbSShawn Guo 
gpio_set_irq_type(struct irq_data * d,u32 type)160d37a65bbSShawn Guo static int gpio_set_irq_type(struct irq_data *d, u32 type)
161d37a65bbSShawn Guo {
162e4ea9333SShawn Guo 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
163e4ea9333SShawn Guo 	struct mxc_gpio_port *port = gc->private;
164e5464277SMarek Vasut 	unsigned long flags;
165d37a65bbSShawn Guo 	u32 bit, val;
1661ab7ef15SShawn Guo 	u32 gpio_idx = d->hwirq;
167d37a65bbSShawn Guo 	int edge;
168d37a65bbSShawn Guo 	void __iomem *reg = port->base;
169d37a65bbSShawn Guo 
1701ab7ef15SShawn Guo 	port->both_edges &= ~(1 << gpio_idx);
171d37a65bbSShawn Guo 	switch (type) {
172d37a65bbSShawn Guo 	case IRQ_TYPE_EDGE_RISING:
173d37a65bbSShawn Guo 		edge = GPIO_INT_RISE_EDGE;
174d37a65bbSShawn Guo 		break;
175d37a65bbSShawn Guo 	case IRQ_TYPE_EDGE_FALLING:
176d37a65bbSShawn Guo 		edge = GPIO_INT_FALL_EDGE;
177d37a65bbSShawn Guo 		break;
178d37a65bbSShawn Guo 	case IRQ_TYPE_EDGE_BOTH:
179aeb27748SBenoît Thébaudeau 		if (GPIO_EDGE_SEL >= 0) {
180aeb27748SBenoît Thébaudeau 			edge = GPIO_INT_BOTH_EDGES;
181aeb27748SBenoît Thébaudeau 		} else {
1828d0bd9a5SLinus Walleij 			val = port->gc.get(&port->gc, gpio_idx);
183d37a65bbSShawn Guo 			if (val) {
184d37a65bbSShawn Guo 				edge = GPIO_INT_LOW_LEV;
1858d0bd9a5SLinus Walleij 				pr_debug("mxc: set GPIO %d to low trigger\n", gpio_idx);
186d37a65bbSShawn Guo 			} else {
187d37a65bbSShawn Guo 				edge = GPIO_INT_HIGH_LEV;
1888d0bd9a5SLinus Walleij 				pr_debug("mxc: set GPIO %d to high trigger\n", gpio_idx);
189d37a65bbSShawn Guo 			}
1901ab7ef15SShawn Guo 			port->both_edges |= 1 << gpio_idx;
191aeb27748SBenoît Thébaudeau 		}
192d37a65bbSShawn Guo 		break;
193d37a65bbSShawn Guo 	case IRQ_TYPE_LEVEL_LOW:
194d37a65bbSShawn Guo 		edge = GPIO_INT_LOW_LEV;
195d37a65bbSShawn Guo 		break;
196d37a65bbSShawn Guo 	case IRQ_TYPE_LEVEL_HIGH:
197d37a65bbSShawn Guo 		edge = GPIO_INT_HIGH_LEV;
198d37a65bbSShawn Guo 		break;
199d37a65bbSShawn Guo 	default:
200d37a65bbSShawn Guo 		return -EINVAL;
201d37a65bbSShawn Guo 	}
202d37a65bbSShawn Guo 
203e5464277SMarek Vasut 	raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags);
204e5464277SMarek Vasut 
205aeb27748SBenoît Thébaudeau 	if (GPIO_EDGE_SEL >= 0) {
206aeb27748SBenoît Thébaudeau 		val = readl(port->base + GPIO_EDGE_SEL);
207aeb27748SBenoît Thébaudeau 		if (edge == GPIO_INT_BOTH_EDGES)
208f948ad07SLinus Torvalds 			writel(val | (1 << gpio_idx),
209aeb27748SBenoît Thébaudeau 				port->base + GPIO_EDGE_SEL);
210aeb27748SBenoît Thébaudeau 		else
211f948ad07SLinus Torvalds 			writel(val & ~(1 << gpio_idx),
212aeb27748SBenoît Thébaudeau 				port->base + GPIO_EDGE_SEL);
213aeb27748SBenoît Thébaudeau 	}
214aeb27748SBenoît Thébaudeau 
215aeb27748SBenoît Thébaudeau 	if (edge != GPIO_INT_BOTH_EDGES) {
216f948ad07SLinus Torvalds 		reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
2171ab7ef15SShawn Guo 		bit = gpio_idx & 0xf;
218b78d8e59SShawn Guo 		val = readl(reg) & ~(0x3 << (bit << 1));
219b78d8e59SShawn Guo 		writel(val | (edge << (bit << 1)), reg);
220aeb27748SBenoît Thébaudeau 	}
221aeb27748SBenoît Thébaudeau 
2221ab7ef15SShawn Guo 	writel(1 << gpio_idx, port->base + GPIO_ISR);
223f60c9eacSShenwei Wang 	port->pad_type[gpio_idx] = type;
224d37a65bbSShawn Guo 
225e5464277SMarek Vasut 	raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
226e5464277SMarek Vasut 
2278e88a0feSMarek Vasut 	return port->gc.direction_input(&port->gc, gpio_idx);
228d37a65bbSShawn Guo }
229d37a65bbSShawn Guo 
mxc_flip_edge(struct mxc_gpio_port * port,u32 gpio)230d37a65bbSShawn Guo static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
231d37a65bbSShawn Guo {
232d37a65bbSShawn Guo 	void __iomem *reg = port->base;
233e5464277SMarek Vasut 	unsigned long flags;
234d37a65bbSShawn Guo 	u32 bit, val;
235d37a65bbSShawn Guo 	int edge;
236d37a65bbSShawn Guo 
237e5464277SMarek Vasut 	raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags);
238e5464277SMarek Vasut 
239d37a65bbSShawn Guo 	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
240d37a65bbSShawn Guo 	bit = gpio & 0xf;
241b78d8e59SShawn Guo 	val = readl(reg);
242d37a65bbSShawn Guo 	edge = (val >> (bit << 1)) & 3;
243d37a65bbSShawn Guo 	val &= ~(0x3 << (bit << 1));
244d37a65bbSShawn Guo 	if (edge == GPIO_INT_HIGH_LEV) {
245d37a65bbSShawn Guo 		edge = GPIO_INT_LOW_LEV;
246d37a65bbSShawn Guo 		pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
247d37a65bbSShawn Guo 	} else if (edge == GPIO_INT_LOW_LEV) {
248d37a65bbSShawn Guo 		edge = GPIO_INT_HIGH_LEV;
249d37a65bbSShawn Guo 		pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
250d37a65bbSShawn Guo 	} else {
251d37a65bbSShawn Guo 		pr_err("mxc: invalid configuration for GPIO %d: %x\n",
252d37a65bbSShawn Guo 		       gpio, edge);
25337870358SDan Carpenter 		goto unlock;
254d37a65bbSShawn Guo 	}
255b78d8e59SShawn Guo 	writel(val | (edge << (bit << 1)), reg);
256e5464277SMarek Vasut 
25737870358SDan Carpenter unlock:
258e5464277SMarek Vasut 	raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
259d37a65bbSShawn Guo }
260d37a65bbSShawn Guo 
261d37a65bbSShawn Guo /* handle 32 interrupts in one status register */
mxc_gpio_irq_handler(struct mxc_gpio_port * port,u32 irq_stat)262d37a65bbSShawn Guo static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
263d37a65bbSShawn Guo {
264d37a65bbSShawn Guo 	while (irq_stat != 0) {
265d37a65bbSShawn Guo 		int irqoffset = fls(irq_stat) - 1;
266d37a65bbSShawn Guo 
267d37a65bbSShawn Guo 		if (port->both_edges & (1 << irqoffset))
268d37a65bbSShawn Guo 			mxc_flip_edge(port, irqoffset);
269d37a65bbSShawn Guo 
270dbd1c54fSMarc Zyngier 		generic_handle_domain_irq(port->domain, irqoffset);
271d37a65bbSShawn Guo 
272d37a65bbSShawn Guo 		irq_stat &= ~(1 << irqoffset);
273d37a65bbSShawn Guo 	}
274d37a65bbSShawn Guo }
275d37a65bbSShawn Guo 
276d37a65bbSShawn Guo /* MX1 and MX3 has one interrupt *per* gpio port */
mx3_gpio_irq_handler(struct irq_desc * desc)277bd0b9ac4SThomas Gleixner static void mx3_gpio_irq_handler(struct irq_desc *desc)
278d37a65bbSShawn Guo {
279d37a65bbSShawn Guo 	u32 irq_stat;
280476f8b4cSJiang Liu 	struct mxc_gpio_port *port = irq_desc_get_handler_data(desc);
281476f8b4cSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
2820e44b6ecSShawn Guo 
283f60c9eacSShenwei Wang 	if (port->is_pad_wakeup)
284f60c9eacSShenwei Wang 		return;
285f60c9eacSShenwei Wang 
2860e44b6ecSShawn Guo 	chained_irq_enter(chip, desc);
287d37a65bbSShawn Guo 
288b78d8e59SShawn Guo 	irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
289d37a65bbSShawn Guo 
290d37a65bbSShawn Guo 	mxc_gpio_irq_handler(port, irq_stat);
2910e44b6ecSShawn Guo 
2920e44b6ecSShawn Guo 	chained_irq_exit(chip, desc);
293d37a65bbSShawn Guo }
294d37a65bbSShawn Guo 
295d37a65bbSShawn Guo /* MX2 has one interrupt *for all* gpio ports */
mx2_gpio_irq_handler(struct irq_desc * desc)296bd0b9ac4SThomas Gleixner static void mx2_gpio_irq_handler(struct irq_desc *desc)
297d37a65bbSShawn Guo {
298d37a65bbSShawn Guo 	u32 irq_msk, irq_stat;
299b78d8e59SShawn Guo 	struct mxc_gpio_port *port;
300476f8b4cSJiang Liu 	struct irq_chip *chip = irq_desc_get_chip(desc);
301c0e811d9SUwe Kleine-König 
302c0e811d9SUwe Kleine-König 	chained_irq_enter(chip, desc);
303d37a65bbSShawn Guo 
304d37a65bbSShawn Guo 	/* walk through all interrupt status registers */
305b78d8e59SShawn Guo 	list_for_each_entry(port, &mxc_gpio_ports, node) {
306b78d8e59SShawn Guo 		irq_msk = readl(port->base + GPIO_IMR);
307d37a65bbSShawn Guo 		if (!irq_msk)
308d37a65bbSShawn Guo 			continue;
309d37a65bbSShawn Guo 
310b78d8e59SShawn Guo 		irq_stat = readl(port->base + GPIO_ISR) & irq_msk;
311d37a65bbSShawn Guo 		if (irq_stat)
312b78d8e59SShawn Guo 			mxc_gpio_irq_handler(port, irq_stat);
313d37a65bbSShawn Guo 	}
314c0e811d9SUwe Kleine-König 	chained_irq_exit(chip, desc);
315d37a65bbSShawn Guo }
316d37a65bbSShawn Guo 
317d37a65bbSShawn Guo /*
318d37a65bbSShawn Guo  * Set interrupt number "irq" in the GPIO as a wake-up source.
319d37a65bbSShawn Guo  * While system is running, all registered GPIO interrupts need to have
320d37a65bbSShawn Guo  * wake-up enabled. When system is suspended, only selected GPIO interrupts
321d37a65bbSShawn Guo  * need to have wake-up enabled.
322d37a65bbSShawn Guo  * @param  irq          interrupt source number
323d37a65bbSShawn Guo  * @param  enable       enable as wake-up if equal to non-zero
324d37a65bbSShawn Guo  * @return       This function returns 0 on success.
325d37a65bbSShawn Guo  */
gpio_set_wake_irq(struct irq_data * d,u32 enable)326d37a65bbSShawn Guo static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
327d37a65bbSShawn Guo {
328e4ea9333SShawn Guo 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
329e4ea9333SShawn Guo 	struct mxc_gpio_port *port = gc->private;
3301ab7ef15SShawn Guo 	u32 gpio_idx = d->hwirq;
33177a4d757SPhilipp Rosenberger 	int ret;
332d37a65bbSShawn Guo 
333d37a65bbSShawn Guo 	if (enable) {
334d37a65bbSShawn Guo 		if (port->irq_high && (gpio_idx >= 16))
33577a4d757SPhilipp Rosenberger 			ret = enable_irq_wake(port->irq_high);
336d37a65bbSShawn Guo 		else
33777a4d757SPhilipp Rosenberger 			ret = enable_irq_wake(port->irq);
338f60c9eacSShenwei Wang 		port->wakeup_pads |= (1 << gpio_idx);
339d37a65bbSShawn Guo 	} else {
340d37a65bbSShawn Guo 		if (port->irq_high && (gpio_idx >= 16))
34177a4d757SPhilipp Rosenberger 			ret = disable_irq_wake(port->irq_high);
342d37a65bbSShawn Guo 		else
34377a4d757SPhilipp Rosenberger 			ret = disable_irq_wake(port->irq);
344f60c9eacSShenwei Wang 		port->wakeup_pads &= ~(1 << gpio_idx);
345d37a65bbSShawn Guo 	}
346d37a65bbSShawn Guo 
34777a4d757SPhilipp Rosenberger 	return ret;
348d37a65bbSShawn Guo }
349d37a65bbSShawn Guo 
mxc_gpio_init_gc(struct mxc_gpio_port * port,int irq_base)3509e26b0b1SPeng Fan static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
351e4ea9333SShawn Guo {
352e4ea9333SShawn Guo 	struct irq_chip_generic *gc;
353e4ea9333SShawn Guo 	struct irq_chip_type *ct;
354db5270acSBartosz Golaszewski 	int rv;
355d37a65bbSShawn Guo 
356db5270acSBartosz Golaszewski 	gc = devm_irq_alloc_generic_chip(port->dev, "gpio-mxc", 1, irq_base,
357e4ea9333SShawn Guo 					 port->base, handle_level_irq);
3589e26b0b1SPeng Fan 	if (!gc)
3599e26b0b1SPeng Fan 		return -ENOMEM;
360e4ea9333SShawn Guo 	gc->private = port;
361e4ea9333SShawn Guo 
362e4ea9333SShawn Guo 	ct = gc->chip_types;
363591567a5SShawn Guo 	ct->chip.irq_ack = irq_gc_ack_set_bit;
364e4ea9333SShawn Guo 	ct->chip.irq_mask = irq_gc_mask_clr_bit;
365e4ea9333SShawn Guo 	ct->chip.irq_unmask = irq_gc_mask_set_bit;
366e4ea9333SShawn Guo 	ct->chip.irq_set_type = gpio_set_irq_type;
367591567a5SShawn Guo 	ct->chip.irq_set_wake = gpio_set_wake_irq;
3683093e6ccSLoic Poulain 	ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND;
369e4ea9333SShawn Guo 	ct->regs.ack = GPIO_ISR;
370e4ea9333SShawn Guo 	ct->regs.mask = GPIO_IMR;
371e4ea9333SShawn Guo 
372db5270acSBartosz Golaszewski 	rv = devm_irq_setup_generic_chip(port->dev, gc, IRQ_MSK(32),
373db5270acSBartosz Golaszewski 					 IRQ_GC_INIT_NESTED_LOCK,
374e4ea9333SShawn Guo 					 IRQ_NOREQUEST, 0);
3759e26b0b1SPeng Fan 
376db5270acSBartosz Golaszewski 	return rv;
377e4ea9333SShawn Guo }
378d37a65bbSShawn Guo 
mxc_gpio_to_irq(struct gpio_chip * gc,unsigned offset)37909ad8039SShawn Guo static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
38009ad8039SShawn Guo {
3810f4630f3SLinus Walleij 	struct mxc_gpio_port *port = gpiochip_get_data(gc);
38209ad8039SShawn Guo 
3831ab7ef15SShawn Guo 	return irq_find_mapping(port->domain, offset);
38409ad8039SShawn Guo }
38509ad8039SShawn Guo 
mxc_gpio_request(struct gpio_chip * chip,unsigned int offset)3863283d820SShenwei Wang static int mxc_gpio_request(struct gpio_chip *chip, unsigned int offset)
3873283d820SShenwei Wang {
3883283d820SShenwei Wang 	int ret;
3893283d820SShenwei Wang 
3903283d820SShenwei Wang 	ret = gpiochip_generic_request(chip, offset);
3913283d820SShenwei Wang 	if (ret)
3923283d820SShenwei Wang 		return ret;
3933283d820SShenwei Wang 
3943283d820SShenwei Wang 	return pm_runtime_resume_and_get(chip->parent);
3953283d820SShenwei Wang }
3963283d820SShenwei Wang 
mxc_gpio_free(struct gpio_chip * chip,unsigned int offset)3973283d820SShenwei Wang static void mxc_gpio_free(struct gpio_chip *chip, unsigned int offset)
3983283d820SShenwei Wang {
3993283d820SShenwei Wang 	gpiochip_generic_free(chip, offset);
4003283d820SShenwei Wang 	pm_runtime_put(chip->parent);
4013283d820SShenwei Wang }
4023283d820SShenwei Wang 
mxc_update_irq_chained_handler(struct mxc_gpio_port * port,bool enable)4035f6d1998SShenwei Wang static void mxc_update_irq_chained_handler(struct mxc_gpio_port *port, bool enable)
4045f6d1998SShenwei Wang {
4055f6d1998SShenwei Wang 	if (enable)
4065f6d1998SShenwei Wang 		irq_set_chained_handler_and_data(port->irq, port->mx_irq_handler, port);
4075f6d1998SShenwei Wang 	else
4085f6d1998SShenwei Wang 		irq_set_chained_handler_and_data(port->irq, NULL, NULL);
4095f6d1998SShenwei Wang 
4105f6d1998SShenwei Wang 	/* setup handler for GPIO 16 to 31 */
4115f6d1998SShenwei Wang 	if (port->irq_high > 0) {
4125f6d1998SShenwei Wang 		if (enable)
4135f6d1998SShenwei Wang 			irq_set_chained_handler_and_data(port->irq_high,
4145f6d1998SShenwei Wang 							 port->mx_irq_handler,
4155f6d1998SShenwei Wang 							 port);
4165f6d1998SShenwei Wang 		else
4175f6d1998SShenwei Wang 			irq_set_chained_handler_and_data(port->irq_high, NULL, NULL);
4185f6d1998SShenwei Wang 	}
4195f6d1998SShenwei Wang }
4205f6d1998SShenwei Wang 
mxc_gpio_probe(struct platform_device * pdev)4213836309dSBill Pemberton static int mxc_gpio_probe(struct platform_device *pdev)
422d37a65bbSShawn Guo {
4238937cb60SShawn Guo 	struct device_node *np = pdev->dev.of_node;
424b78d8e59SShawn Guo 	struct mxc_gpio_port *port;
425c8f3d144SAnson Huang 	int irq_count;
4261ab7ef15SShawn Guo 	int irq_base;
427e4ea9333SShawn Guo 	int err;
428d37a65bbSShawn Guo 
4298cd73e4eSFabio Estevam 	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
430b78d8e59SShawn Guo 	if (!port)
431b78d8e59SShawn Guo 		return -ENOMEM;
432d37a65bbSShawn Guo 
433db5270acSBartosz Golaszewski 	port->dev = &pdev->dev;
4340f2c7af4SFabio Estevam 	port->hwdata = device_get_match_data(&pdev->dev);
4350f2c7af4SFabio Estevam 
436123ac0e5SEnrico Weigelt, metux IT consult 	port->base = devm_platform_ioremap_resource(pdev, 0);
4378cd73e4eSFabio Estevam 	if (IS_ERR(port->base))
4388cd73e4eSFabio Estevam 		return PTR_ERR(port->base);
439b78d8e59SShawn Guo 
440c8f3d144SAnson Huang 	irq_count = platform_irq_count(pdev);
441c8f3d144SAnson Huang 	if (irq_count < 0)
442c8f3d144SAnson Huang 		return irq_count;
443c8f3d144SAnson Huang 
444c8f3d144SAnson Huang 	if (irq_count > 1) {
445b78d8e59SShawn Guo 		port->irq_high = platform_get_irq(pdev, 1);
446cc9269f8SPhilipp Rosenberger 		if (port->irq_high < 0)
447cc9269f8SPhilipp Rosenberger 			port->irq_high = 0;
448c8f3d144SAnson Huang 	}
449cc9269f8SPhilipp Rosenberger 
450b78d8e59SShawn Guo 	port->irq = platform_get_irq(pdev, 0);
4518cd73e4eSFabio Estevam 	if (port->irq < 0)
4525ea80e49SSachin Kamat 		return port->irq;
453b78d8e59SShawn Guo 
4542808801aSAnson Huang 	/* the controller clock is optional */
455*9e0fa5d8SLi Zetao 	port->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
4567beb620fSAnson Huang 	if (IS_ERR(port->clk))
4577beb620fSAnson Huang 		return PTR_ERR(port->clk);
4582808801aSAnson Huang 
459c19fdaeeSAnson Huang 	if (of_device_is_compatible(np, "fsl,imx7d-gpio"))
460c19fdaeeSAnson Huang 		port->power_off = true;
461c19fdaeeSAnson Huang 
4623283d820SShenwei Wang 	pm_runtime_get_noresume(&pdev->dev);
4633283d820SShenwei Wang 	pm_runtime_set_active(&pdev->dev);
4643283d820SShenwei Wang 	pm_runtime_enable(&pdev->dev);
4653283d820SShenwei Wang 
466d37a65bbSShawn Guo 	/* disable the interrupt and clear the status */
467b78d8e59SShawn Guo 	writel(0, port->base + GPIO_IMR);
468b78d8e59SShawn Guo 	writel(~0, port->base + GPIO_ISR);
469d37a65bbSShawn Guo 
4700f2c7af4SFabio Estevam 	if (of_device_is_compatible(np, "fsl,imx21-gpio")) {
47133a4e985SUwe Kleine-König 		/*
47233a4e985SUwe Kleine-König 		 * Setup one handler for all GPIO interrupts. Actually setting
47333a4e985SUwe Kleine-König 		 * the handler is needed only once, but doing it for every port
47433a4e985SUwe Kleine-König 		 * is more robust and easier.
47533a4e985SUwe Kleine-König 		 */
4765f6d1998SShenwei Wang 		port->irq_high = -1;
4775f6d1998SShenwei Wang 		port->mx_irq_handler = mx2_gpio_irq_handler;
4785f6d1998SShenwei Wang 	} else
4795f6d1998SShenwei Wang 		port->mx_irq_handler = mx3_gpio_irq_handler;
480d37a65bbSShawn Guo 
4815f6d1998SShenwei Wang 	mxc_update_irq_chained_handler(port, true);
4820f4630f3SLinus Walleij 	err = bgpio_init(&port->gc, &pdev->dev, 4,
4832ce420daSShawn Guo 			 port->base + GPIO_PSR,
4842ce420daSShawn Guo 			 port->base + GPIO_DR, NULL,
485442b2494SVladimir Zapolskiy 			 port->base + GPIO_GDIR, NULL,
486442b2494SVladimir Zapolskiy 			 BGPIOF_READ_OUTPUT_REG_SET);
487b78d8e59SShawn Guo 	if (err)
4888cd73e4eSFabio Estevam 		goto out_bgio;
489b78d8e59SShawn Guo 
4903283d820SShenwei Wang 	port->gc.request = mxc_gpio_request;
4913283d820SShenwei Wang 	port->gc.free = mxc_gpio_free;
4920f4630f3SLinus Walleij 	port->gc.to_irq = mxc_gpio_to_irq;
4930f4630f3SLinus Walleij 	port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
4947e6086d9SShawn Guo 					     pdev->id * 32;
4952ce420daSShawn Guo 
496ffc56630SLaxman Dewangan 	err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port);
4972ce420daSShawn Guo 	if (err)
4980f4630f3SLinus Walleij 		goto out_bgio;
4992ce420daSShawn Guo 
500c553c3c4SBartosz Golaszewski 	irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, 32, numa_node_id());
5011ab7ef15SShawn Guo 	if (irq_base < 0) {
5021ab7ef15SShawn Guo 		err = irq_base;
503ffc56630SLaxman Dewangan 		goto out_bgio;
5041ab7ef15SShawn Guo 	}
5051ab7ef15SShawn Guo 
5061ab7ef15SShawn Guo 	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
5071ab7ef15SShawn Guo 					     &irq_domain_simple_ops, NULL);
5081ab7ef15SShawn Guo 	if (!port->domain) {
5091ab7ef15SShawn Guo 		err = -ENODEV;
510c553c3c4SBartosz Golaszewski 		goto out_bgio;
5111ab7ef15SShawn Guo 	}
5128937cb60SShawn Guo 
5133283d820SShenwei Wang 	irq_domain_set_pm_device(port->domain, &pdev->dev);
5143283d820SShenwei Wang 
5158937cb60SShawn Guo 	/* gpio-mxc can be a generic irq chip */
5169e26b0b1SPeng Fan 	err = mxc_gpio_init_gc(port, irq_base);
5179e26b0b1SPeng Fan 	if (err < 0)
5189e26b0b1SPeng Fan 		goto out_irqdomain_remove;
5198937cb60SShawn Guo 
520b78d8e59SShawn Guo 	list_add_tail(&port->node, &mxc_gpio_ports);
521b78d8e59SShawn Guo 
522c19fdaeeSAnson Huang 	platform_set_drvdata(pdev, port);
5233283d820SShenwei Wang 	pm_runtime_put_autosuspend(&pdev->dev);
524c19fdaeeSAnson Huang 
525d37a65bbSShawn Guo 	return 0;
526b78d8e59SShawn Guo 
5279e26b0b1SPeng Fan out_irqdomain_remove:
5289e26b0b1SPeng Fan 	irq_domain_remove(port->domain);
5298cd73e4eSFabio Estevam out_bgio:
5303283d820SShenwei Wang 	pm_runtime_disable(&pdev->dev);
5313283d820SShenwei Wang 	pm_runtime_put_noidle(&pdev->dev);
532b78d8e59SShawn Guo 	dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
533b78d8e59SShawn Guo 	return err;
534d37a65bbSShawn Guo }
535b78d8e59SShawn Guo 
mxc_gpio_save_regs(struct mxc_gpio_port * port)536c19fdaeeSAnson Huang static void mxc_gpio_save_regs(struct mxc_gpio_port *port)
537c19fdaeeSAnson Huang {
538c19fdaeeSAnson Huang 	if (!port->power_off)
539c19fdaeeSAnson Huang 		return;
540c19fdaeeSAnson Huang 
541c19fdaeeSAnson Huang 	port->gpio_saved_reg.icr1 = readl(port->base + GPIO_ICR1);
542c19fdaeeSAnson Huang 	port->gpio_saved_reg.icr2 = readl(port->base + GPIO_ICR2);
543c19fdaeeSAnson Huang 	port->gpio_saved_reg.imr = readl(port->base + GPIO_IMR);
544c19fdaeeSAnson Huang 	port->gpio_saved_reg.gdir = readl(port->base + GPIO_GDIR);
545c19fdaeeSAnson Huang 	port->gpio_saved_reg.edge_sel = readl(port->base + GPIO_EDGE_SEL);
546c19fdaeeSAnson Huang 	port->gpio_saved_reg.dr = readl(port->base + GPIO_DR);
547c19fdaeeSAnson Huang }
548c19fdaeeSAnson Huang 
mxc_gpio_restore_regs(struct mxc_gpio_port * port)549c19fdaeeSAnson Huang static void mxc_gpio_restore_regs(struct mxc_gpio_port *port)
550c19fdaeeSAnson Huang {
551c19fdaeeSAnson Huang 	if (!port->power_off)
552c19fdaeeSAnson Huang 		return;
553c19fdaeeSAnson Huang 
554c19fdaeeSAnson Huang 	writel(port->gpio_saved_reg.icr1, port->base + GPIO_ICR1);
555c19fdaeeSAnson Huang 	writel(port->gpio_saved_reg.icr2, port->base + GPIO_ICR2);
556c19fdaeeSAnson Huang 	writel(port->gpio_saved_reg.imr, port->base + GPIO_IMR);
557c19fdaeeSAnson Huang 	writel(port->gpio_saved_reg.gdir, port->base + GPIO_GDIR);
558c19fdaeeSAnson Huang 	writel(port->gpio_saved_reg.edge_sel, port->base + GPIO_EDGE_SEL);
559c19fdaeeSAnson Huang 	writel(port->gpio_saved_reg.dr, port->base + GPIO_DR);
560c19fdaeeSAnson Huang }
561c19fdaeeSAnson Huang 
mxc_gpio_generic_config(struct mxc_gpio_port * port,unsigned int offset,unsigned long conf)562f60c9eacSShenwei Wang static bool mxc_gpio_generic_config(struct mxc_gpio_port *port,
563f60c9eacSShenwei Wang 		unsigned int offset, unsigned long conf)
564f60c9eacSShenwei Wang {
565f60c9eacSShenwei Wang 	struct device_node *np = port->dev->of_node;
566f60c9eacSShenwei Wang 
567f60c9eacSShenwei Wang 	if (of_device_is_compatible(np, "fsl,imx8dxl-gpio") ||
568f60c9eacSShenwei Wang 	    of_device_is_compatible(np, "fsl,imx8qxp-gpio") ||
569f60c9eacSShenwei Wang 	    of_device_is_compatible(np, "fsl,imx8qm-gpio"))
570f60c9eacSShenwei Wang 		return (gpiochip_generic_config(&port->gc, offset, conf) == 0);
571f60c9eacSShenwei Wang 
572f60c9eacSShenwei Wang 	return false;
573f60c9eacSShenwei Wang }
574f60c9eacSShenwei Wang 
mxc_gpio_set_pad_wakeup(struct mxc_gpio_port * port,bool enable)575f60c9eacSShenwei Wang static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
576f60c9eacSShenwei Wang {
577f60c9eacSShenwei Wang 	unsigned long config;
578f60c9eacSShenwei Wang 	bool ret = false;
579f60c9eacSShenwei Wang 	int i, type;
580f60c9eacSShenwei Wang 
581f60c9eacSShenwei Wang 	static const u32 pad_type_map[] = {
582f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_OFF,		/* 0 */
583f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_RISE_EDGE,	/* IRQ_TYPE_EDGE_RISING */
584f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_FALL_EDGE,	/* IRQ_TYPE_EDGE_FALLING */
585f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_FALL_EDGE,	/* IRQ_TYPE_EDGE_BOTH */
586f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_HIGH_LVL,	/* IRQ_TYPE_LEVEL_HIGH */
587f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_OFF,		/* 5 */
588f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_OFF,		/* 6 */
589f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_OFF,		/* 7 */
590f60c9eacSShenwei Wang 		IMX_SCU_WAKEUP_LOW_LVL,		/* IRQ_TYPE_LEVEL_LOW */
591f60c9eacSShenwei Wang 	};
592f60c9eacSShenwei Wang 
593f60c9eacSShenwei Wang 	for (i = 0; i < 32; i++) {
594f60c9eacSShenwei Wang 		if ((port->wakeup_pads & (1 << i))) {
595f60c9eacSShenwei Wang 			type = port->pad_type[i];
596f60c9eacSShenwei Wang 			if (enable)
597f60c9eacSShenwei Wang 				config = pad_type_map[type];
598f60c9eacSShenwei Wang 			else
599f60c9eacSShenwei Wang 				config = IMX_SCU_WAKEUP_OFF;
600f60c9eacSShenwei Wang 			ret |= mxc_gpio_generic_config(port, i, config);
601f60c9eacSShenwei Wang 		}
602f60c9eacSShenwei Wang 	}
603f60c9eacSShenwei Wang 
604f60c9eacSShenwei Wang 	return ret;
605f60c9eacSShenwei Wang }
606f60c9eacSShenwei Wang 
mxc_gpio_runtime_suspend(struct device * dev)6073283d820SShenwei Wang static int mxc_gpio_runtime_suspend(struct device *dev)
6083283d820SShenwei Wang {
6093283d820SShenwei Wang 	struct mxc_gpio_port *port = dev_get_drvdata(dev);
6103283d820SShenwei Wang 
6113283d820SShenwei Wang 	mxc_gpio_save_regs(port);
6123283d820SShenwei Wang 	clk_disable_unprepare(port->clk);
6135f6d1998SShenwei Wang 	mxc_update_irq_chained_handler(port, false);
6143283d820SShenwei Wang 
6153283d820SShenwei Wang 	return 0;
6163283d820SShenwei Wang }
6173283d820SShenwei Wang 
mxc_gpio_runtime_resume(struct device * dev)6183283d820SShenwei Wang static int mxc_gpio_runtime_resume(struct device *dev)
6193283d820SShenwei Wang {
6203283d820SShenwei Wang 	struct mxc_gpio_port *port = dev_get_drvdata(dev);
6213283d820SShenwei Wang 	int ret;
6223283d820SShenwei Wang 
6235f6d1998SShenwei Wang 	mxc_update_irq_chained_handler(port, true);
6243283d820SShenwei Wang 	ret = clk_prepare_enable(port->clk);
6255f6d1998SShenwei Wang 	if (ret) {
6265f6d1998SShenwei Wang 		mxc_update_irq_chained_handler(port, false);
6273283d820SShenwei Wang 		return ret;
6285f6d1998SShenwei Wang 	}
6293283d820SShenwei Wang 
6303283d820SShenwei Wang 	mxc_gpio_restore_regs(port);
6313283d820SShenwei Wang 
6323283d820SShenwei Wang 	return 0;
6333283d820SShenwei Wang }
6343283d820SShenwei Wang 
mxc_gpio_noirq_suspend(struct device * dev)6356a270bbdSGeert Uytterhoeven static int mxc_gpio_noirq_suspend(struct device *dev)
636f60c9eacSShenwei Wang {
637f60c9eacSShenwei Wang 	struct platform_device *pdev = to_platform_device(dev);
638f60c9eacSShenwei Wang 	struct mxc_gpio_port *port = platform_get_drvdata(pdev);
639f60c9eacSShenwei Wang 
640f60c9eacSShenwei Wang 	if (port->wakeup_pads > 0)
641f60c9eacSShenwei Wang 		port->is_pad_wakeup = mxc_gpio_set_pad_wakeup(port, true);
642f60c9eacSShenwei Wang 
643f60c9eacSShenwei Wang 	return 0;
644f60c9eacSShenwei Wang }
645f60c9eacSShenwei Wang 
mxc_gpio_noirq_resume(struct device * dev)6466a270bbdSGeert Uytterhoeven static int mxc_gpio_noirq_resume(struct device *dev)
647f60c9eacSShenwei Wang {
648f60c9eacSShenwei Wang 	struct platform_device *pdev = to_platform_device(dev);
649f60c9eacSShenwei Wang 	struct mxc_gpio_port *port = platform_get_drvdata(pdev);
650f60c9eacSShenwei Wang 
651f60c9eacSShenwei Wang 	if (port->wakeup_pads > 0)
652f60c9eacSShenwei Wang 		mxc_gpio_set_pad_wakeup(port, false);
653f60c9eacSShenwei Wang 	port->is_pad_wakeup = false;
654f60c9eacSShenwei Wang 
655f60c9eacSShenwei Wang 	return 0;
656f60c9eacSShenwei Wang }
657f60c9eacSShenwei Wang 
658f60c9eacSShenwei Wang static const struct dev_pm_ops mxc_gpio_dev_pm_ops = {
6596a270bbdSGeert Uytterhoeven 	NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume)
6606a270bbdSGeert Uytterhoeven 	RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL)
661f60c9eacSShenwei Wang };
662f60c9eacSShenwei Wang 
mxc_gpio_syscore_suspend(void)6631a5287a3SAnson Huang static int mxc_gpio_syscore_suspend(void)
664c19fdaeeSAnson Huang {
6651a5287a3SAnson Huang 	struct mxc_gpio_port *port;
6663283d820SShenwei Wang 	int ret;
667c19fdaeeSAnson Huang 
6681a5287a3SAnson Huang 	/* walk through all ports */
6691a5287a3SAnson Huang 	list_for_each_entry(port, &mxc_gpio_ports, node) {
6703283d820SShenwei Wang 		ret = clk_prepare_enable(port->clk);
6713283d820SShenwei Wang 		if (ret)
6723283d820SShenwei Wang 			return ret;
673c19fdaeeSAnson Huang 		mxc_gpio_save_regs(port);
674c19fdaeeSAnson Huang 		clk_disable_unprepare(port->clk);
6751a5287a3SAnson Huang 	}
676c19fdaeeSAnson Huang 
677c19fdaeeSAnson Huang 	return 0;
678c19fdaeeSAnson Huang }
679c19fdaeeSAnson Huang 
mxc_gpio_syscore_resume(void)6801a5287a3SAnson Huang static void mxc_gpio_syscore_resume(void)
681c19fdaeeSAnson Huang {
6821a5287a3SAnson Huang 	struct mxc_gpio_port *port;
683c19fdaeeSAnson Huang 	int ret;
684c19fdaeeSAnson Huang 
6851a5287a3SAnson Huang 	/* walk through all ports */
6861a5287a3SAnson Huang 	list_for_each_entry(port, &mxc_gpio_ports, node) {
687c19fdaeeSAnson Huang 		ret = clk_prepare_enable(port->clk);
6881a5287a3SAnson Huang 		if (ret) {
6891a5287a3SAnson Huang 			pr_err("mxc: failed to enable gpio clock %d\n", ret);
6901a5287a3SAnson Huang 			return;
6911a5287a3SAnson Huang 		}
692c19fdaeeSAnson Huang 		mxc_gpio_restore_regs(port);
6933283d820SShenwei Wang 		clk_disable_unprepare(port->clk);
6941a5287a3SAnson Huang 	}
695c19fdaeeSAnson Huang }
696c19fdaeeSAnson Huang 
6971a5287a3SAnson Huang static struct syscore_ops mxc_gpio_syscore_ops = {
6981a5287a3SAnson Huang 	.suspend = mxc_gpio_syscore_suspend,
6991a5287a3SAnson Huang 	.resume = mxc_gpio_syscore_resume,
700c19fdaeeSAnson Huang };
701c19fdaeeSAnson Huang 
702b78d8e59SShawn Guo static struct platform_driver mxc_gpio_driver = {
703b78d8e59SShawn Guo 	.driver		= {
704b78d8e59SShawn Guo 		.name	= "gpio-mxc",
7058937cb60SShawn Guo 		.of_match_table = mxc_gpio_dt_ids,
70690e1fc4cSBartosz Golaszewski 		.suppress_bind_attrs = true,
7076a270bbdSGeert Uytterhoeven 		.pm = pm_ptr(&mxc_gpio_dev_pm_ops),
708b78d8e59SShawn Guo 	},
709b78d8e59SShawn Guo 	.probe		= mxc_gpio_probe,
710b78d8e59SShawn Guo };
711b78d8e59SShawn Guo 
gpio_mxc_init(void)712b78d8e59SShawn Guo static int __init gpio_mxc_init(void)
713b78d8e59SShawn Guo {
7141a5287a3SAnson Huang 	register_syscore_ops(&mxc_gpio_syscore_ops);
7151a5287a3SAnson Huang 
716b78d8e59SShawn Guo 	return platform_driver_register(&mxc_gpio_driver);
717b78d8e59SShawn Guo }
718e188cbf7SVladimir Zapolskiy subsys_initcall(gpio_mxc_init);
71912d16b39SAnson Huang 
72012d16b39SAnson Huang MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
72112d16b39SAnson Huang MODULE_DESCRIPTION("i.MX GPIO Driver");
72212d16b39SAnson Huang MODULE_LICENSE("GPL");
723