Skip to content

Commit 07bd1a6

Browse files
Juergen Beisertrschwebel
authored andcommitted
MXC arch: Add gpio support for the whole platform
This patch bases on the one from Daniel Mack. The most important change to Daniel's patch is to be more generic. This gpio routine supports at least the i.MX27 and i.MX31 processors. Signed-off-by: Juergen Beisert <[email protected]> Acked-by: Daniel Mack <[email protected]>
1 parent e3d13ff commit 07bd1a6

File tree

8 files changed

+348
-1
lines changed

8 files changed

+348
-1
lines changed

arch/arm/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,8 @@ config ARCH_NS9XXX
368368
config ARCH_MXC
369369
bool "Freescale MXC/iMX-based"
370370
select ARCH_MTD_XIP
371+
select GENERIC_GPIO
372+
select HAVE_GPIO_LIB
371373
help
372374
Support for Freescale MXC/iMX-based family of processors
373375

arch/arm/mach-mx3/devices.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/module.h>
2121
#include <linux/platform_device.h>
2222
#include <linux/serial.h>
23+
#include <linux/gpio.h>
2324
#include <asm/hardware.h>
2425
#include <asm/arch/imx-uart.h>
2526

@@ -151,3 +152,29 @@ int __init imx_init_uart(int uart_no, struct imxuart_platform_data *pdata)
151152
return 0;
152153
}
153154

155+
/* GPIO port description */
156+
static struct mxc_gpio_port imx_gpio_ports[] = {
157+
[0] = {
158+
.chip.label = "gpio-0",
159+
.base = IO_ADDRESS(GPIO1_BASE_ADDR),
160+
.irq = MXC_INT_GPIO1,
161+
.virtual_irq_start = MXC_GPIO_INT_BASE
162+
},
163+
[1] = {
164+
.chip.label = "gpio-1",
165+
.base = IO_ADDRESS(GPIO2_BASE_ADDR),
166+
.irq = MXC_INT_GPIO2,
167+
.virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN
168+
},
169+
[2] = {
170+
.chip.label = "gpio-2",
171+
.base = IO_ADDRESS(GPIO3_BASE_ADDR),
172+
.irq = MXC_INT_GPIO3,
173+
.virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
174+
}
175+
};
176+
177+
int __init mxc_register_gpios(void)
178+
{
179+
return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports));
180+
}

arch/arm/plat-mxc/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
#
44

55
# Common support
6-
obj-y := irq.o clock.o
6+
obj-y := irq.o clock.o gpio.o

arch/arm/plat-mxc/gpio.c

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
* MXC GPIO support. (c) 2008 Daniel Mack <[email protected]>
3+
* Copyright 2008 Juergen Beisert, [email protected]
4+
*
5+
* Based on code from Freescale,
6+
* Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
7+
*
8+
* This program is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU General Public License
10+
* as published by the Free Software Foundation; either version 2
11+
* of the License, or (at your option) any later version.
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20+
*/
21+
22+
#include <linux/init.h>
23+
#include <linux/io.h>
24+
#include <linux/irq.h>
25+
#include <linux/gpio.h>
26+
#include <asm/hardware.h>
27+
#include <asm-generic/bug.h>
28+
29+
static struct mxc_gpio_port *mxc_gpio_ports;
30+
static int gpio_table_size;
31+
32+
/* Note: This driver assumes 32 GPIOs are handled in one register */
33+
34+
static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index)
35+
{
36+
__raw_writel(1 << index, port->base + GPIO_ISR);
37+
}
38+
39+
static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index,
40+
int enable)
41+
{
42+
u32 l;
43+
44+
l = __raw_readl(port->base + GPIO_IMR);
45+
l = (l & (~(1 << index))) | (!!enable << index);
46+
__raw_writel(l, port->base + GPIO_IMR);
47+
}
48+
49+
static void gpio_ack_irq(u32 irq)
50+
{
51+
u32 gpio = irq_to_gpio(irq);
52+
_clear_gpio_irqstatus(&mxc_gpio_ports[gpio / 32], gpio & 0x1f);
53+
}
54+
55+
static void gpio_mask_irq(u32 irq)
56+
{
57+
u32 gpio = irq_to_gpio(irq);
58+
_set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 0);
59+
}
60+
61+
static void gpio_unmask_irq(u32 irq)
62+
{
63+
u32 gpio = irq_to_gpio(irq);
64+
_set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1);
65+
}
66+
67+
static int gpio_set_irq_type(u32 irq, u32 type)
68+
{
69+
u32 gpio = irq_to_gpio(irq);
70+
struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32];
71+
u32 bit, val;
72+
int edge;
73+
void __iomem *reg = port->base;
74+
75+
switch (type) {
76+
case IRQT_RISING:
77+
edge = GPIO_INT_RISE_EDGE;
78+
break;
79+
case IRQT_FALLING:
80+
edge = GPIO_INT_FALL_EDGE;
81+
break;
82+
case IRQT_LOW:
83+
edge = GPIO_INT_LOW_LEV;
84+
break;
85+
case IRQT_HIGH:
86+
edge = GPIO_INT_HIGH_LEV;
87+
break;
88+
default: /* this includes IRQT_BOTHEDGE */
89+
return -EINVAL;
90+
}
91+
92+
reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
93+
bit = gpio & 0xf;
94+
val = __raw_readl(reg) & ~(0x3 << (bit << 1));
95+
__raw_writel(val | (edge << (bit << 1)), reg);
96+
_clear_gpio_irqstatus(port, gpio & 0x1f);
97+
98+
return 0;
99+
}
100+
101+
/* handle n interrupts in one status register */
102+
static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
103+
{
104+
u32 gpio_irq_no;
105+
106+
gpio_irq_no = port->virtual_irq_start;
107+
for (; irq_stat != 0; irq_stat >>= 1, gpio_irq_no++) {
108+
109+
if ((irq_stat & 1) == 0)
110+
continue;
111+
112+
BUG_ON(!(irq_desc[gpio_irq_no].handle_irq));
113+
irq_desc[gpio_irq_no].handle_irq(gpio_irq_no,
114+
&irq_desc[gpio_irq_no]);
115+
}
116+
}
117+
118+
#ifdef CONFIG_ARCH_MX3
119+
/* MX3 has one interrupt *per* gpio port */
120+
static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
121+
{
122+
u32 irq_stat;
123+
struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq);
124+
125+
irq_stat = __raw_readl(port->base + GPIO_ISR) &
126+
__raw_readl(port->base + GPIO_IMR);
127+
BUG_ON(!irq_stat);
128+
mxc_gpio_irq_handler(port, irq_stat);
129+
}
130+
#endif
131+
132+
#ifdef CONFIG_ARCH_MX2
133+
/* MX2 has one interrupt *for all* gpio ports */
134+
static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
135+
{
136+
int i;
137+
u32 irq_msk, irq_stat;
138+
struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq);
139+
140+
/* walk through all interrupt status registers */
141+
for (i = 0; i < gpio_table_size; i++) {
142+
irq_msk = __raw_readl(port[i].base + GPIO_IMR);
143+
if (!irq_msk)
144+
continue;
145+
146+
irq_stat = __raw_readl(port[i].base + GPIO_ISR) & irq_msk;
147+
if (irq_stat)
148+
mxc_gpio_irq_handler(&port[i], irq_stat);
149+
}
150+
}
151+
#endif
152+
153+
static struct irq_chip gpio_irq_chip = {
154+
.ack = gpio_ack_irq,
155+
.mask = gpio_mask_irq,
156+
.unmask = gpio_unmask_irq,
157+
.set_type = gpio_set_irq_type,
158+
};
159+
160+
static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset,
161+
int dir)
162+
{
163+
struct mxc_gpio_port *port =
164+
container_of(chip, struct mxc_gpio_port, chip);
165+
u32 l;
166+
167+
l = __raw_readl(port->base + GPIO_GDIR);
168+
if (dir)
169+
l |= 1 << offset;
170+
else
171+
l &= ~(1 << offset);
172+
__raw_writel(l, port->base + GPIO_GDIR);
173+
}
174+
175+
static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
176+
{
177+
struct mxc_gpio_port *port =
178+
container_of(chip, struct mxc_gpio_port, chip);
179+
void __iomem *reg = port->base + GPIO_DR;
180+
u32 l;
181+
182+
l = (__raw_readl(reg) & (~(1 << offset))) | (value << offset);
183+
__raw_writel(l, reg);
184+
}
185+
186+
static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset)
187+
{
188+
struct mxc_gpio_port *port =
189+
container_of(chip, struct mxc_gpio_port, chip);
190+
191+
return (__raw_readl(port->base + GPIO_DR) >> offset) & 1;
192+
}
193+
194+
static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
195+
{
196+
_set_gpio_direction(chip, offset, 0);
197+
return 0;
198+
}
199+
200+
static int mxc_gpio_direction_output(struct gpio_chip *chip,
201+
unsigned offset, int value)
202+
{
203+
_set_gpio_direction(chip, offset, 1);
204+
mxc_gpio_set(chip, offset, value);
205+
return 0;
206+
}
207+
208+
int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
209+
{
210+
int i, j;
211+
212+
/* save for local usage */
213+
mxc_gpio_ports = port;
214+
gpio_table_size = cnt;
215+
216+
printk(KERN_INFO "MXC GPIO hardware\n");
217+
218+
for (i = 0; i < cnt; i++) {
219+
/* disable the interrupt and clear the status */
220+
__raw_writel(0, port[i].base + GPIO_IMR);
221+
__raw_writel(~0, port[i].base + GPIO_ISR);
222+
for (j = port[i].virtual_irq_start;
223+
j < port[i].virtual_irq_start + 32; j++) {
224+
set_irq_chip(j, &gpio_irq_chip);
225+
set_irq_handler(j, handle_edge_irq);
226+
set_irq_flags(j, IRQF_VALID);
227+
}
228+
229+
/* register gpio chip */
230+
port[i].chip.direction_input = mxc_gpio_direction_input;
231+
port[i].chip.direction_output = mxc_gpio_direction_output;
232+
port[i].chip.get = mxc_gpio_get;
233+
port[i].chip.set = mxc_gpio_set;
234+
port[i].chip.base = i * 32;
235+
port[i].chip.ngpio = 32;
236+
237+
/* its a serious configuration bug when it fails */
238+
BUG_ON( gpiochip_add(&port[i].chip) < 0 );
239+
240+
#ifdef CONFIG_ARCH_MX3
241+
/* setup one handler for each entry */
242+
set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler);
243+
set_irq_data(port[i].irq, &port[i]);
244+
#endif
245+
}
246+
247+
#ifdef CONFIG_ARCH_MX2
248+
/* setup one handler for all GPIO interrupts */
249+
set_irq_chained_handler(port[0].irq, mx2_gpio_irq_handler);
250+
set_irq_data(port[0].irq, port);
251+
#endif
252+
return 0;
253+
}

arch/arm/plat-mxc/irq.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,8 @@ void __init mxc_init_irq(void)
7171
reg |= (0xF << 28);
7272
__raw_writel(reg, AVIC_NIPRIORITY6);
7373

74+
/* init architectures chained interrupt handler */
75+
mxc_register_gpios();
76+
7477
printk(KERN_INFO "MXC IRQ initialized\n");
7578
}

include/asm-arm/arch-mxc/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ extern void mxc_map_io(void);
1717
extern void mxc_init_irq(void);
1818
extern struct sys_timer mxc_timer;
1919
extern int mxc_clocks_init(unsigned long fref);
20+
extern int mxc_register_gpios(void);
2021

2122
#endif

include/asm-arm/arch-mxc/gpio.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
3+
* Copyright 2008 Juergen Beisert, [email protected]
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU General Public License
7+
* as published by the Free Software Foundation; either version 2
8+
* of the License, or (at your option) any later version.
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
19+
#ifndef __ASM_ARCH_MXC_GPIO_H__
20+
#define __ASM_ARCH_MXC_GPIO_H__
21+
22+
#include <asm/hardware.h>
23+
#include <asm-generic/gpio.h>
24+
25+
/* use gpiolib dispatchers */
26+
#define gpio_get_value __gpio_get_value
27+
#define gpio_set_value __gpio_set_value
28+
#define gpio_cansleep __gpio_cansleep
29+
30+
#define gpio_to_irq(gpio) (MXC_MAX_INT_LINES + (gpio))
31+
#define irq_to_gpio(irq) ((irq) - MXC_MAX_INT_LINES)
32+
33+
struct mxc_gpio_port {
34+
void __iomem *base;
35+
int irq;
36+
int virtual_irq_start;
37+
struct gpio_chip chip;
38+
};
39+
40+
int mxc_gpio_init(struct mxc_gpio_port*, int);
41+
42+
#endif

include/asm-arm/arch-mxc/mx31.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,25 @@
347347
#define SYSTEM_REV_MIN CHIP_REV_1_0
348348
#define SYSTEM_REV_NUM 3
349349

350+
/* gpio and gpio based interrupt handling */
351+
#define GPIO_DR 0x00
352+
#define GPIO_GDIR 0x04
353+
#define GPIO_PSR 0x08
354+
#define GPIO_ICR1 0x0C
355+
#define GPIO_ICR2 0x10
356+
#define GPIO_IMR 0x14
357+
#define GPIO_ISR 0x18
358+
#define GPIO_INT_LOW_LEV 0x0
359+
#define GPIO_INT_HIGH_LEV 0x1
360+
#define GPIO_INT_RISE_EDGE 0x2
361+
#define GPIO_INT_FALL_EDGE 0x3
362+
#define GPIO_INT_NONE 0x4
363+
364+
/* Mandatory defines used globally */
365+
366+
/* this CPU supports up to 96 GPIOs */
367+
#define ARCH_NR_GPIOS 96
368+
350369
#if !defined(__ASSEMBLY__) && !defined(__MXC_BOOT_UNCOMPRESS)
351370

352371
/* this is a i.MX31 CPU */

0 commit comments

Comments
 (0)