/* 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); }