|
@@ -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);
|
|
|
+}
|