From 491e6bdbc28298bc5021d805f40427f5b301c2ba Mon Sep 17 00:00:00 2001 From: Paulius Zaleckas Date: Thu, 26 Mar 2009 10:06:27 +0200 Subject: [PATCH] --- yaml --- r: 137535 b: refs/heads/master c: 1df621ae2f3b03d557d962a7afec2b1d04558986 h: refs/heads/master i: 137533: 1b5b5c953b850b43901ff05b11042c10b0c0fd06 137531: 9ddacb9aa6fdf91c3760d172ecc4a72078a3c63a 137527: e4b462bb113d0bb2f722e6cd92cfc8deb1d98944 137519: 38476fc783791148eabe232b05d871add9e09ea5 137503: b9fedce52f191125bccf424c2aed5cdf207e5d27 137471: 2e32a7919a9197a6c935e4b230db622908d1ad2e v: v3 --- [refs] | 2 +- trunk/arch/arm/Kconfig | 2 + trunk/arch/arm/mach-gemini/Makefile | 2 +- trunk/arch/arm/mach-gemini/common.h | 1 + trunk/arch/arm/mach-gemini/gpio.c | 232 ++++++++++++++++++ .../arch/arm/mach-gemini/include/mach/gpio.h | 25 ++ .../arch/arm/mach-gemini/include/mach/irqs.h | 5 +- 7 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 trunk/arch/arm/mach-gemini/gpio.c create mode 100644 trunk/arch/arm/mach-gemini/include/mach/gpio.h diff --git a/[refs] b/[refs] index e400ba24d232..b2720d4aabcc 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 881a95f976e687307b41ba3c767561f533485c7e +refs/heads/master: 1df621ae2f3b03d557d962a7afec2b1d04558986 diff --git a/trunk/arch/arm/Kconfig b/trunk/arch/arm/Kconfig index 5686f4074dd0..5b0204083e0e 100644 --- a/trunk/arch/arm/Kconfig +++ b/trunk/arch/arm/Kconfig @@ -278,6 +278,8 @@ config ARCH_EP93XX config ARCH_GEMINI bool "Cortina Systems Gemini" select CPU_FA526 + select GENERIC_GPIO + select ARCH_REQUIRE_GPIOLIB help Support for the Cortina Systems Gemini family SoCs diff --git a/trunk/arch/arm/mach-gemini/Makefile b/trunk/arch/arm/mach-gemini/Makefile index 133e2050685e..e2835cdb0d24 100644 --- a/trunk/arch/arm/mach-gemini/Makefile +++ b/trunk/arch/arm/mach-gemini/Makefile @@ -4,4 +4,4 @@ # Object file lists. -obj-y := irq.o mm.o time.o devices.o +obj-y := irq.o mm.o time.o devices.o gpio.o diff --git a/trunk/arch/arm/mach-gemini/common.h b/trunk/arch/arm/mach-gemini/common.h index 9c1afa1c5803..9392834a214f 100644 --- a/trunk/arch/arm/mach-gemini/common.h +++ b/trunk/arch/arm/mach-gemini/common.h @@ -17,6 +17,7 @@ struct mtd_partition; extern void gemini_map_io(void); extern void gemini_init_irq(void); extern void gemini_timer_init(void); +extern void gemini_gpio_init(void); /* Common platform devices registration functions */ extern int platform_register_uart(void); diff --git a/trunk/arch/arm/mach-gemini/gpio.c b/trunk/arch/arm/mach-gemini/gpio.c new file mode 100644 index 000000000000..e7263854bc7b --- /dev/null +++ b/trunk/arch/arm/mach-gemini/gpio.c @@ -0,0 +1,232 @@ +/* + * Gemini gpiochip and interrupt routines + * + * Copyright (C) 2008-2009 Paulius Zaleckas + * + * Based on plat-mxc/gpio.c: + * MXC GPIO support. (c) 2008 Daniel Mack + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define GPIO_BASE(x) IO_ADDRESS(GEMINI_GPIO_BASE(x)) + +/* GPIO registers definition */ +#define GPIO_DATA_OUT 0x0 +#define GPIO_DATA_IN 0x4 +#define GPIO_DIR 0x8 +#define GPIO_DATA_SET 0x10 +#define GPIO_DATA_CLR 0x14 +#define GPIO_PULL_EN 0x18 +#define GPIO_PULL_TYPE 0x1C +#define GPIO_INT_EN 0x20 +#define GPIO_INT_STAT 0x24 +#define GPIO_INT_MASK 0x2C +#define GPIO_INT_CLR 0x30 +#define GPIO_INT_TYPE 0x34 +#define GPIO_INT_BOTH_EDGE 0x38 +#define GPIO_INT_LEVEL 0x3C +#define GPIO_DEBOUNCE_EN 0x40 +#define GPIO_DEBOUNCE_PRESCALE 0x44 + +#define GPIO_PORT_NUM 3 + +static void _set_gpio_irqenable(unsigned int base, unsigned int index, + int enable) +{ + unsigned int reg; + + reg = __raw_readl(base + GPIO_INT_EN); + reg = (reg & (~(1 << index))) | (!!enable << index); + __raw_writel(reg, base + GPIO_INT_EN); +} + +static void gpio_ack_irq(unsigned int irq) +{ + unsigned int gpio = irq_to_gpio(irq); + unsigned int base = GPIO_BASE(gpio / 32); + + __raw_writel(1 << (gpio % 32), base + GPIO_INT_CLR); +} + +static void gpio_mask_irq(unsigned int irq) +{ + unsigned int gpio = irq_to_gpio(irq); + unsigned int base = GPIO_BASE(gpio / 32); + + _set_gpio_irqenable(base, gpio % 32, 0); +} + +static void gpio_unmask_irq(unsigned int irq) +{ + unsigned int gpio = irq_to_gpio(irq); + unsigned int base = GPIO_BASE(gpio / 32); + + _set_gpio_irqenable(base, gpio % 32, 1); +} + +static int gpio_set_irq_type(unsigned int irq, unsigned int type) +{ + unsigned int gpio = irq_to_gpio(irq); + unsigned int gpio_mask = 1 << (gpio % 32); + unsigned int base = GPIO_BASE(gpio / 32); + unsigned int reg_both, reg_level, reg_type; + + reg_type = __raw_readl(base + GPIO_INT_TYPE); + reg_level = __raw_readl(base + GPIO_INT_BOTH_EDGE); + reg_both = __raw_readl(base + GPIO_INT_BOTH_EDGE); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + reg_type &= ~gpio_mask; + reg_both |= gpio_mask; + break; + case IRQ_TYPE_EDGE_RISING: + reg_type &= ~gpio_mask; + reg_both &= ~gpio_mask; + reg_level &= ~gpio_mask; + break; + case IRQ_TYPE_EDGE_FALLING: + reg_type &= ~gpio_mask; + reg_both &= ~gpio_mask; + reg_level |= gpio_mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + reg_type |= gpio_mask; + reg_level &= ~gpio_mask; + break; + case IRQ_TYPE_LEVEL_LOW: + reg_type |= gpio_mask; + reg_level |= gpio_mask; + break; + default: + return -EINVAL; + } + + __raw_writel(reg_type, base + GPIO_INT_TYPE); + __raw_writel(reg_level, base + GPIO_INT_BOTH_EDGE); + __raw_writel(reg_both, base + GPIO_INT_BOTH_EDGE); + + gpio_ack_irq(irq); + + return 0; +} + +static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + unsigned int gpio_irq_no, irq_stat; + unsigned int port = (unsigned int)get_irq_data(irq); + + irq_stat = __raw_readl(GPIO_BASE(port) + GPIO_INT_STAT); + + gpio_irq_no = GPIO_IRQ_BASE + port * 32; + 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]); + } +} + +static struct irq_chip gpio_irq_chip = { + .name = "GPIO", + .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) +{ + unsigned int base = GPIO_BASE(offset / 32); + unsigned int reg; + + reg = __raw_readl(base + GPIO_DIR); + if (dir) + reg |= 1 << (offset % 32); + else + reg &= ~(1 << (offset % 32)); + __raw_writel(reg, base + GPIO_DIR); +} + +static void gemini_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + unsigned int base = GPIO_BASE(offset / 32); + + if (value) + __raw_writel(1 << (offset % 32), base + GPIO_DATA_SET); + else + __raw_writel(1 << (offset % 32), base + GPIO_DATA_CLR); +} + +static int gemini_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int base = GPIO_BASE(offset / 32); + + return (__raw_readl(base + GPIO_DATA_IN) >> (offset % 32)) & 1; +} + +static int gemini_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + _set_gpio_direction(chip, offset, 0); + return 0; +} + +static int gemini_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + _set_gpio_direction(chip, offset, 1); + gemini_gpio_set(chip, offset, value); + return 0; +} + +static struct gpio_chip gemini_gpio_chip = { + .label = "Gemini", + .direction_input = gemini_gpio_direction_input, + .get = gemini_gpio_get, + .direction_output = gemini_gpio_direction_output, + .set = gemini_gpio_set, + .base = 0, + .ngpio = GPIO_PORT_NUM * 32, +}; + +void __init gemini_gpio_init(void) +{ + int i, j; + + for (i = 0; i < GPIO_PORT_NUM; i++) { + /* disable, unmask and clear all interrupts */ + __raw_writel(0x0, GPIO_BASE(i) + GPIO_INT_EN); + __raw_writel(0x0, GPIO_BASE(i) + GPIO_INT_MASK); + __raw_writel(~0x0, GPIO_BASE(i) + GPIO_INT_CLR); + + for (j = GPIO_IRQ_BASE + i * 32; + j < GPIO_IRQ_BASE + (i + 1) * 32; j++) { + set_irq_chip(j, &gpio_irq_chip); + set_irq_handler(j, handle_edge_irq); + set_irq_flags(j, IRQF_VALID); + } + + set_irq_chained_handler(IRQ_GPIO(i), gpio_irq_handler); + set_irq_data(IRQ_GPIO(i), (void *)i); + } + + BUG_ON(gpiochip_add(&gemini_gpio_chip)); +} diff --git a/trunk/arch/arm/mach-gemini/include/mach/gpio.h b/trunk/arch/arm/mach-gemini/include/mach/gpio.h new file mode 100644 index 000000000000..3bc2c70f2989 --- /dev/null +++ b/trunk/arch/arm/mach-gemini/include/mach/gpio.h @@ -0,0 +1,25 @@ +/* + * Gemini gpiolib specific defines + * + * Copyright (C) 2008-2009 Paulius Zaleckas + * + * 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. + */ + +#ifndef __MACH_GPIO_H__ +#define __MACH_GPIO_H__ + +#include +#include + +#define gpio_get_value __gpio_get_value +#define gpio_set_value __gpio_set_value +#define gpio_cansleep __gpio_cansleep + +#define gpio_to_irq(x) ((x) + GPIO_IRQ_BASE) +#define irq_to_gpio(x) ((x) - GPIO_IRQ_BASE) + +#endif /* __MACH_GPIO_H__ */ diff --git a/trunk/arch/arm/mach-gemini/include/mach/irqs.h b/trunk/arch/arm/mach-gemini/include/mach/irqs.h index c7728ac458f3..06bc47e77e8b 100644 --- a/trunk/arch/arm/mach-gemini/include/mach/irqs.h +++ b/trunk/arch/arm/mach-gemini/include/mach/irqs.h @@ -43,8 +43,11 @@ #define NORMAL_IRQ_NUM 32 +#define GPIO_IRQ_BASE NORMAL_IRQ_NUM +#define GPIO_IRQ_NUM (3 * 32) + #define ARCH_TIMER_IRQ IRQ_TIMER2 -#define NR_IRQS NORMAL_IRQ_NUM +#define NR_IRQS (NORMAL_IRQ_NUM + GPIO_IRQ_NUM) #endif /* __MACH_IRQS_H__ */