diff options
author | Kevin | 2014-11-15 10:00:36 +0800 |
---|---|---|
committer | Kevin | 2014-11-15 10:00:36 +0800 |
commit | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (patch) | |
tree | de942df665fac4bac0d9cb7ae86910fe937b0c1a /ANDROID_3.4.5/arch/arm/mach-wmt/gpio.c | |
parent | 392e8802486cb573b916e746010e141a75f507e6 (diff) | |
download | FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.tar.gz FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.tar.bz2 FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.zip |
add via modify part source code for wm8880 4.4 kitkat
Diffstat (limited to 'ANDROID_3.4.5/arch/arm/mach-wmt/gpio.c')
-rwxr-xr-x | ANDROID_3.4.5/arch/arm/mach-wmt/gpio.c | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/ANDROID_3.4.5/arch/arm/mach-wmt/gpio.c b/ANDROID_3.4.5/arch/arm/mach-wmt/gpio.c new file mode 100755 index 00000000..7558c137 --- /dev/null +++ b/ANDROID_3.4.5/arch/arm/mach-wmt/gpio.c @@ -0,0 +1,614 @@ +/* linux/arch/arm/mach-wmt/gpio.c + * + * Copyright (c) 2013 WonderMedia Technologies, Inc. + * + * 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/module.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/seq_file.h> +#include <linux/kobject.h> +#include <linux/debugfs.h> + +#include <mach/hardware.h> +#include <mach/wmt_iomux.h> + +#undef WMT_PIN +#define WMT_PIN(__gp, __bit, __irq, __name) \ + { .label = #__name, .regoff = __gp, .shift = __bit, .irqnum = __irq }, + +const static struct wmt_gpio { + const char *label; + unsigned char regoff; + unsigned char shift; + int irqnum; +} wmt_gpios[] = { + #include <mach/iomux.h> +}; + +#define to_wmt(__chip) container_of(__chip, struct wmt_gpio_port, chip) + +struct wmt_gpio_port { + uint32_t base; + uint32_t gpio_irq_no_base; + int gpio_irq_nr; + struct gpio_chip chip; + spinlock_t lock; +}; + +static struct wmt_gpio_port wmt_gpio_port; + +#define INVALUE_REGS 0x00 +#define ENABLE_REGS 0x40 +#define DIRECTION_REGS 0x80 +#define OUTVALUE_REGS 0xc0 + +#define INTMASK_REGS 0x300 +#define INTSTAT_REGS 0x360 + +#define PULLENABLE_REGS 0x480 +#define PULLCONTROL_REGS 0x4c0 + +#define GPIO_INT_LOW_LEV 0x0 +#define GPIO_INT_HIGH_LEV 0x1 +#define GPIO_INT_FALL_EDGE 0x2 +#define GPIO_INT_RISE_EDGE 0x3 +#define GPIO_INT_BOTH_EDGE 0x4 + +#define CHIP_GPIO_BASE 0 + +static void _clear_gpio_irqstatus(u32 irqindex) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + u32 addr = port->base + INTSTAT_REGS + (irqindex >> 3); + u8 index = irqindex & 0x7; + + __raw_writeb(1 << index, addr); +} + +static int _gpio_irqstatus(u32 irqindex) +{ + u8 l; + struct wmt_gpio_port *port = &wmt_gpio_port; + u32 addr = port->base + INTSTAT_REGS + (irqindex >> 3); + u8 index = irqindex & 0x7; + l = __raw_readb(addr); + return l & (1<<index); +} + +int gpio_irqstatus(unsigned int gpio) +{ + int offset = gpio - CHIP_GPIO_BASE; + int irqindex = wmt_gpios[offset].irqnum; + return _gpio_irqstatus(irqindex); +} +EXPORT_SYMBOL(gpio_irqstatus); + +static void _set_gpio_irqenable(u32 irqindex, int enable) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + u32 addr = port->base + INTMASK_REGS + irqindex; + u8 l; + + l = __raw_readb(addr); + l = (l & 0x7f) | (!!enable << 7); + __raw_writeb(l, addr); +} + +int is_gpio_irqenable(unsigned int gpio) +{ + u8 l; + int offset = gpio - CHIP_GPIO_BASE; + int irqindex = wmt_gpios[offset].irqnum; + struct wmt_gpio_port *port = &wmt_gpio_port; + u32 addr = port->base + INTMASK_REGS + irqindex; + + l = __raw_readb(addr); + return (l & 0x80); +} +EXPORT_SYMBOL(is_gpio_irqenable); + +void wmt_gpio_ack_irq(unsigned int gpio) +{ + int offset = gpio - CHIP_GPIO_BASE; + int irqindex = wmt_gpios[offset].irqnum; + + _clear_gpio_irqstatus(irqindex); +} +EXPORT_SYMBOL(wmt_gpio_ack_irq); + +void wmt_gpio_mask_irq(unsigned int gpio) +{ + int offset = gpio - CHIP_GPIO_BASE; + int irqindex = wmt_gpios[offset].irqnum; + + _set_gpio_irqenable(irqindex, 0); +} +EXPORT_SYMBOL(wmt_gpio_mask_irq); + +void wmt_gpio_unmask_irq(unsigned int gpio) +{ + int offset = gpio - CHIP_GPIO_BASE; + int irqindex = wmt_gpios[offset].irqnum; + + _set_gpio_irqenable(irqindex, 1); +} +EXPORT_SYMBOL(wmt_gpio_unmask_irq); + +int wmt_gpio_set_irq_type(unsigned int gpio, u32 type) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + int offset = gpio - CHIP_GPIO_BASE; + int irqindex = wmt_gpios[offset].irqnum; + int edge; + u32 reg, val; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + edge = GPIO_INT_RISE_EDGE; + break; + case IRQ_TYPE_EDGE_FALLING: + edge = GPIO_INT_FALL_EDGE; + break; + case IRQ_TYPE_EDGE_BOTH: + edge = GPIO_INT_BOTH_EDGE; + break; + case IRQ_TYPE_LEVEL_LOW: + edge = GPIO_INT_LOW_LEV; + break; + case IRQ_TYPE_LEVEL_HIGH: + edge = GPIO_INT_HIGH_LEV; + break; + default: + return -EINVAL; + } + + reg = port->base + INTMASK_REGS + irqindex; + val = __raw_readb(reg) & 0xf8; + __raw_writeb(val | edge, reg); + + _clear_gpio_irqstatus(irqindex); + + return 0; +} +EXPORT_SYMBOL(wmt_gpio_set_irq_type); + +#ifdef WMT_GPIO_IRQ +static void gpio_ack_irq(struct irq_data *d) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + int irqindex = d->irq - port->gpio_irq_no_base; + + _clear_gpio_irqstatus(irqindex); +} + +static void gpio_mask_irq(struct irq_data *d) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + int irqindex = d->irq - port->gpio_irq_no_base; + + _set_gpio_irqenable(irqindex, 0); +} + +static void gpio_unmask_irq(struct irq_data *d) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + int irqindex = d->irq - port->gpio_irq_no_base; + + _set_gpio_irqenable(irqindex, 1); +} + +static int gpio_set_irq_type(struct irq_data *d, u32 type) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + int edge, irqindex; + u32 reg, val; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + edge = GPIO_INT_RISE_EDGE; + break; + case IRQ_TYPE_EDGE_FALLING: + edge = GPIO_INT_FALL_EDGE; + break; + case IRQ_TYPE_EDGE_BOTH: + edge = GPIO_INT_BOTH_EDGE; + break; + case IRQ_TYPE_LEVEL_LOW: + edge = GPIO_INT_LOW_LEV; + break; + case IRQ_TYPE_LEVEL_HIGH: + edge = GPIO_INT_HIGH_LEV; + break; + default: + return -EINVAL; + } + + irqindex = d->irq - port->gpio_irq_no_base; + reg = port->base + INTMASK_REGS + irqindex; + val = __raw_readb(reg) & 0xf8; + __raw_writeb(val | edge, reg); + + _clear_gpio_irqstatus(irqindex); + + return 0; +} + +/* WMT has one interrupt *for all* gpio ports */ +static void wmt_gpio_irq_handler(u32 irq, struct irq_desc *desc) +{ + int i; + u32 irq_msk, irq_stat; + struct wmt_gpio_port *port = &wmt_gpio_port; + + /* walk through all interrupt status registers */ + for (i = 0; i < 4; i++) { + irq_msk = __raw_readl(port->base + INTMASK_REGS + i*4); + if (!irq_msk) + continue; + + irq_stat = __raw_readl(port->base + INTSTAT_REGS + i*4) & irq_msk; + while (irq_stat != 0) { + int irqoffset = fls(irq_stat) - 1; + + generic_handle_irq(port->gpio_irq_no_base + i*32 + irqoffset); + + irq_stat &= ~(1 << irqoffset); + } + } +} + +static struct irq_chip gpio_irq_chip = { + .name = "GPIO", + .irq_ack = gpio_ack_irq, + .irq_mask = gpio_mask_irq, + .irq_unmask = gpio_unmask_irq, + .irq_set_type = gpio_set_irq_type, +}; +#endif + +static int wmt_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct wmt_gpio_port *port = to_wmt(chip); + uint32_t base = port->base; + uint8_t regoff = wmt_gpios[offset].regoff; + uint8_t shift = wmt_gpios[offset].shift; + uint8_t val; + + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + + val = readb(base + ENABLE_REGS + regoff); + val |= (1 << shift); + writeb(val, base + ENABLE_REGS + regoff); + + spin_unlock_irqrestore(&port->lock, flags); + return 0; +} + +static void wmt_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct wmt_gpio_port *port = to_wmt(chip); +// uint32_t base = port->base; +// uint8_t regoff = wmt_gpios[offset].regoff; +// uint8_t shift = wmt_gpios[offset].shift; +// uint8_t val; + + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + +// val = readb(base + ENABLE_REGS + regoff); +// val &= ~(1 << shift); +// writeb(val, base + ENABLE_REGS + regoff); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, int dir) +{ + struct wmt_gpio_port *port = to_wmt(chip); + uint32_t base = port->base; + uint8_t regoff = wmt_gpios[offset].regoff; + uint8_t shift = wmt_gpios[offset].shift; + uint8_t val; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + + val = readb(base + DIRECTION_REGS + regoff); + if (dir) + val |= (1 << shift); + else + val &= ~(1 << shift); + writeb(val, base + DIRECTION_REGS + regoff); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int wmt_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct wmt_gpio_port *port = to_wmt(chip); + uint32_t base = port->base; + uint8_t regoff = wmt_gpios[offset].regoff; + uint8_t shift = wmt_gpios[offset].shift; + uint8_t val; + int dir; + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + + val = readb(base + DIRECTION_REGS + regoff); + dir = (val >> shift) & 1; + + if (dir) + return (readb(base + OUTVALUE_REGS + regoff) >> shift) & 1; + else + return (readb(base + INVALUE_REGS + regoff) >> shift) & 1; +} + +static void wmt_gpio_set_value(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wmt_gpio_port *port = to_wmt(chip); + uint32_t base = port->base; + uint8_t regoff = wmt_gpios[offset].regoff; + uint8_t shift = wmt_gpios[offset].shift; + uint8_t val; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + + val = readb(base + OUTVALUE_REGS + regoff); + + if (value) + val |= (1 << shift); + else + val &= ~(1 << shift); + + writeb(val, base + OUTVALUE_REGS + regoff); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int wmt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + _set_gpio_direction(chip, offset, 0); + return 0; +} + +static int wmt_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + wmt_gpio_set_value(chip, offset, value); + _set_gpio_direction(chip, offset, 1); + return 0; +} + +static void _set_gpio_pullenable(unsigned offset, int enable) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + uint32_t base = port->base; + uint8_t regoff = wmt_gpios[offset].regoff; + uint8_t shift = wmt_gpios[offset].shift; + uint8_t val; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + + val = readb(base + PULLENABLE_REGS + regoff); + if (enable) + val |= (1 << shift); + else + val &= ~(1 << shift); + writeb(val, base + PULLENABLE_REGS + regoff); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void _set_gpio_pullup(unsigned offset, int up) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + uint32_t base = port->base; + uint8_t regoff = wmt_gpios[offset].regoff; + uint8_t shift = wmt_gpios[offset].shift; + uint8_t val; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + + val = readb(base + PULLCONTROL_REGS + regoff); + if (up) + val |= (1 << shift); + else + val &= ~(1 << shift); + writeb(val, base + PULLCONTROL_REGS + regoff); + + spin_unlock_irqrestore(&port->lock, flags); +} + +int wmt_gpio_setpull(unsigned int gpio, enum wmt_gpio_pulltype pull) +{ + int offset = gpio - CHIP_GPIO_BASE; + + switch (pull) { + case WMT_GPIO_PULL_NONE: + _set_gpio_pullenable(offset, 0); + break; + case WMT_GPIO_PULL_UP: + _set_gpio_pullenable(offset, 1); + _set_gpio_pullup(offset, 1); + break; + case WMT_GPIO_PULL_DOWN: + _set_gpio_pullenable(offset, 1); + _set_gpio_pullup(offset, 0); + break; + default: + return -EINVAL; + } + return 0; +} + +static int _get_gpio_pull(unsigned offset) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + uint32_t base = port->base; + uint8_t regoff = wmt_gpios[offset].regoff; + uint8_t shift = wmt_gpios[offset].shift; + uint8_t val; + int dir; + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + + val = readb(base + DIRECTION_REGS + regoff); + dir = (val >> shift) & 1; + + if (dir) + return (readb(base + OUTVALUE_REGS + regoff) >> shift) & 1; + else + return (readb(base + INVALUE_REGS + regoff) >> shift) & 1; +} + + +int wmt_gpio_getpull(unsigned int gpio) +{ + int offset = gpio - CHIP_GPIO_BASE; + + return _get_gpio_pull(offset); +} + +EXPORT_SYMBOL(wmt_gpio_setpull); +EXPORT_SYMBOL(wmt_gpio_getpull); + +#ifdef WMT_GPIO_IRQ +static int wmt_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct wmt_gpio_port *port = &wmt_gpio_port; + + WARN_ON(offset >= ARRAY_SIZE(wmt_gpios)); + + if (wmt_gpios[offset].irqnum < 0) + return -EINVAL; + + return port->gpio_irq_no_base + wmt_gpios[offset].irqnum; +} +#endif + +static struct wmt_gpio_port wmt_gpio_port = { + .base = GPIO_BASE_ADDR, +#ifdef WMT_GPIO_IRQ + .gpio_irq_no_base = WMT_GPIO_IRQ_START, + .gpio_irq_nr = WMT_GPIO_IRQS, +#endif + .chip = { + .label = "wmt-gpio", + .direction_input = wmt_gpio_direction_input, + .direction_output = wmt_gpio_direction_output, + .request = wmt_gpio_request, + .free = wmt_gpio_free, + .get = wmt_gpio_get_value, + .set = wmt_gpio_set_value, +#ifdef WMT_GPIO_IRQ + .to_irq = wmt_gpio_to_irq, +#endif + .can_sleep = 0, + .base = CHIP_GPIO_BASE, + .ngpio = ARRAY_SIZE(wmt_gpios), + }, +}; + +void __init wmt_gpio_init(void) +{ +#ifdef WMT_GPIO_IRQ + int irq; +#endif + + spin_lock_init(&wmt_gpio_port.lock); + BUG_ON(gpiochip_add(&wmt_gpio_port.chip) < 0); + + /* XXX conflict with touchscreen irq */ +#ifdef WMT_GPIO_IRQ + for (irq = wmt_gpio_port.gpio_irq_no_base; + irq < wmt_gpio_port.gpio_irq_no_base + wmt_gpio_port.gpio_irq_nr; + irq++) { + irq_set_chip_and_handler(irq, &gpio_irq_chip, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + } + + irq_set_chained_handler(IRQ_GPIO, wmt_gpio_irq_handler); +#endif +} + +const char *wmt_gpio_name(int gpio) +{ + if (gpio >= ARRAY_SIZE(wmt_gpios)) { + pr_err("wmt: can not find gpio-%d\n", gpio); + return NULL; + } + + return wmt_gpios[gpio].label; +} + +#ifdef CONFIG_DEBUG_FS + +static int wmt_gpio_show(struct seq_file *s, void *unused) +{ + unsigned i; + + seq_printf(s, "GPIONUM , REGOFF , SHIFT , MACRO NAME\n"); + seq_printf(s, "---------+--------+-------+-----------\n"); + + for (i = 0; i < wmt_gpio_port.chip.ngpio; i++) { + seq_printf(s, "gpio-%-3d , 0x%02x , %d , %s", + i, wmt_gpios[i].regoff, wmt_gpios[i].shift, wmt_gpios[i].label); + seq_printf(s, "\n"); + } + return 0; +} + +static int wmt_gpio_open(struct inode *inode, struct file *file) +{ + return single_open(file, wmt_gpio_show, NULL); +} + +static const struct file_operations wmt_gpio_operations = { + .open = wmt_gpio_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init wmt_gpio_debugfs_init(void) +{ + /* /sys/kernel/debug/wmt-gpio */ + (void) debugfs_create_file("wmt-gpio", S_IFREG | S_IRUGO, + NULL, NULL, &wmt_gpio_operations); + return 0; +} +subsys_initcall(wmt_gpio_debugfs_init); + +#endif /* DEBUG_FS */ + |