diff options
Diffstat (limited to 'target/linux/cns21xx/patches-3.7/005-arm-add-fa-gpio-driver.patch')
-rw-r--r-- | target/linux/cns21xx/patches-3.7/005-arm-add-fa-gpio-driver.patch | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/target/linux/cns21xx/patches-3.7/005-arm-add-fa-gpio-driver.patch b/target/linux/cns21xx/patches-3.7/005-arm-add-fa-gpio-driver.patch new file mode 100644 index 0000000..d7e8e88 --- /dev/null +++ b/target/linux/cns21xx/patches-3.7/005-arm-add-fa-gpio-driver.patch @@ -0,0 +1,343 @@ +--- /dev/null ++++ b/arch/arm/plat-fa/gpio.c +@@ -0,0 +1,283 @@ ++/* ++ * Gpiochip and interrupt routines for Faraday FA526 based SoCs ++ * ++ * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> ++ * Copyright (C) 2010-2012 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * Based on plat-mxc/gpio.c: ++ * MXC GPIO supchip. (c) 2008 Daniel Mack <daniel@caiaq.de> ++ * 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 <linux/spinlock.h> ++ ++#include <plat/gpio.h> ++ ++#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_REGS_SIZE 0x48 ++ ++static DEFINE_SPINLOCK(fa_gpio_lock); ++ ++static inline struct fa_gpio_chip *to_fgc(struct gpio_chip *chip) ++{ ++ return container_of(chip, struct fa_gpio_chip, gpio_chip); ++} ++ ++static void _fa_gpio_irq_setenable(struct irq_data *d, int enable) ++{ ++ struct fa_gpio_chip *fgc = irq_get_chip_data(d->irq); ++ void __iomem *base = fgc->mem_base; ++ unsigned int gpio = d->irq - fgc->irq_base; ++ unsigned int reg; ++ ++ reg = __raw_readl(base + GPIO_INT_EN); ++ reg = (reg & (~(1 << gpio))) | (!!enable << gpio); ++ __raw_writel(reg, base + GPIO_INT_EN); ++} ++ ++static void fa_gpio_irq_ack(struct irq_data *d) ++{ ++ struct fa_gpio_chip *fgc = irq_get_chip_data(d->irq); ++ unsigned int gpio = d->irq - fgc->irq_base; ++ ++ __raw_writel(1 << gpio, fgc->mem_base + GPIO_INT_CLR); ++} ++ ++static void fa_gpio_irq_mask(struct irq_data *d) ++{ ++ _fa_gpio_irq_setenable(d, 0); ++} ++ ++static void fa_gpio_irq_unmask(struct irq_data *d) ++{ ++ _fa_gpio_irq_setenable(d, 1); ++} ++ ++static int fa_gpio_irq_set_type(struct irq_data *d, unsigned int type) ++{ ++ struct fa_gpio_chip *fgc = irq_get_chip_data(d->irq); ++ void __iomem *base = fgc->mem_base; ++ unsigned int gpio = d->irq - fgc->irq_base; ++ unsigned int gpio_mask = 1 << gpio; ++ unsigned int reg_both, reg_level, reg_type; ++ ++ reg_type = __raw_readl(base + GPIO_INT_TYPE); ++ reg_level = __raw_readl(base + GPIO_INT_LEVEL); ++ 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_LEVEL); ++ __raw_writel(reg_both, base + GPIO_INT_BOTH_EDGE); ++ ++ fa_gpio_irq_ack(d); ++ ++ return 0; ++} ++ ++static void fa_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ struct fa_gpio_data *data = irq_get_handler_data(irq); ++ unsigned int chip; ++ ++ for (chip = 0; chip < data->nchips; chip++) { ++ struct fa_gpio_chip *fgc = &data->chips[chip]; ++ unsigned int status; ++ unsigned int i; ++ ++ status = __raw_readl(fgc->mem_base + GPIO_INT_STAT); ++ for (i = fgc->irq_base; status != 0; status >>= 1, i++) { ++ if ((status & 1) == 0) ++ continue; ++ ++ BUG_ON(!(irq_desc[i].handle_irq)); ++ irq_desc[i].handle_irq(i, &irq_desc[i]); ++ } ++ } ++} ++ ++static struct irq_chip fa_gpio_irq_chip = { ++ .name = "GPIO", ++ .irq_ack = fa_gpio_irq_ack, ++ .irq_mask = fa_gpio_irq_mask, ++ .irq_unmask = fa_gpio_irq_unmask, ++ .irq_set_type = fa_gpio_irq_set_type, ++}; ++ ++static void _fa_gpio_set_direction(struct fa_gpio_chip *fgc, unsigned offset, ++ int is_output) ++{ ++ unsigned int reg; ++ ++ reg = __raw_readl(fgc->mem_base + GPIO_DIR); ++ if (is_output) ++ reg |= 1 << offset; ++ else ++ reg &= ~(1 << offset); ++ __raw_writel(reg, fgc->mem_base + GPIO_DIR); ++} ++ ++static void _fa_gpio_set(struct fa_gpio_chip *fgc, unsigned offset, int value) ++{ ++ if (value) ++ __raw_writel(1 << offset, fgc->mem_base + GPIO_DATA_SET); ++ else ++ __raw_writel(1 << offset, fgc->mem_base + GPIO_DATA_CLR); ++} ++ ++static void fa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ++{ ++ struct fa_gpio_chip *fgc = to_fgc(chip); ++ ++ _fa_gpio_set(fgc, offset, value); ++} ++ ++static int fa_gpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ struct fa_gpio_chip *fgc = to_fgc(chip); ++ ++ return (__raw_readl(fgc->mem_base + GPIO_DATA_IN) >> offset) & 1; ++} ++ ++static int fa_gpio_to_irq(struct gpio_chip *chip, unsigned offset) ++{ ++ struct fa_gpio_chip *fgc = to_fgc(chip); ++ ++ return fgc->irq_base + offset; ++} ++ ++static int fa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ++{ ++ struct fa_gpio_chip *fgc = to_fgc(chip); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fa_gpio_lock, flags); ++ ++ _fa_gpio_set_direction(fgc, offset, 0); ++ ++ spin_unlock_irqrestore(&fa_gpio_lock, flags); ++ ++ return 0; ++} ++ ++static int fa_gpio_direction_output(struct gpio_chip *chip, ++ unsigned offset, ++ int value) ++{ ++ struct fa_gpio_chip *fgc = to_fgc(chip); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fa_gpio_lock, flags); ++ ++ _fa_gpio_set(fgc, offset, value); ++ _fa_gpio_set_direction(fgc, offset, 1); ++ ++ spin_unlock_irqrestore(&fa_gpio_lock, flags); ++ ++ return 0; ++} ++ ++static int fa_gpio_init_chip(struct fa_gpio_chip *fgc) ++{ ++ void __iomem *mem_base; ++ unsigned int i; ++ int err; ++ ++ mem_base = ioremap(fgc->map_base, GPIO_REGS_SIZE); ++ if (!mem_base) ++ return -ENXIO; ++ ++ fgc->mem_base = mem_base; ++ ++ fgc->gpio_chip.direction_input = fa_gpio_direction_input; ++ fgc->gpio_chip.direction_output = fa_gpio_direction_output; ++ fgc->gpio_chip.get = fa_gpio_get; ++ fgc->gpio_chip.set = fa_gpio_set; ++ fgc->gpio_chip.to_irq = fa_gpio_to_irq; ++ ++ /* disable, unmask and clear all interrupts */ ++ __raw_writel(0x0, mem_base + GPIO_INT_EN); ++ __raw_writel(0x0, mem_base + GPIO_INT_MASK); ++ __raw_writel(~0x0, mem_base + GPIO_INT_CLR); ++ ++ for (i = fgc->irq_base; ++ i < fgc->irq_base + fgc->gpio_chip.ngpio; i++) { ++ irq_set_chip(i, &fa_gpio_irq_chip); ++ irq_set_chip_data(i, fgc); ++ irq_set_handler(i, handle_edge_irq); ++ set_irq_flags(i, IRQF_VALID); ++ } ++ ++ err = gpiochip_add(&fgc->gpio_chip); ++ if (err) ++ goto unmap; ++ ++ return 0; ++ ++ unmap: ++ iounmap(fgc->mem_base); ++ return err; ++} ++ ++void __init fa_gpio_init(struct fa_gpio_data *data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < data->nchips; i++) { ++ int err; ++ ++ err = fa_gpio_init_chip(&data->chips[i]); ++ if (WARN(err, "GPIO init failed\n")) ++ return; ++ } ++ ++ irq_set_chained_handler(data->irq, fa_gpio_irq_handler); ++ irq_set_handler_data(data->irq, data); ++} +--- /dev/null ++++ b/arch/arm/plat-fa/include/plat/gpio.h +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (c) 2010-2012 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This file is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License, Version 2, as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _FA_GPIO_H ++#define _FA_GPIO_H ++ ++#include <linux/init.h> ++#include <linux/gpio.h> ++#include <linux/irq.h> ++#include <linux/io.h> ++ ++struct fa_gpio_chip { ++ struct gpio_chip gpio_chip; ++ unsigned int map_base; ++ unsigned int irq_base; ++ ++ void __iomem *mem_base; ++}; ++ ++struct fa_gpio_data { ++ struct fa_gpio_chip *chips; ++ unsigned int nchips; ++ unsigned int irq; ++}; ++ ++void __init fa_gpio_init(struct fa_gpio_data *data); ++ ++#endif /* _FA_GPIO_H */ +--- a/arch/arm/plat-fa/Kconfig ++++ b/arch/arm/plat-fa/Kconfig +@@ -1,5 +1,8 @@ + if PLAT_FA + ++config PLAT_FA_GPIO ++ def_bool n ++ + config PLAT_FA_TIME + def_bool n + +--- a/arch/arm/plat-fa/Makefile ++++ b/arch/arm/plat-fa/Makefile +@@ -4,6 +4,7 @@ + + obj-y := + ++obj-$(CONFIG_PLAT_FA_GPIO) += gpio.o + obj-$(CONFIG_PLAT_FA_TIME) += time.o + + obj-m := |