diff options
Diffstat (limited to 'arch/arm/mach-vt8500/gpio.c')
-rw-r--r-- | arch/arm/mach-vt8500/gpio.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c new file mode 100644 index 000000000000..2bcc0ec783df --- /dev/null +++ b/arch/arm/mach-vt8500/gpio.c @@ -0,0 +1,240 @@ +/* linux/arch/arm/mach-vt8500/gpio.c + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include "devices.h" + +#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip) + +#define ENABLE_REGS 0x0 +#define DIRECTION_REGS 0x20 +#define OUTVALUE_REGS 0x40 +#define INVALUE_REGS 0x60 + +#define EXT_REGOFF 0x1c + +static void __iomem *regbase; + +struct vt8500_gpio_chip { + struct gpio_chip chip; + unsigned int shift; + unsigned int regoff; +}; + +static int gpio_to_irq_map[8]; + +static int vt8500_muxed_gpio_request(struct gpio_chip *chip, + unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + unsigned val = readl(regbase + ENABLE_REGS + vt8500_chip->regoff); + + val |= (1 << vt8500_chip->shift << offset); + writel(val, regbase + ENABLE_REGS + vt8500_chip->regoff); + + return 0; +} + +static void vt8500_muxed_gpio_free(struct gpio_chip *chip, + unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + unsigned val = readl(regbase + ENABLE_REGS + vt8500_chip->regoff); + + val &= ~(1 << vt8500_chip->shift << offset); + writel(val, regbase + ENABLE_REGS + vt8500_chip->regoff); +} + +static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + unsigned val = readl(regbase + DIRECTION_REGS + vt8500_chip->regoff); + + val &= ~(1 << vt8500_chip->shift << offset); + writel(val, regbase + DIRECTION_REGS + vt8500_chip->regoff); + + return 0; +} + +static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + unsigned val = readl(regbase + DIRECTION_REGS + vt8500_chip->regoff); + + val |= (1 << vt8500_chip->shift << offset); + writel(val, regbase + DIRECTION_REGS + vt8500_chip->regoff); + + if (value) { + val = readl(regbase + OUTVALUE_REGS + vt8500_chip->regoff); + val |= (1 << vt8500_chip->shift << offset); + writel(val, regbase + OUTVALUE_REGS + vt8500_chip->regoff); + } + return 0; +} + +static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip, + unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + return (readl(regbase + INVALUE_REGS + vt8500_chip->regoff) + >> vt8500_chip->shift >> offset) & 1; +} + +static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + unsigned val = readl(regbase + INVALUE_REGS + vt8500_chip->regoff); + + if (value) + val |= (1 << vt8500_chip->shift << offset); + else + val &= ~(1 << vt8500_chip->shift << offset); + + writel(val, regbase + INVALUE_REGS + vt8500_chip->regoff); +} + +#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num) \ +{ \ + .chip = { \ + .label = __name, \ + .request = vt8500_muxed_gpio_request, \ + .free = vt8500_muxed_gpio_free, \ + .direction_input = vt8500_muxed_gpio_direction_input, \ + .direction_output = vt8500_muxed_gpio_direction_output, \ + .get = vt8500_muxed_gpio_get_value, \ + .set = vt8500_muxed_gpio_set_value, \ + .can_sleep = 0, \ + .base = __base, \ + .ngpio = __num, \ + }, \ + .shift = __shift, \ + .regoff = __off, \ +} + +static struct vt8500_gpio_chip vt8500_muxed_gpios[] = { + VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4), + VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4), + VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4), + VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4), + VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4), + VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2), + + VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11), + VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7), + VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2), + VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2), + + VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20), + VT8500_GPIO_BANK("see", 20, 0x8, 72, 4), + VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7), + + VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19), + + VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11), + + VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23), +}; + +static int vt8500_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + unsigned val = readl(regbase + DIRECTION_REGS + EXT_REGOFF); + + val &= ~(1 << offset); + writel(val, regbase + DIRECTION_REGS + EXT_REGOFF); + return 0; +} + +static int vt8500_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + unsigned val = readl(regbase + DIRECTION_REGS + EXT_REGOFF); + + val |= (1 << offset); + writel(val, regbase + DIRECTION_REGS + EXT_REGOFF); + + if (value) { + val = readl(regbase + OUTVALUE_REGS + EXT_REGOFF); + val |= (1 << offset); + writel(val, regbase + OUTVALUE_REGS + EXT_REGOFF); + } + return 0; +} + +static int vt8500_gpio_get_value(struct gpio_chip *chip, + unsigned offset) +{ + return (readl(regbase + INVALUE_REGS + EXT_REGOFF) >> offset) & 1; +} + +static void vt8500_gpio_set_value(struct gpio_chip *chip, + unsigned offset, int value) +{ + unsigned val = readl(regbase + OUTVALUE_REGS + EXT_REGOFF); + + if (value) + val |= (1 << offset); + else + val &= ~(1 << offset); + + writel(val, regbase + OUTVALUE_REGS + EXT_REGOFF); +} + +static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + if (offset > 7) + return -EINVAL; + + return gpio_to_irq_map[offset]; +} + +static struct gpio_chip vt8500_external_gpios = { + .label = "extgpio", + .direction_input = vt8500_gpio_direction_input, + .direction_output = vt8500_gpio_direction_output, + .get = vt8500_gpio_get_value, + .set = vt8500_gpio_set_value, + .to_irq = vt8500_gpio_to_irq, + .can_sleep = 0, + .base = 0, + .ngpio = 8, +}; + +void __init vt8500_gpio_init(void) +{ + int i; + + for (i = 0; i < 8; i++) + gpio_to_irq_map[i] = wmt_gpio_ext_irq[i]; + + regbase = ioremap(wmt_gpio_base, SZ_64K); + if (!regbase) { + printk(KERN_ERR "Failed to map MMIO registers for GPIO\n"); + return; + } + + gpiochip_add(&vt8500_external_gpios); + + for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++) + gpiochip_add(&vt8500_muxed_gpios[i].chip); +} |