Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 100873
b: refs/heads/master
c: 07bd1a6
h: refs/heads/master
i:
  100871: 8a4daa9
v: v3
  • Loading branch information
Juergen Beisert authored and Robert Schwebel committed Jul 5, 2008
1 parent a9e8ea4 commit b4a36bf
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e3d13ff4b9d3b05d7a969153e2c049548e25deea
refs/heads/master: 07bd1a6cc7cbb3f373fbe49b204c6cde5e9155fc
2 changes: 2 additions & 0 deletions trunk/arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
27 changes: 27 additions & 0 deletions trunk/arch/arm/mach-mx3/devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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));
}
2 changes: 1 addition & 1 deletion trunk/arch/arm/plat-mxc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
#

# Common support
obj-y := irq.o clock.o
obj-y := irq.o clock.o gpio.o
253 changes: 253 additions & 0 deletions trunk/arch/arm/plat-mxc/gpio.c
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 3 additions & 0 deletions trunk/arch/arm/plat-mxc/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
1 change: 1 addition & 0 deletions trunk/include/asm-arm/arch-mxc/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
42 changes: 42 additions & 0 deletions trunk/include/asm-arm/arch-mxc/gpio.h
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions trunk/include/asm-arm/arch-mxc/mx31.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit b4a36bf

Please sign in to comment.