diff options
| -rw-r--r-- | arch/arm/Kconfig | 2 | ||||
| -rw-r--r-- | arch/arm/mach-mx3/devices.c | 27 | ||||
| -rw-r--r-- | arch/arm/plat-mxc/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/plat-mxc/gpio.c | 253 | ||||
| -rw-r--r-- | arch/arm/plat-mxc/irq.c | 3 | ||||
| -rw-r--r-- | include/asm-arm/arch-mxc/common.h | 1 | ||||
| -rw-r--r-- | include/asm-arm/arch-mxc/gpio.h | 42 | ||||
| -rw-r--r-- | include/asm-arm/arch-mxc/mx31.h | 19 | 
8 files changed, 348 insertions, 1 deletions
| diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b786e68914d4..5c8c1a89be73 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -368,6 +368,8 @@ config ARCH_NS9XXX  config ARCH_MXC  	bool "Freescale MXC/iMX-based"  	select ARCH_MTD_XIP +	select GENERIC_GPIO +	select HAVE_GPIO_LIB  	help  	  Support for Freescale MXC/iMX-based family of processors diff --git a/arch/arm/mach-mx3/devices.c b/arch/arm/mach-mx3/devices.c index 1bc6d23a1d58..5c0320fce5b6 100644 --- a/arch/arm/mach-mx3/devices.c +++ b/arch/arm/mach-mx3/devices.c @@ -20,6 +20,7 @@  #include <linux/module.h>  #include <linux/platform_device.h>  #include <linux/serial.h> +#include <linux/gpio.h>  #include <asm/hardware.h>  #include <asm/arch/imx-uart.h> @@ -151,3 +152,29 @@ int __init imx_init_uart(int uart_no, struct imxuart_platform_data *pdata)  	return 0;  } +/* GPIO port description */ +static struct mxc_gpio_port imx_gpio_ports[] = { +	[0] = { +		.chip.label = "gpio-0", +		.base = IO_ADDRESS(GPIO1_BASE_ADDR), +		.irq = MXC_INT_GPIO1, +		.virtual_irq_start = MXC_GPIO_INT_BASE +	}, +	[1] = { +		.chip.label = "gpio-1", +		.base = IO_ADDRESS(GPIO2_BASE_ADDR), +		.irq = MXC_INT_GPIO2, +		.virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN +	}, +	[2] = { +		.chip.label = "gpio-2", +		.base = IO_ADDRESS(GPIO3_BASE_ADDR), +		.irq = MXC_INT_GPIO3, +		.virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2 +	} +}; + +int __init mxc_register_gpios(void) +{ +	return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports)); +} diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile index 3eb181cb7afb..66272a6fc323 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile @@ -3,4 +3,4 @@  #  # Common support -obj-y := irq.o clock.o +obj-y := irq.o clock.o gpio.o diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c new file mode 100644 index 000000000000..4a7736717d86 --- /dev/null +++ b/arch/arm/plat-mxc/gpio.c @@ -0,0 +1,253 @@ +/* + * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de> + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * Based on code from Freescale, + * Copyright 2004-2006 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. + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <asm/hardware.h> +#include <asm-generic/bug.h> + +static struct mxc_gpio_port *mxc_gpio_ports; +static int gpio_table_size; + +/* Note: This driver assumes 32 GPIOs are handled in one register */ + +static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index) +{ +	__raw_writel(1 << index, port->base + GPIO_ISR); +} + +static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, +				int enable) +{ +	u32 l; + +	l = __raw_readl(port->base + GPIO_IMR); +	l = (l & (~(1 << index))) | (!!enable << index); +	__raw_writel(l, port->base + GPIO_IMR); +} + +static void gpio_ack_irq(u32 irq) +{ +	u32 gpio = irq_to_gpio(irq); +	_clear_gpio_irqstatus(&mxc_gpio_ports[gpio / 32], gpio & 0x1f); +} + +static void gpio_mask_irq(u32 irq) +{ +	u32 gpio = irq_to_gpio(irq); +	_set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 0); +} + +static void gpio_unmask_irq(u32 irq) +{ +	u32 gpio = irq_to_gpio(irq); +	_set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1); +} + +static int gpio_set_irq_type(u32 irq, u32 type) +{ +	u32 gpio = irq_to_gpio(irq); +	struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; +	u32 bit, val; +	int edge; +	void __iomem *reg = port->base; + +	switch (type) { +	case IRQT_RISING: +		edge = GPIO_INT_RISE_EDGE; +		break; +	case IRQT_FALLING: +		edge = GPIO_INT_FALL_EDGE; +		break; +	case IRQT_LOW: +		edge = GPIO_INT_LOW_LEV; +		break; +	case IRQT_HIGH: +		edge = GPIO_INT_HIGH_LEV; +		break; +	default:	/* this includes IRQT_BOTHEDGE */ +		return -EINVAL; +	} + +	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ +	bit = gpio & 0xf; +	val = __raw_readl(reg) & ~(0x3 << (bit << 1)); +	__raw_writel(val | (edge << (bit << 1)), reg); +	_clear_gpio_irqstatus(port, gpio & 0x1f); + +	return 0; +} + +/* handle n interrupts in one status register */ +static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat) +{ +	u32 gpio_irq_no; + +	gpio_irq_no = port->virtual_irq_start; +	for (; irq_stat != 0; irq_stat >>= 1, gpio_irq_no++) { + +		if ((irq_stat & 1) == 0) +			continue; + +		BUG_ON(!(irq_desc[gpio_irq_no].handle_irq)); +		irq_desc[gpio_irq_no].handle_irq(gpio_irq_no, +				&irq_desc[gpio_irq_no]); +	} +} + +#ifdef CONFIG_ARCH_MX3 +/* MX3 has one interrupt *per* gpio port */ +static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) +{ +	u32 irq_stat; +	struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq); + +	irq_stat = __raw_readl(port->base + GPIO_ISR) & +			__raw_readl(port->base + GPIO_IMR); +	BUG_ON(!irq_stat); +	mxc_gpio_irq_handler(port, irq_stat); +} +#endif + +#ifdef CONFIG_ARCH_MX2 +/* MX2 has one interrupt *for all* gpio ports */ +static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) +{ +	int i; +	u32 irq_msk, irq_stat; +	struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq); + +	/* walk through all interrupt status registers */ +	for (i = 0; i < gpio_table_size; i++) { +		irq_msk = __raw_readl(port[i].base + GPIO_IMR); +		if (!irq_msk) +			continue; + +		irq_stat = __raw_readl(port[i].base + GPIO_ISR) & irq_msk; +		if (irq_stat) +			mxc_gpio_irq_handler(&port[i], irq_stat); +	} +} +#endif + +static struct irq_chip gpio_irq_chip = { +	.ack = gpio_ack_irq, +	.mask = gpio_mask_irq, +	.unmask = gpio_unmask_irq, +	.set_type = gpio_set_irq_type, +}; + +static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, +				int dir) +{ +	struct mxc_gpio_port *port = +		container_of(chip, struct mxc_gpio_port, chip); +	u32 l; + +	l = __raw_readl(port->base + GPIO_GDIR); +	if (dir) +		l |= 1 << offset; +	else +		l &= ~(1 << offset); +	__raw_writel(l, port->base + GPIO_GDIR); +} + +static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ +	struct mxc_gpio_port *port = +		container_of(chip, struct mxc_gpio_port, chip); +	void __iomem *reg = port->base + GPIO_DR; +	u32 l; + +	l = (__raw_readl(reg) & (~(1 << offset))) | (value << offset); +	__raw_writel(l, reg); +} + +static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset) +{ +	struct mxc_gpio_port *port = +		container_of(chip, struct mxc_gpio_port, chip); + +	return (__raw_readl(port->base + GPIO_DR) >> offset) & 1; +} + +static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ +	_set_gpio_direction(chip, offset, 0); +	return 0; +} + +static int mxc_gpio_direction_output(struct gpio_chip *chip, +				     unsigned offset, int value) +{ +	_set_gpio_direction(chip, offset, 1); +	mxc_gpio_set(chip, offset, value); +	return 0; +} + +int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt) +{ +	int i, j; + +	/* save for local usage */ +	mxc_gpio_ports = port; +	gpio_table_size = cnt; + +	printk(KERN_INFO "MXC GPIO hardware\n"); + +	for (i = 0; i < cnt; i++) { +		/* disable the interrupt and clear the status */ +		__raw_writel(0, port[i].base + GPIO_IMR); +		__raw_writel(~0, port[i].base + GPIO_ISR); +		for (j = port[i].virtual_irq_start; +			j < port[i].virtual_irq_start + 32; j++) { +			set_irq_chip(j, &gpio_irq_chip); +			set_irq_handler(j, handle_edge_irq); +			set_irq_flags(j, IRQF_VALID); +		} + +		/* register gpio chip */ +		port[i].chip.direction_input = mxc_gpio_direction_input; +		port[i].chip.direction_output = mxc_gpio_direction_output; +		port[i].chip.get = mxc_gpio_get; +		port[i].chip.set = mxc_gpio_set; +		port[i].chip.base = i * 32; +		port[i].chip.ngpio = 32; + +		/* its a serious configuration bug when it fails */ +		BUG_ON( gpiochip_add(&port[i].chip) < 0 ); + +#ifdef CONFIG_ARCH_MX3 +		/* setup one handler for each entry */ +		set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler); +		set_irq_data(port[i].irq, &port[i]); +#endif +	} + +#ifdef CONFIG_ARCH_MX2 +	/* setup one handler for all GPIO interrupts */ +	set_irq_chained_handler(port[0].irq, mx2_gpio_irq_handler); +	set_irq_data(port[0].irq, port); +#endif +	return 0; +} diff --git a/arch/arm/plat-mxc/irq.c b/arch/arm/plat-mxc/irq.c index 2ad5a6917b3f..88f0cfababda 100644 --- a/arch/arm/plat-mxc/irq.c +++ b/arch/arm/plat-mxc/irq.c @@ -71,5 +71,8 @@ void __init mxc_init_irq(void)  	reg |= (0xF << 28);  	__raw_writel(reg, AVIC_NIPRIORITY6); +	/* init architectures chained interrupt handler */ +	mxc_register_gpios(); +  	printk(KERN_INFO "MXC IRQ initialized\n");  } diff --git a/include/asm-arm/arch-mxc/common.h b/include/asm-arm/arch-mxc/common.h index c6d4aa360635..8774783ed984 100644 --- a/include/asm-arm/arch-mxc/common.h +++ b/include/asm-arm/arch-mxc/common.h @@ -17,5 +17,6 @@ extern void mxc_map_io(void);  extern void mxc_init_irq(void);  extern struct sys_timer mxc_timer;  extern int mxc_clocks_init(unsigned long fref); +extern int mxc_register_gpios(void);  #endif diff --git a/include/asm-arm/arch-mxc/gpio.h b/include/asm-arm/arch-mxc/gpio.h new file mode 100644 index 000000000000..d393e15f5a6b --- /dev/null +++ b/include/asm-arm/arch-mxc/gpio.h @@ -0,0 +1,42 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * 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. + */ + +#ifndef __ASM_ARCH_MXC_GPIO_H__ +#define __ASM_ARCH_MXC_GPIO_H__ + +#include <asm/hardware.h> +#include <asm-generic/gpio.h> + +/* use gpiolib dispatchers */ +#define gpio_get_value		__gpio_get_value +#define gpio_set_value		__gpio_set_value +#define gpio_cansleep		__gpio_cansleep + +#define gpio_to_irq(gpio)	(MXC_MAX_INT_LINES + (gpio)) +#define irq_to_gpio(irq)	((irq) - MXC_MAX_INT_LINES) + +struct mxc_gpio_port { +	void __iomem *base; +	int irq; +	int virtual_irq_start; +	struct gpio_chip chip; +}; + +int mxc_gpio_init(struct mxc_gpio_port*, int); + +#endif diff --git a/include/asm-arm/arch-mxc/mx31.h b/include/asm-arm/arch-mxc/mx31.h index 36a1af495bb3..98e6a4cd1ea9 100644 --- a/include/asm-arm/arch-mxc/mx31.h +++ b/include/asm-arm/arch-mxc/mx31.h @@ -347,6 +347,25 @@  #define SYSTEM_REV_MIN		CHIP_REV_1_0  #define SYSTEM_REV_NUM		3 +/* gpio and gpio based interrupt handling */ +#define GPIO_DR		 	0x00 +#define GPIO_GDIR	 	0x04 +#define GPIO_PSR	 	0x08 +#define GPIO_ICR1	 	0x0C +#define GPIO_ICR2	 	0x10 +#define GPIO_IMR	 	0x14 +#define GPIO_ISR	 	0x18 +#define GPIO_INT_LOW_LEV	0x0 +#define GPIO_INT_HIGH_LEV	0x1 +#define GPIO_INT_RISE_EDGE	0x2 +#define GPIO_INT_FALL_EDGE	0x3 +#define GPIO_INT_NONE		0x4 + +/* Mandatory defines used globally */ + +/* this CPU supports up to 96 GPIOs */ +#define ARCH_NR_GPIOS		96 +  #if !defined(__ASSEMBLY__) && !defined(__MXC_BOOT_UNCOMPRESS)  /* this is a i.MX31 CPU */ | 
