| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de> | 
					
						
							|  |  |  |  * Copyright 2008 Juergen Beisert, kernel@pengutronix.de | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Based on code from Freescale, | 
					
						
							|  |  |  |  * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  |  * as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  |  * of the License, or (at your option) any later version. | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software | 
					
						
							|  |  |  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | 
					
						
							|  |  |  |  * MA  02110-1301, USA. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-21 11:09:01 +01:00
										 |  |  | #include <linux/err.h>
 | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | #include <linux/irq.h>
 | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | #include <linux/irqdomain.h>
 | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | #include <linux/gpio.h>
 | 
					
						
							| 
									
										
										
										
											2012-05-04 14:29:22 +08:00
										 |  |  | #include <linux/of.h>
 | 
					
						
							|  |  |  | #include <linux/of_address.h>
 | 
					
						
							|  |  |  | #include <linux/of_device.h>
 | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2011-06-06 22:31:29 +08:00
										 |  |  | #include <linux/basic_mmio_gpio.h>
 | 
					
						
							| 
									
										
										
										
											2011-07-03 13:38:09 -04:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | #define MXS_SET		0x4
 | 
					
						
							|  |  |  | #define MXS_CLR		0x8
 | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | #define PINCTRL_DOUT(p)		((is_imx23_gpio(p) ? 0x0500 : 0x0700) + (p->id) * 0x10)
 | 
					
						
							|  |  |  | #define PINCTRL_DIN(p)		((is_imx23_gpio(p) ? 0x0600 : 0x0900) + (p->id) * 0x10)
 | 
					
						
							|  |  |  | #define PINCTRL_DOE(p)		((is_imx23_gpio(p) ? 0x0700 : 0x0b00) + (p->id) * 0x10)
 | 
					
						
							|  |  |  | #define PINCTRL_PIN2IRQ(p)	((is_imx23_gpio(p) ? 0x0800 : 0x1000) + (p->id) * 0x10)
 | 
					
						
							|  |  |  | #define PINCTRL_IRQEN(p)	((is_imx23_gpio(p) ? 0x0900 : 0x1100) + (p->id) * 0x10)
 | 
					
						
							|  |  |  | #define PINCTRL_IRQLEV(p)	((is_imx23_gpio(p) ? 0x0a00 : 0x1200) + (p->id) * 0x10)
 | 
					
						
							|  |  |  | #define PINCTRL_IRQPOL(p)	((is_imx23_gpio(p) ? 0x0b00 : 0x1300) + (p->id) * 0x10)
 | 
					
						
							|  |  |  | #define PINCTRL_IRQSTAT(p)	((is_imx23_gpio(p) ? 0x0c00 : 0x1400) + (p->id) * 0x10)
 | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define GPIO_INT_FALL_EDGE	0x0
 | 
					
						
							|  |  |  | #define GPIO_INT_LOW_LEV	0x1
 | 
					
						
							|  |  |  | #define GPIO_INT_RISE_EDGE	0x2
 | 
					
						
							|  |  |  | #define GPIO_INT_HIGH_LEV	0x3
 | 
					
						
							|  |  |  | #define GPIO_INT_LEV_MASK	(1 << 0)
 | 
					
						
							|  |  |  | #define GPIO_INT_POL_MASK	(1 << 1)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | enum mxs_gpio_id { | 
					
						
							|  |  |  | 	IMX23_GPIO, | 
					
						
							|  |  |  | 	IMX28_GPIO, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | struct mxs_gpio_port { | 
					
						
							|  |  |  | 	void __iomem *base; | 
					
						
							|  |  |  | 	int id; | 
					
						
							|  |  |  | 	int irq; | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	struct irq_domain *domain; | 
					
						
							| 
									
										
										
										
											2011-06-06 22:31:29 +08:00
										 |  |  | 	struct bgpio_chip bgc; | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	enum mxs_gpio_id devid; | 
					
						
							| 
									
										
										
										
											2013-01-29 09:16:33 +01:00
										 |  |  | 	u32 both_edges; | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | static inline int is_imx23_gpio(struct mxs_gpio_port *port) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return port->devid == IMX23_GPIO; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int is_imx28_gpio(struct mxs_gpio_port *port) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return port->devid == IMX28_GPIO; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | /* Note: This driver assumes 32 GPIOs are handled in one register */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-18 21:31:41 +01:00
										 |  |  | static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-29 09:16:33 +01:00
										 |  |  | 	u32 val; | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	u32 pin_mask = 1 << d->hwirq; | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | 
					
						
							|  |  |  | 	struct mxs_gpio_port *port = gc->private; | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 	void __iomem *pin_addr; | 
					
						
							|  |  |  | 	int edge; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-29 09:16:33 +01:00
										 |  |  | 	port->both_edges &= ~pin_mask; | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 	switch (type) { | 
					
						
							| 
									
										
										
										
											2013-01-29 09:16:33 +01:00
										 |  |  | 	case IRQ_TYPE_EDGE_BOTH: | 
					
						
							|  |  |  | 		val = gpio_get_value(port->bgc.gc.base + d->hwirq); | 
					
						
							|  |  |  | 		if (val) | 
					
						
							|  |  |  | 			edge = GPIO_INT_FALL_EDGE; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			edge = GPIO_INT_RISE_EDGE; | 
					
						
							|  |  |  | 		port->both_edges |= pin_mask; | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 	case IRQ_TYPE_EDGE_RISING: | 
					
						
							|  |  |  | 		edge = GPIO_INT_RISE_EDGE; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case IRQ_TYPE_EDGE_FALLING: | 
					
						
							|  |  |  | 		edge = GPIO_INT_FALL_EDGE; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case IRQ_TYPE_LEVEL_LOW: | 
					
						
							|  |  |  | 		edge = GPIO_INT_LOW_LEV; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case IRQ_TYPE_LEVEL_HIGH: | 
					
						
							|  |  |  | 		edge = GPIO_INT_HIGH_LEV; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set level or edge */ | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	pin_addr = port->base + PINCTRL_IRQLEV(port); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 	if (edge & GPIO_INT_LEV_MASK) | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 		writel(pin_mask, pin_addr + MXS_SET); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 		writel(pin_mask, pin_addr + MXS_CLR); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* set polarity */ | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	pin_addr = port->base + PINCTRL_IRQPOL(port); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 	if (edge & GPIO_INT_POL_MASK) | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 		writel(pin_mask, pin_addr + MXS_SET); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 		writel(pin_mask, pin_addr + MXS_CLR); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	writel(pin_mask, | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	       port->base + PINCTRL_IRQSTAT(port) + MXS_CLR); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-29 09:16:33 +01:00
										 |  |  | static void mxs_flip_edge(struct mxs_gpio_port *port, u32 gpio) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 bit, val, edge; | 
					
						
							|  |  |  | 	void __iomem *pin_addr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bit = 1 << gpio; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pin_addr = port->base + PINCTRL_IRQPOL(port); | 
					
						
							|  |  |  | 	val = readl(pin_addr); | 
					
						
							|  |  |  | 	edge = val & bit; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (edge) | 
					
						
							|  |  |  | 		writel(bit, pin_addr + MXS_CLR); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		writel(bit, pin_addr + MXS_SET); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | /* MXS has one interrupt *per* gpio port */ | 
					
						
							|  |  |  | static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 irq_stat; | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	struct mxs_gpio_port *port = irq_get_handler_data(irq); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-25 16:54:22 +01:00
										 |  |  | 	desc->irq_data.chip->irq_ack(&desc->irq_data); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	irq_stat = readl(port->base + PINCTRL_IRQSTAT(port)) & | 
					
						
							|  |  |  | 			readl(port->base + PINCTRL_IRQEN(port)); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	while (irq_stat != 0) { | 
					
						
							|  |  |  | 		int irqoffset = fls(irq_stat) - 1; | 
					
						
							| 
									
										
										
										
											2013-01-29 09:16:33 +01:00
										 |  |  | 		if (port->both_edges & (1 << irqoffset)) | 
					
						
							|  |  |  | 			mxs_flip_edge(port, irqoffset); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 		generic_handle_irq(irq_find_mapping(port->domain, irqoffset)); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 		irq_stat &= ~(1 << irqoffset); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Set interrupt number "irq" in the GPIO as a wake-up source. | 
					
						
							|  |  |  |  * While system is running, all registered GPIO interrupts need to have | 
					
						
							|  |  |  |  * wake-up enabled. When system is suspended, only selected GPIO interrupts | 
					
						
							|  |  |  |  * need to have wake-up enabled. | 
					
						
							|  |  |  |  * @param  irq          interrupt source number | 
					
						
							|  |  |  |  * @param  enable       enable as wake-up if equal to non-zero | 
					
						
							|  |  |  |  * @return       This function returns 0 on success. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-02-18 21:31:41 +01:00
										 |  |  | static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | 
					
						
							|  |  |  | 	struct mxs_gpio_port *port = gc->private; | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:53 +08:00
										 |  |  | 	if (enable) | 
					
						
							|  |  |  | 		enable_irq_wake(port->irq); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		disable_irq_wake(port->irq); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base) | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct irq_chip_generic *gc; | 
					
						
							|  |  |  | 	struct irq_chip_type *ct; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	gc = irq_alloc_generic_chip("gpio-mxs", 1, irq_base, | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | 				    port->base, handle_level_irq); | 
					
						
							|  |  |  | 	gc->private = port; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ct = gc->chip_types; | 
					
						
							| 
									
										
										
										
											2011-07-19 21:16:56 +08:00
										 |  |  | 	ct->chip.irq_ack = irq_gc_ack_set_bit; | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | 	ct->chip.irq_mask = irq_gc_mask_clr_bit; | 
					
						
							|  |  |  | 	ct->chip.irq_unmask = irq_gc_mask_set_bit; | 
					
						
							|  |  |  | 	ct->chip.irq_set_type = mxs_gpio_set_irq_type; | 
					
						
							| 
									
										
										
										
											2011-07-19 21:16:56 +08:00
										 |  |  | 	ct->chip.irq_set_wake = mxs_gpio_set_wake_irq; | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	ct->regs.ack = PINCTRL_IRQSTAT(port) + MXS_CLR; | 
					
						
							|  |  |  | 	ct->regs.mask = PINCTRL_IRQEN(port); | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												gpio: mxs: Allow for recursive enable_irq_wake() call
The scenario here is that someone calls enable_irq_wake() from somewhere
in the code. This will result in the lockdep producing a backtrace as can
be seen below. In my case, this problem is triggered when using the wl1271
(TI WlCore) driver found in drivers/net/wireless/ti/ .
The problem cause is rather obvious from the backtrace, but let's outline
the dependency. enable_irq_wake() grabs the IRQ buslock in irq_set_irq_wake(),
which in turns calls mxs_gpio_set_wake_irq() . But mxs_gpio_set_wake_irq()
calls enable_irq_wake() again on the one-level-higher IRQ , thus it tries to
grab the IRQ buslock again in irq_set_irq_wake() . Because the spinlock in
irq_set_irq_wake()->irq_get_desc_buslock()->__irq_get_desc_lock() is not
marked as recursive, lockdep will spew the stuff below.
We know we can safely re-enter the lock, so use IRQ_GC_INIT_NESTED_LOCK to
fix the spew.
 =============================================
 [ INFO: possible recursive locking detected ]
 3.10.33-00012-gf06b763-dirty #61 Not tainted
 ---------------------------------------------
 kworker/0:1/18 is trying to acquire lock:
  (&irq_desc_lock_class){-.-...}, at: [<c00685f0>] __irq_get_desc_lock+0x48/0x88
 but task is already holding lock:
  (&irq_desc_lock_class){-.-...}, at: [<c00685f0>] __irq_get_desc_lock+0x48/0x88
 other info that might help us debug this:
  Possible unsafe locking scenario:
        CPU0
        ----
   lock(&irq_desc_lock_class);
   lock(&irq_desc_lock_class);
  *** DEADLOCK ***
  May be due to missing lock nesting notation
 3 locks held by kworker/0:1/18:
  #0:  (events){.+.+.+}, at: [<c0036308>] process_one_work+0x134/0x4a4
  #1:  ((&fw_work->work)){+.+.+.}, at: [<c0036308>] process_one_work+0x134/0x4a4
  #2:  (&irq_desc_lock_class){-.-...}, at: [<c00685f0>] __irq_get_desc_lock+0x48/0x88
 stack backtrace:
 CPU: 0 PID: 18 Comm: kworker/0:1 Not tainted 3.10.33-00012-gf06b763-dirty #61
 Workqueue: events request_firmware_work_func
 [<c0013eb4>] (unwind_backtrace+0x0/0xf0) from [<c0011c74>] (show_stack+0x10/0x14)
 [<c0011c74>] (show_stack+0x10/0x14) from [<c005bb08>] (__lock_acquire+0x140c/0x1a64)
 [<c005bb08>] (__lock_acquire+0x140c/0x1a64) from [<c005c6a8>] (lock_acquire+0x9c/0x104)
 [<c005c6a8>] (lock_acquire+0x9c/0x104) from [<c051d5a4>] (_raw_spin_lock_irqsave+0x44/0x58)
 [<c051d5a4>] (_raw_spin_lock_irqsave+0x44/0x58) from [<c00685f0>] (__irq_get_desc_lock+0x48/0x88)
 [<c00685f0>] (__irq_get_desc_lock+0x48/0x88) from [<c0068e78>] (irq_set_irq_wake+0x20/0xf4)
 [<c0068e78>] (irq_set_irq_wake+0x20/0xf4) from [<c027260c>] (mxs_gpio_set_wake_irq+0x1c/0x24)
 [<c027260c>] (mxs_gpio_set_wake_irq+0x1c/0x24) from [<c0068cf4>] (set_irq_wake_real+0x30/0x44)
 [<c0068cf4>] (set_irq_wake_real+0x30/0x44) from [<c0068ee4>] (irq_set_irq_wake+0x8c/0xf4)
 [<c0068ee4>] (irq_set_irq_wake+0x8c/0xf4) from [<c0310748>] (wlcore_nvs_cb+0x10c/0x97c)
 [<c0310748>] (wlcore_nvs_cb+0x10c/0x97c) from [<c02be5e8>] (request_firmware_work_func+0x38/0x58)
 [<c02be5e8>] (request_firmware_work_func+0x38/0x58) from [<c0036394>] (process_one_work+0x1c0/0x4a4)
 [<c0036394>] (process_one_work+0x1c0/0x4a4) from [<c0036a4c>] (worker_thread+0x138/0x394)
 [<c0036a4c>] (worker_thread+0x138/0x394) from [<c003cb74>] (kthread+0xa4/0xb0)
 [<c003cb74>] (kthread+0xa4/0xb0) from [<c000ee00>] (ret_from_fork+0x14/0x34)
 wlcore: loaded
Cc: stable@vger.kernel.org
Signed-off-by: Marek Vasut <marex@denx.de>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
											
										 
											2014-03-24 03:38:10 +01:00
										 |  |  | 	irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, | 
					
						
							|  |  |  | 			       IRQ_NOREQUEST, 0); | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 22:31:29 +08:00
										 |  |  | static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset) | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-06 22:31:29 +08:00
										 |  |  | 	struct bgpio_chip *bgc = to_bgpio_chip(gc); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 	struct mxs_gpio_port *port = | 
					
						
							| 
									
										
										
										
											2011-06-06 22:31:29 +08:00
										 |  |  | 		container_of(bgc, struct mxs_gpio_port, bgc); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	return irq_find_mapping(port->domain, offset); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | static struct platform_device_id mxs_gpio_ids[] = { | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		.name = "imx23-gpio", | 
					
						
							|  |  |  | 		.driver_data = IMX23_GPIO, | 
					
						
							|  |  |  | 	}, { | 
					
						
							|  |  |  | 		.name = "imx28-gpio", | 
					
						
							|  |  |  | 		.driver_data = IMX28_GPIO, | 
					
						
							|  |  |  | 	}, { | 
					
						
							|  |  |  | 		/* sentinel */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(platform, mxs_gpio_ids); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 14:29:22 +08:00
										 |  |  | static const struct of_device_id mxs_gpio_dt_ids[] = { | 
					
						
							|  |  |  | 	{ .compatible = "fsl,imx23-gpio", .data = (void *) IMX23_GPIO, }, | 
					
						
							|  |  |  | 	{ .compatible = "fsl,imx28-gpio", .data = (void *) IMX28_GPIO, }, | 
					
						
							|  |  |  | 	{ /* sentinel */ } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(of, mxs_gpio_dt_ids); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 13:22:34 -05:00
										 |  |  | static int mxs_gpio_probe(struct platform_device *pdev) | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-04 14:29:22 +08:00
										 |  |  | 	const struct of_device_id *of_id = | 
					
						
							|  |  |  | 			of_match_device(mxs_gpio_dt_ids, &pdev->dev); | 
					
						
							|  |  |  | 	struct device_node *np = pdev->dev.of_node; | 
					
						
							|  |  |  | 	struct device_node *parent; | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	static void __iomem *base; | 
					
						
							|  |  |  | 	struct mxs_gpio_port *port; | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	int irq_base; | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 10:30:14 +08:00
										 |  |  | 	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	if (!port) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-05 17:21:22 -02:00
										 |  |  | 	port->id = of_alias_get_id(np, "gpio"); | 
					
						
							|  |  |  | 	if (port->id < 0) | 
					
						
							|  |  |  | 		return port->id; | 
					
						
							|  |  |  | 	port->devid = (enum mxs_gpio_id) of_id->data; | 
					
						
							| 
									
										
										
										
											2012-05-04 10:30:14 +08:00
										 |  |  | 	port->irq = platform_get_irq(pdev, 0); | 
					
						
							|  |  |  | 	if (port->irq < 0) | 
					
						
							|  |  |  | 		return port->irq; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * map memory region only once, as all the gpio ports | 
					
						
							|  |  |  | 	 * share the same one | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (!base) { | 
					
						
							| 
									
										
										
										
											2013-11-05 17:21:22 -02:00
										 |  |  | 		parent = of_get_parent(np); | 
					
						
							|  |  |  | 		base = of_iomap(parent, 0); | 
					
						
							|  |  |  | 		of_node_put(parent); | 
					
						
							|  |  |  | 		if (!base) | 
					
						
							|  |  |  | 			return -EADDRNOTAVAIL; | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	port->base = base; | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * select the pin interrupt functionality but initially | 
					
						
							|  |  |  | 	 * disable the interrupts | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	writel(~0U, port->base + PINCTRL_PIN2IRQ(port)); | 
					
						
							|  |  |  | 	writel(0, port->base + PINCTRL_IRQEN(port)); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	/* clear address has to be used to clear IRQSTAT bits */ | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id()); | 
					
						
							|  |  |  | 	if (irq_base < 0) | 
					
						
							|  |  |  | 		return irq_base; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, | 
					
						
							|  |  |  | 					     &irq_domain_simple_ops, NULL); | 
					
						
							|  |  |  | 	if (!port->domain) { | 
					
						
							|  |  |  | 		err = -ENODEV; | 
					
						
							|  |  |  | 		goto out_irqdesc_free; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-07 22:00:54 +08:00
										 |  |  | 	/* gpio-mxs can be a generic irq chip */ | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	mxs_gpio_init_gc(port, irq_base); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	/* setup one handler for each entry */ | 
					
						
							|  |  |  | 	irq_set_chained_handler(port->irq, mxs_gpio_irq_handler); | 
					
						
							|  |  |  | 	irq_set_handler_data(port->irq, port); | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 22:31:29 +08:00
										 |  |  | 	err = bgpio_init(&port->bgc, &pdev->dev, 4, | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 			 port->base + PINCTRL_DIN(port), | 
					
						
							| 
									
										
										
										
											2013-04-29 16:07:18 +02:00
										 |  |  | 			 port->base + PINCTRL_DOUT(port) + MXS_SET, | 
					
						
							|  |  |  | 			 port->base + PINCTRL_DOUT(port) + MXS_CLR, | 
					
						
							| 
									
										
										
										
											2012-05-26 12:57:47 -07:00
										 |  |  | 			 port->base + PINCTRL_DOE(port), NULL, 0); | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	if (err) | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 		goto out_irqdesc_free; | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 22:31:29 +08:00
										 |  |  | 	port->bgc.gc.to_irq = mxs_gpio_to_irq; | 
					
						
							|  |  |  | 	port->bgc.gc.base = port->id * 32; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = gpiochip_add(&port->bgc.gc); | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto out_bgpio_remove; | 
					
						
							| 
									
										
										
										
											2011-06-06 22:31:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2012-08-20 16:43:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | out_bgpio_remove: | 
					
						
							|  |  |  | 	bgpio_remove(&port->bgc); | 
					
						
							|  |  |  | out_irqdesc_free: | 
					
						
							|  |  |  | 	irq_free_descs(irq_base, 32); | 
					
						
							|  |  |  | 	return err; | 
					
						
							| 
									
										
										
										
											2011-01-24 12:57:46 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver mxs_gpio_driver = { | 
					
						
							|  |  |  | 	.driver		= { | 
					
						
							|  |  |  | 		.name	= "gpio-mxs", | 
					
						
							|  |  |  | 		.owner	= THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2012-05-04 14:29:22 +08:00
										 |  |  | 		.of_match_table = mxs_gpio_dt_ids, | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 	.probe		= mxs_gpio_probe, | 
					
						
							| 
									
										
										
										
											2012-05-03 23:32:52 +08:00
										 |  |  | 	.id_table	= mxs_gpio_ids, | 
					
						
							| 
									
										
										
										
											2010-12-18 21:39:31 +08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2011-01-24 12:57:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | static int __init mxs_gpio_init(void) | 
					
						
							| 
									
										
										
										
											2011-01-24 12:57:46 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | 	return platform_driver_register(&mxs_gpio_driver); | 
					
						
							| 
									
										
										
										
											2011-01-24 12:57:46 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-06-06 09:37:58 -06:00
										 |  |  | postcore_initcall(mxs_gpio_init); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Freescale Semiconductor, " | 
					
						
							|  |  |  | 	      "Daniel Mack <danielncaiaq.de>, " | 
					
						
							|  |  |  | 	      "Juergen Beisert <kernel@pengutronix.de>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Freescale MXS GPIO"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |