📄 twl4030_gpio.c
字号:
/* * linux/drivers/i2c/chips/twl4030_gpio.c * * Copyright (C) 2006-2007 Texas Instruments, Inc. * Copyright (C) 2006 MontaVista Software, Inc. * * Code re-arranged and cleaned up by: * Syed Mohammed Khasim <x0khasim@ti.com> * * Initial Code: * Andy Lowe / Nishanth Menon * * 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. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <linux/module.h>#include <linux/kernel_stat.h>#include <linux/init.h>#include <linux/time.h>#include <linux/interrupt.h>#include <linux/random.h>#include <linux/syscalls.h>#include <linux/kthread.h>#include <linux/i2c.h>#include <linux/slab.h>#include <asm/arch/twl4030.h>#include <asm/arch/irqs.h>#include <asm/irq.h>#include <asm/mach/irq.h>#include <asm/arch/gpio.h>#include <asm/arch/mux.h>#include <linux/device.h>/***************************************** GPIO Block Register definitions****************************************/#define REG_GPIODATAIN1 (0x0)#define REG_GPIODATAIN2 (0x1)#define REG_GPIODATAIN3 (0x2)#define REG_GPIODATADIR1 (0x3)#define REG_GPIODATADIR2 (0x4)#define REG_GPIODATADIR3 (0x5)#define REG_GPIODATAOUT1 (0x6)#define REG_GPIODATAOUT2 (0x7)#define REG_GPIODATAOUT3 (0x8)#define REG_CLEARGPIODATAOUT1 (0x9)#define REG_CLEARGPIODATAOUT2 (0xA)#define REG_CLEARGPIODATAOUT3 (0xB)#define REG_SETGPIODATAOUT1 (0xC)#define REG_SETGPIODATAOUT2 (0xD)#define REG_SETGPIODATAOUT3 (0xE)#define REG_GPIO_DEBEN1 (0xF)#define REG_GPIO_DEBEN2 (0x10)#define REG_GPIO_DEBEN3 (0x11)#define REG_GPIO_CTRL (0x12)#define REG_GPIOPUPDCTR1 (0x13)#define REG_GPIOPUPDCTR2 (0x14)#define REG_GPIOPUPDCTR3 (0x15)#define REG_GPIOPUPDCTR4 (0x16)#define REG_GPIOPUPDCTR5 (0x17)#define REG_GPIO_ISR1A (0x19)#define REG_GPIO_ISR2A (0x1A)#define REG_GPIO_ISR3A (0x1B)#define REG_GPIO_IMR1A (0x1C)#define REG_GPIO_IMR2A (0x1D)#define REG_GPIO_IMR3A (0x1E)#define REG_GPIO_ISR1B (0x1F)#define REG_GPIO_ISR2B (0x20)#define REG_GPIO_ISR3B (0x21)#define REG_GPIO_IMR1B (0x22)#define REG_GPIO_IMR2B (0x23)#define REG_GPIO_IMR3B (0x24)#define REG_GPIO_EDR1 (0x28)#define REG_GPIO_EDR2 (0x29)#define REG_GPIO_EDR3 (0x2A)#define REG_GPIO_EDR4 (0x2B)#define REG_GPIO_EDR5 (0x2C)#define REG_GPIO_SIH_CTRL (0x2D)/**** BitField Definitions *//***** Data banks : 3 banks for 8 gpios each */#define DATA_BANK_MAX 8#define GET_GPIO_DATA_BANK(x) ((x)/DATA_BANK_MAX)#define GET_GPIO_DATA_OFF(x) ((x)%DATA_BANK_MAX)/* GPIODATADIR Fields each block 0-7 */#define BIT_GPIODATADIR_GPIOxDIR(x) (x)#define MASK_GPIODATADIR_GPIOxDIR(x) (0x01<<(x))/* GPIODATAIN Fields each block 0-7 */#define BIT_GPIODATAIN_GPIOxIN(x) (x)#define MASK_GPIODATAIN_GPIOxIN(x) (0x01<<(x))/* GPIODATAOUT Fields each block 0-7 */#define BIT_GPIODATAOUT_GPIOxOUT(x) (x)#define MASK_GPIODATAOUT_GPIOxOUT(x) (0x01<<(x))/* CLEARGPIODATAOUT Fields */#define BIT_CLEARGPIODATAOUT_GPIOxOUT(x) (x)#define MASK_CLEARGPIODATAOUT_GPIOxOUT(x) (0x01<<(x))/* SETGPIODATAOUT Fields */#define BIT_SETGPIODATAOUT_GPIOxOUT(x) (x)#define MASK_SETGPIODATAOUT_GPIOxOUT(x) (0x01<<(x))/* GPIO_DEBEN Fields */#define BIT_GPIO_DEBEN_GPIOxDEB(x) (x)#define MASK_GPIO_DEBEN_GPIOxDEB(x) (0x01<<(x))/* GPIO_ISR1A Fields */#define BIT_GPIO_ISR_GPIOxISR(x) (x)#define MASK_GPIO_ISR_GPIOxISR(x) (0x01<<(x))/* GPIO_IMR1A Fields */#define BIT_GPIO_IMR1A_GPIOxIMR(x) (x)#define MASK_GPIO_IMR1A_GPIOxIMR(x) (0x01<<(x))/* GPIO_SIR1 Fields */#define BIT_GPIO_SIR1_GPIOxSIR(x) (x)#define MASK_GPIO_SIR1_GPIO0SIR (0x01<<(x))/**** Control banks : 5 banks for 4 gpios each */#define DATA_CTL_MAX 4#define GET_GPIO_CTL_BANK(x) ((x)/DATA_CTL_MAX)#define GET_GPIO_CTL_OFF(x) ((x)%DATA_CTL_MAX)#define GPIO_BANK_MAX GET_GPIO_CTL_BANK(TWL4030_GPIO_MAX)/* GPIOPUPDCTRx Fields 5 banks of 4 gpios each */#define BIT_GPIOPUPDCTR1_GPIOxPD(x) (2 *(x))#define MASK_GPIOPUPDCTR1_GPIOxPD(x) (0x01<<(2*(x)))#define BIT_GPIOPUPDCTR1_GPIOxPU(x) ((x) + 1)#define MASK_GPIOPUPDCTR1_GPIOxPU(x) (0x01<<(((2*(x)) + 1)))/* GPIO_EDR1 Fields */#define BIT_GPIO_EDR1_GPIOxFALLING(x) (2 *(x))#define MASK_GPIO_EDR1_GPIOxFALLING(x) (0x01<<(2*(x)))#define BIT_GPIO_EDR1_GPIOxRISING(x) ((x) + 1)#define MASK_GPIO_EDR1_GPIOxRISING(x) (0x01<<(((2*(x)) + 1)))/* GPIO_SIH_CTRL Fields */#define BIT_GPIO_SIH_CTRL_EXCLEN (0x000)#define MASK_GPIO_SIH_CTRL_EXCLEN (0x00000001)#define BIT_GPIO_SIH_CTRL_PENDDIS (0x001)#define MASK_GPIO_SIH_CTRL_PENDDIS (0x00000002)#define BIT_GPIO_SIH_CTRL_COR (0x002)#define MASK_GPIO_SIH_CTRL_COR (0x00000004)/* GPIO_CTRL Fields */#define BIT_GPIO_CTRL_GPIO0CD1 (0x000)#define MASK_GPIO_CTRL_GPIO0CD1 (0x00000001)#define BIT_GPIO_CTRL_GPIO1CD2 (0x001)#define MASK_GPIO_CTRL_GPIO1CD2 (0x00000002)#define BIT_GPIO_CTRL_GPIO_ON (0x002)#define MASK_GPIO_CTRL_GPIO_ON (0x00000004)/* Mask for GPIO registers when aggregated into a 32-bit integer */#define GPIO_32_MASK 0x0003ffff/* Data structures */static struct semaphore gpio_sem;/* store usage of each GPIO. - each bit represents one GPIO */static unsigned int gpio_usage_count;/* shadow the imr register */static unsigned int gpio_imr_shadow;/* bitmask of pending requests to unmask gpio interrupts */static unsigned int gpio_pending_unmask;/* pointer to gpio unmask thread struct */static struct task_struct *gpio_unmask_thread;static inline int gpio_twl4030_read(u8 address);static inline int gpio_twl4030_write(u8 address, u8 data);/* * Helper functions to read and write the GPIO ISR and IMR registers as * 32-bit integers. Functions return 0 on success, non-zero otherwise. * The caller must hold a lock on gpio_sem. */static int gpio_read_isr(unsigned int *isr){ int ret; *isr = 0; ret = twl4030_i2c_read(TWL4030_MODULE_GPIO, (u8 *) isr, REG_GPIO_ISR1A, 3); le32_to_cpup(isr); *isr &= GPIO_32_MASK; return ret;}static int gpio_write_isr(unsigned int isr){ int ret; isr &= GPIO_32_MASK; /* * The buffer passed to the twl4030_i2c_write() routine must have an * extra byte at the beginning reserved for its internal use. */ isr <<= 8; isr = cpu_to_le32(isr); ret = twl4030_i2c_write(TWL4030_MODULE_GPIO, (u8 *) &isr, REG_GPIO_ISR1A, 3); return ret;}static int gpio_write_imr(unsigned int imr){ int ret; imr &= GPIO_32_MASK; /* * The buffer passed to the twl4030_i2c_write() routine must have an * extra byte at the beginning reserved for its internal use. */ imr <<= 8; imr = cpu_to_le32(imr); ret = twl4030_i2c_write(TWL4030_MODULE_GPIO, (u8 *) &imr, REG_GPIO_IMR1A, 3); return ret;}/* * These routines are analagous to the irqchip methods, but they are designed * to be called from thread context with cpu interrupts enabled and with no * locked spinlocks. We call these routines from our custom IRQ handler * instead of the usual irqchip methods. */static void twl4030_gpio_mask_and_ack(unsigned int irq){ int gpio = irq - IH_TWL4030_GPIO_BASE; down(&gpio_sem); /* mask */ gpio_imr_shadow |= (1 << gpio); gpio_write_imr(gpio_imr_shadow); /* ack */ gpio_write_isr(1 << gpio); up(&gpio_sem);}static void twl4030_gpio_unmask(unsigned int irq){ int gpio = irq - IH_TWL4030_GPIO_BASE; down(&gpio_sem); gpio_imr_shadow &= ~(1 << gpio); gpio_write_imr(gpio_imr_shadow); up(&gpio_sem);}/* * These are the irqchip methods for the TWL4030 GPIO interrupts. * Our IRQ handle method doesn't call these, but they will be called by * other routines such as setup_irq() and enable_irq(). They are called * with cpu interrupts disabled and with a lock on the irq_controller_lock * spinlock. This complicates matters, because accessing the TWL4030 GPIO * interrupt controller requires I2C bus transactions that can't be initiated * in this context. Our solution is to defer accessing the interrupt * controller to a kernel thread. We only need to support the unmask method. */static void twl4030_gpio_mask_and_ack_irqchip(unsigned int irq) {}static void twl4030_gpio_mask_irqchip(unsigned int irq) {}static void twl4030_gpio_unmask_irqchip(unsigned int irq){ int gpio = irq - IH_TWL4030_GPIO_BASE; gpio_pending_unmask |= (1 << gpio); if (gpio_unmask_thread && gpio_unmask_thread->state != TASK_RUNNING) wake_up_process(gpio_unmask_thread);}static struct irq_chip twl4030_gpio_irq_chip = { .name = "twl4030-gpio", .ack = twl4030_gpio_mask_and_ack_irqchip, .mask = twl4030_gpio_mask_irqchip, .unmask = twl4030_gpio_unmask_irqchip,};/* * These are the irqchip methods for the TWL4030 PIH GPIO module interrupt. * The PIH module doesn't have interrupt masking capability, so these * methods are NULL. */static void twl4030_gpio_module_ack(unsigned int irq) {}static void twl4030_gpio_module_mask(unsigned int irq) {}static void twl4030_gpio_module_unmask(unsigned int irq) {}static struct irq_chip twl4030_gpio_module_irq_chip = { .ack = twl4030_gpio_module_ack, .mask = twl4030_gpio_module_mask, .unmask = twl4030_gpio_module_unmask,};/* * twl4030 GPIO request function */int twl4030_request_gpio(int gpio){ int ret = 0; if (unlikely(gpio >= TWL4030_GPIO_MAX)) return -EPERM; down(&gpio_sem); if (gpio_usage_count & (0x1 << gpio)) ret = -EBUSY; else { u8 clear_pull[6] = { 0, 0, 0, 0, 0, 0 }; /* First time usage? - switch on GPIO module */ if (!gpio_usage_count) { ret = gpio_twl4030_write(REG_GPIO_CTRL, MASK_GPIO_CTRL_GPIO_ON); ret = gpio_twl4030_write(REG_GPIO_SIH_CTRL, 0x00); } if (!ret) gpio_usage_count |= (0x1 << gpio); ret = twl4030_i2c_write(TWL4030_MODULE_GPIO, clear_pull, REG_GPIOPUPDCTR1, 5); } up(&gpio_sem); return ret;}/* * TWL4030 GPIO free module */int twl4030_free_gpio(int gpio){ int ret = 0; if (unlikely(gpio >= TWL4030_GPIO_MAX)) return -EPERM; down(&gpio_sem); if ((gpio_usage_count & (0x1 << gpio)) == 0) ret = -EPERM; else gpio_usage_count &= ~(0x1 << gpio); /* Last time usage? - switch off GPIO module */ if (!gpio_usage_count) ret = gpio_twl4030_write(REG_GPIO_CTRL, 0x0); up(&gpio_sem); return ret;}/* * Set direction for TWL4030 GPIO */int twl4030_set_gpio_direction(int gpio, int is_input){ u8 d_bnk = GET_GPIO_DATA_BANK(gpio); u8 d_msk = MASK_GPIODATADIR_GPIOxDIR(GET_GPIO_DATA_OFF(gpio)); u8 reg = 0; u8 base = 0; int ret = 0; if (unlikely((gpio >= TWL4030_GPIO_MAX) || !(gpio_usage_count & (0x1 << gpio)))) return -EPERM; base = REG_GPIODATADIR1 + d_bnk; down(&gpio_sem); ret = gpio_twl4030_read(base); if (ret >= 0) { if (is_input) reg = (u8) ((ret) & ~(d_msk)); else reg = (u8) ((ret) | (d_msk)); ret = gpio_twl4030_write(base, reg); } up(&gpio_sem); return ret;}/* * To enable/disable GPIO pin on TWL4030 */int twl4030_set_gpio_dataout(int gpio, int enable){ u8 d_bnk = GET_GPIO_DATA_BANK(gpio); u8 d_msk = MASK_GPIODATAOUT_GPIOxOUT(GET_GPIO_DATA_OFF(gpio)); u8 base = 0; int ret = 0; if (unlikely((gpio >= TWL4030_GPIO_MAX) || !(gpio_usage_count & (0x1 << gpio)))) return -EPERM; if (enable) base = REG_SETGPIODATAOUT1 + d_bnk; else base = REG_CLEARGPIODATAOUT1 + d_bnk;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -