Commit 2e056215 authored by David Brownell's avatar David Brownell Committed by Tony Lindgren

twl4030 uses gpiolib

Make the twl4030 core create a platform device to which its
GPIO code will bind, with platform_data used to configure
board-specific behaviors and configuration.

Update the twl4030 GPIO code:

  - Morph its gpio function code into a platform driver.

  - Move away from IRQ (and GPIO) numbers hard-wired in headers.

  - Hook up the twl4030 GPIO code to gpiolib.

  - Start phasing out the older TWL-specific calls ... currently
    those are used only by arch/arm/mach-omap2/hsmmc.c setup code.

  - Use a mutex for locking, not a binary semaphore.

NOTE:  more patches pending:  (a) this doesn't use pdata->pullups
to initialize (currently hsmmc code always sets GPIO-0 pullup even
if the board has an external pullup);  (b) there's a new gpio
request/free hook forthcoming in 2.6.28, which this should use;
(c) likewise there's a new gpio_to_irq() hook; (d) the irq_chip
set_type() mechanism needs to be supported; (e) needs to move over
to drivers/gpio; (f) upcoming threaded IRQ infrastructure should
be used, when that merges.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent fca78930
...@@ -159,7 +159,7 @@ config TWL4030_CORE ...@@ -159,7 +159,7 @@ config TWL4030_CORE
config TWL4030_GPIO config TWL4030_GPIO
bool "TWL4030 GPIO Driver" bool "TWL4030 GPIO Driver"
depends on TWL4030_CORE depends on TWL4030_CORE && GPIOLIB
config TWL4030_MADC config TWL4030_MADC
tristate "TWL4030 MADC Driver" tristate "TWL4030 MADC Driver"
......
...@@ -63,6 +63,12 @@ ...@@ -63,6 +63,12 @@
#define twl_has_usb() false #define twl_has_usb() false
#endif #endif
#ifdef CONFIG_TWL4030_GPIO
#define twl_has_gpio() true
#else
#define twl_has_gpio() false
#endif
/* Primary Interrupt Handler on TWL4030 Registers */ /* Primary Interrupt Handler on TWL4030 Registers */
/* Register Definitions */ /* Register Definitions */
...@@ -656,6 +662,44 @@ static int add_children(struct twl4030_platform_data *pdata) ...@@ -656,6 +662,44 @@ static int add_children(struct twl4030_platform_data *pdata)
struct twl4030_client *twl = NULL; struct twl4030_client *twl = NULL;
int status = 0; int status = 0;
if (twl_has_gpio() && pdata->gpio) {
twl = &twl4030_modules[TWL4030_SLAVENUM_NUM1];
pdev = platform_device_alloc("twl4030_gpio", -1);
if (!pdev)
status = -ENOMEM;
/* more driver model init */
if (status == 0) {
pdev->dev.parent = &twl->client->dev;
/* device_init_wakeup(&pdev->dev, 1); */
status = platform_device_add_data(pdev, pdata->gpio,
sizeof(*pdata->gpio));
}
/* GPIO module IRQ */
if (status == 0) {
struct resource r = {
.start = pdata->irq_base + 0,
.flags = IORESOURCE_IRQ,
};
status = platform_device_add_resources(pdev, &r, 1);
}
if (status == 0)
status = platform_device_add(pdev);
if (status < 0) {
platform_device_put(pdev);
dev_dbg(&twl->client->dev,
"can't create gpio dev, %d\n",
status);
goto err;
}
}
if (twl_has_rtc()) { if (twl_has_rtc()) {
pdev = platform_device_alloc("twl4030_rtc", -1); pdev = platform_device_alloc("twl4030_rtc", -1);
if (pdev) { if (pdev) {
......
...@@ -31,22 +31,31 @@ ...@@ -31,22 +31,31 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/random.h> #include <linux/device.h>
#include <linux/syscalls.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c/twl4030.h> #include <linux/i2c/twl4030.h>
#include <linux/i2c/twl4030-gpio.h> #include <linux/i2c/twl4030-gpio.h>
#include <linux/slab.h>
#include <mach/irqs.h> #include <mach/irqs.h>
#include <asm/mach/irq.h> #include <asm/mach/irq.h>
#include <mach/gpio.h> #include <mach/gpio.h>
#include <mach/mux.h> #include <mach/mux.h>
#include <linux/device.h>
/* REVISIT when these symbols vanish elsewhere, remove them here too */
#undef TWL4030_GPIO_IRQ_BASE
#undef TWL4030_GPIO_IRQ_END
#undef TWL4030_MODIRQ_GPIO
static struct gpio_chip twl_gpiochip;
static int twl4030_gpio_irq_base;
static int twl4030_gpio_irq_end;
/* BitField Definitions */ /* BitField Definitions */
...@@ -130,7 +139,7 @@ ...@@ -130,7 +139,7 @@
#define GPIO_32_MASK 0x0003ffff #define GPIO_32_MASK 0x0003ffff
/* Data structures */ /* Data structures */
static struct semaphore gpio_sem; static DEFINE_MUTEX(gpio_lock);
/* store usage of each GPIO. - each bit represents one GPIO */ /* store usage of each GPIO. - each bit represents one GPIO */
static unsigned int gpio_usage_count; static unsigned int gpio_usage_count;
...@@ -147,7 +156,7 @@ static struct task_struct *gpio_unmask_thread; ...@@ -147,7 +156,7 @@ static struct task_struct *gpio_unmask_thread;
/* /*
* Helper functions to read and write the GPIO ISR and IMR registers as * Helper functions to read and write the GPIO ISR and IMR registers as
* 32-bit integers. Functions return 0 on success, non-zero otherwise. * 32-bit integers. Functions return 0 on success, non-zero otherwise.
* The caller must hold a lock on gpio_sem. * The caller must hold gpio_lock.
*/ */
static int gpio_read_isr(unsigned int *isr) static int gpio_read_isr(unsigned int *isr)
...@@ -197,25 +206,25 @@ static int gpio_write_imr(unsigned int imr) ...@@ -197,25 +206,25 @@ static int gpio_write_imr(unsigned int imr)
*/ */
static void twl4030_gpio_mask_and_ack(unsigned int irq) static void twl4030_gpio_mask_and_ack(unsigned int irq)
{ {
int gpio = irq - TWL4030_GPIO_IRQ_BASE; int gpio = irq - twl4030_gpio_irq_base;
down(&gpio_sem); mutex_lock(&gpio_lock);
/* mask */ /* mask */
gpio_imr_shadow |= (1 << gpio); gpio_imr_shadow |= (1 << gpio);
gpio_write_imr(gpio_imr_shadow); gpio_write_imr(gpio_imr_shadow);
/* ack */ /* ack */
gpio_write_isr(1 << gpio); gpio_write_isr(1 << gpio);
up(&gpio_sem); mutex_unlock(&gpio_lock);
} }
static void twl4030_gpio_unmask(unsigned int irq) static void twl4030_gpio_unmask(unsigned int irq)
{ {
int gpio = irq - TWL4030_GPIO_IRQ_BASE; int gpio = irq - twl4030_gpio_irq_base;
down(&gpio_sem); mutex_lock(&gpio_lock);
gpio_imr_shadow &= ~(1 << gpio); gpio_imr_shadow &= ~(1 << gpio);
gpio_write_imr(gpio_imr_shadow); gpio_write_imr(gpio_imr_shadow);
up(&gpio_sem); mutex_unlock(&gpio_lock);
} }
/* /*
...@@ -234,7 +243,7 @@ static void twl4030_gpio_mask_irqchip(unsigned int irq) {} ...@@ -234,7 +243,7 @@ static void twl4030_gpio_mask_irqchip(unsigned int irq) {}
static void twl4030_gpio_unmask_irqchip(unsigned int irq) static void twl4030_gpio_unmask_irqchip(unsigned int irq)
{ {
int gpio = irq - TWL4030_GPIO_IRQ_BASE; int gpio = irq - twl4030_gpio_irq_base;
gpio_pending_unmask |= (1 << gpio); gpio_pending_unmask |= (1 << gpio);
if (gpio_unmask_thread && gpio_unmask_thread->state != TASK_RUNNING) if (gpio_unmask_thread && gpio_unmask_thread->state != TASK_RUNNING)
...@@ -242,7 +251,7 @@ static void twl4030_gpio_unmask_irqchip(unsigned int irq) ...@@ -242,7 +251,7 @@ static void twl4030_gpio_unmask_irqchip(unsigned int irq)
} }
static struct irq_chip twl4030_gpio_irq_chip = { static struct irq_chip twl4030_gpio_irq_chip = {
.name = "twl4030-gpio", .name = "twl4030",
.ack = twl4030_gpio_mask_and_ack_irqchip, .ack = twl4030_gpio_mask_and_ack_irqchip,
.mask = twl4030_gpio_mask_irqchip, .mask = twl4030_gpio_mask_irqchip,
.unmask = twl4030_gpio_unmask_irqchip, .unmask = twl4030_gpio_unmask_irqchip,
...@@ -297,21 +306,26 @@ int twl4030_request_gpio(int gpio) ...@@ -297,21 +306,26 @@ int twl4030_request_gpio(int gpio)
if (unlikely(gpio >= TWL4030_GPIO_MAX)) if (unlikely(gpio >= TWL4030_GPIO_MAX))
return -EPERM; return -EPERM;
down(&gpio_sem); ret = gpio_request(twl_gpiochip.base + gpio, NULL);
if (gpio_usage_count & (0x1 << gpio)) if (ret < 0)
return ret;
mutex_lock(&gpio_lock);
if (gpio_usage_count & (0x1 << gpio)) {
ret = -EBUSY; ret = -EBUSY;
else { } else {
/* First time usage? - switch on GPIO module */ /* First time usage? - switch on GPIO module */
if (!gpio_usage_count) { if (!gpio_usage_count) {
ret = ret = gpio_twl4030_write(REG_GPIO_CTRL,
gpio_twl4030_write(REG_GPIO_CTRL,
MASK_GPIO_CTRL_GPIO_ON); MASK_GPIO_CTRL_GPIO_ON);
ret = gpio_twl4030_write(REG_GPIO_SIH_CTRL, 0x00); ret = gpio_twl4030_write(REG_GPIO_SIH_CTRL, 0x00);
} }
if (!ret) if (!ret)
gpio_usage_count |= (0x1 << gpio); gpio_usage_count |= (0x1 << gpio);
else
gpio_free(twl_gpiochip.base + gpio);
} }
up(&gpio_sem); mutex_unlock(&gpio_lock);
return ret; return ret;
} }
EXPORT_SYMBOL(twl4030_request_gpio); EXPORT_SYMBOL(twl4030_request_gpio);
...@@ -326,18 +340,20 @@ int twl4030_free_gpio(int gpio) ...@@ -326,18 +340,20 @@ int twl4030_free_gpio(int gpio)
if (unlikely(gpio >= TWL4030_GPIO_MAX)) if (unlikely(gpio >= TWL4030_GPIO_MAX))
return -EPERM; return -EPERM;
down(&gpio_sem); mutex_lock(&gpio_lock);
if ((gpio_usage_count & (0x1 << gpio)) == 0) if ((gpio_usage_count & (0x1 << gpio)) == 0) {
ret = -EPERM; ret = -EPERM;
else } else {
gpio_usage_count &= ~(0x1 << gpio); gpio_usage_count &= ~(0x1 << gpio);
gpio_free(twl_gpiochip.base + gpio);
}
/* Last time usage? - switch off GPIO module */ /* Last time usage? - switch off GPIO module */
if (!gpio_usage_count) if (ret == 0 && !gpio_usage_count)
ret = gpio_twl4030_write(REG_GPIO_CTRL, 0x0); ret = gpio_twl4030_write(REG_GPIO_CTRL, 0x0);
up(&gpio_sem); mutex_unlock(&gpio_lock);
return ret; return ret;
} }
EXPORT_SYMBOL(twl4030_free_gpio); EXPORT_SYMBOL(twl4030_free_gpio);
...@@ -345,21 +361,18 @@ EXPORT_SYMBOL(twl4030_free_gpio); ...@@ -345,21 +361,18 @@ EXPORT_SYMBOL(twl4030_free_gpio);
/* /*
* Set direction for TWL4030 GPIO * Set direction for TWL4030 GPIO
*/ */
int twl4030_set_gpio_direction(int gpio, int is_input) static int twl4030_set_gpio_direction(int gpio, int is_input)
{ {
u8 d_bnk = GET_GPIO_DATA_BANK(gpio); u8 d_bnk = GET_GPIO_DATA_BANK(gpio);
u8 d_msk = MASK_GPIODATADIR_GPIOxDIR(GET_GPIO_DATA_OFF(gpio)); u8 d_msk = MASK_GPIODATADIR_GPIOxDIR(GET_GPIO_DATA_OFF(gpio));
u8 reg = 0; u8 reg = 0;
u8 base = 0; u8 base = REG_GPIODATADIR1 + d_bnk;
int ret = 0; int ret = 0;
if (unlikely((gpio >= TWL4030_GPIO_MAX) if (unlikely(!(gpio_usage_count & (0x1 << gpio))))
|| !(gpio_usage_count & (0x1 << gpio))))
return -EPERM; return -EPERM;
base = REG_GPIODATADIR1 + d_bnk; mutex_lock(&gpio_lock);
down(&gpio_sem);
ret = gpio_twl4030_read(base); ret = gpio_twl4030_read(base);
if (ret >= 0) { if (ret >= 0) {
if (is_input) if (is_input)
...@@ -369,23 +382,21 @@ int twl4030_set_gpio_direction(int gpio, int is_input) ...@@ -369,23 +382,21 @@ int twl4030_set_gpio_direction(int gpio, int is_input)
ret = gpio_twl4030_write(base, reg); ret = gpio_twl4030_write(base, reg);
} }
up(&gpio_sem); mutex_unlock(&gpio_lock);
return ret; return ret;
} }
EXPORT_SYMBOL(twl4030_set_gpio_direction);
/* /*
* To enable/disable GPIO pin on TWL4030 * To enable/disable GPIO pin on TWL4030
*/ */
int twl4030_set_gpio_dataout(int gpio, int enable) static int twl4030_set_gpio_dataout(int gpio, int enable)
{ {
u8 d_bnk = GET_GPIO_DATA_BANK(gpio); u8 d_bnk = GET_GPIO_DATA_BANK(gpio);
u8 d_msk = MASK_GPIODATAOUT_GPIOxOUT(GET_GPIO_DATA_OFF(gpio)); u8 d_msk = MASK_GPIODATAOUT_GPIOxOUT(GET_GPIO_DATA_OFF(gpio));
u8 base = 0; u8 base = 0;
int ret = 0; int ret = 0;
if (unlikely((gpio >= TWL4030_GPIO_MAX) if (unlikely(!(gpio_usage_count & (0x1 << gpio))))
|| !(gpio_usage_count & (0x1 << gpio))))
return -EPERM; return -EPERM;
if (enable) if (enable)
...@@ -393,12 +404,11 @@ int twl4030_set_gpio_dataout(int gpio, int enable) ...@@ -393,12 +404,11 @@ int twl4030_set_gpio_dataout(int gpio, int enable)
else else
base = REG_CLEARGPIODATAOUT1 + d_bnk; base = REG_CLEARGPIODATAOUT1 + d_bnk;
down(&gpio_sem); mutex_lock(&gpio_lock);
ret = gpio_twl4030_write(base, d_msk); ret = gpio_twl4030_write(base, d_msk);
up(&gpio_sem); mutex_unlock(&gpio_lock);
return ret; return ret;
} }
EXPORT_SYMBOL(twl4030_set_gpio_dataout);
/* /*
* To get the status of a GPIO pin on TWL4030 * To get the status of a GPIO pin on TWL4030
...@@ -415,9 +425,9 @@ int twl4030_get_gpio_datain(int gpio) ...@@ -415,9 +425,9 @@ int twl4030_get_gpio_datain(int gpio)
return -EPERM; return -EPERM;
base = REG_GPIODATAIN1 + d_bnk; base = REG_GPIODATAIN1 + d_bnk;
down(&gpio_sem); mutex_lock(&gpio_lock);
ret = gpio_twl4030_read(base); ret = gpio_twl4030_read(base);
up(&gpio_sem); mutex_unlock(&gpio_lock);
if (ret > 0) if (ret > 0)
ret = (ret >> d_off) & 0x1; ret = (ret >> d_off) & 0x1;
...@@ -425,6 +435,7 @@ int twl4030_get_gpio_datain(int gpio) ...@@ -425,6 +435,7 @@ int twl4030_get_gpio_datain(int gpio)
} }
EXPORT_SYMBOL(twl4030_get_gpio_datain); EXPORT_SYMBOL(twl4030_get_gpio_datain);
#if 0
/* /*
* Configure PULL type for a GPIO pin on TWL4030 * Configure PULL type for a GPIO pin on TWL4030
*/ */
...@@ -447,7 +458,7 @@ int twl4030_set_gpio_pull(int gpio, int pull_dircn) ...@@ -447,7 +458,7 @@ int twl4030_set_gpio_pull(int gpio, int pull_dircn)
else if (pull_dircn == TWL4030_GPIO_PULL_UP) else if (pull_dircn == TWL4030_GPIO_PULL_UP)
c_msk = MASK_GPIOPUPDCTR1_GPIOxPU(c_off); c_msk = MASK_GPIOPUPDCTR1_GPIOxPU(c_off);
down(&gpio_sem); mutex_lock(&gpio_lock);
ret = gpio_twl4030_read(base); ret = gpio_twl4030_read(base);
if (ret >= 0) { if (ret >= 0) {
/* clear the previous up/down values */ /* clear the previous up/down values */
...@@ -457,13 +468,15 @@ int twl4030_set_gpio_pull(int gpio, int pull_dircn) ...@@ -457,13 +468,15 @@ int twl4030_set_gpio_pull(int gpio, int pull_dircn)
reg |= c_msk; reg |= c_msk;
ret = gpio_twl4030_write(base, reg); ret = gpio_twl4030_write(base, reg);
} }
up(&gpio_sem); mutex_unlock(&gpio_lock);
return ret; return ret;
} }
EXPORT_SYMBOL(twl4030_set_gpio_pull); #endif
/* /*
* Configure Edge control for a GPIO pin on TWL4030 * Configure Edge control for a GPIO pin on TWL4030
*
* FIXME this should just be the irq_chip.set_type() method
*/ */
int twl4030_set_gpio_edge_ctrl(int gpio, int edge) int twl4030_set_gpio_edge_ctrl(int gpio, int edge)
{ {
...@@ -486,7 +499,7 @@ int twl4030_set_gpio_edge_ctrl(int gpio, int edge) ...@@ -486,7 +499,7 @@ int twl4030_set_gpio_edge_ctrl(int gpio, int edge)
if (edge & TWL4030_GPIO_EDGE_FALLING) if (edge & TWL4030_GPIO_EDGE_FALLING)
c_msk |= MASK_GPIO_EDR1_GPIOxFALLING(c_off); c_msk |= MASK_GPIO_EDR1_GPIOxFALLING(c_off);
down(&gpio_sem); mutex_lock(&gpio_lock);
ret = gpio_twl4030_read(base); ret = gpio_twl4030_read(base);
if (ret >= 0) { if (ret >= 0) {
/* clear the previous rising/falling values */ /* clear the previous rising/falling values */
...@@ -497,7 +510,7 @@ int twl4030_set_gpio_edge_ctrl(int gpio, int edge) ...@@ -497,7 +510,7 @@ int twl4030_set_gpio_edge_ctrl(int gpio, int edge)
reg |= c_msk; reg |= c_msk;
ret = gpio_twl4030_write(base, reg); ret = gpio_twl4030_write(base, reg);
} }
up(&gpio_sem); mutex_unlock(&gpio_lock);
return ret; return ret;
} }
EXPORT_SYMBOL(twl4030_set_gpio_edge_ctrl); EXPORT_SYMBOL(twl4030_set_gpio_edge_ctrl);
...@@ -518,7 +531,7 @@ int twl4030_set_gpio_debounce(int gpio, int enable) ...@@ -518,7 +531,7 @@ int twl4030_set_gpio_debounce(int gpio, int enable)
return -EPERM; return -EPERM;
base = REG_GPIO_DEBEN1 + d_bnk; base = REG_GPIO_DEBEN1 + d_bnk;
down(&gpio_sem); mutex_lock(&gpio_lock);
ret = gpio_twl4030_read(base); ret = gpio_twl4030_read(base);
if (ret >= 0) { if (ret >= 0) {
if (enable) if (enable)
...@@ -528,11 +541,12 @@ int twl4030_set_gpio_debounce(int gpio, int enable) ...@@ -528,11 +541,12 @@ int twl4030_set_gpio_debounce(int gpio, int enable)
ret = gpio_twl4030_write(base, reg); ret = gpio_twl4030_write(base, reg);
} }
up(&gpio_sem); mutex_unlock(&gpio_lock);
return ret; return ret;
} }
EXPORT_SYMBOL(twl4030_set_gpio_debounce); EXPORT_SYMBOL(twl4030_set_gpio_debounce);
#if 0
/* /*
* Configure Card detect for GPIO pin on TWL4030 * Configure Card detect for GPIO pin on TWL4030
*/ */
...@@ -549,7 +563,7 @@ int twl4030_set_gpio_card_detect(int gpio, int enable) ...@@ -549,7 +563,7 @@ int twl4030_set_gpio_card_detect(int gpio, int enable)
return -EPERM; return -EPERM;
} }
down(&gpio_sem); mutex_lock(&gpio_lock);
ret = gpio_twl4030_read(REG_GPIO_CTRL); ret = gpio_twl4030_read(REG_GPIO_CTRL);
if (ret >= 0) { if (ret >= 0) {
if (enable) if (enable)
...@@ -559,10 +573,10 @@ int twl4030_set_gpio_card_detect(int gpio, int enable) ...@@ -559,10 +573,10 @@ int twl4030_set_gpio_card_detect(int gpio, int enable)
ret = gpio_twl4030_write(REG_GPIO_CTRL, reg); ret = gpio_twl4030_write(REG_GPIO_CTRL, reg);
} }
up(&gpio_sem); mutex_unlock(&gpio_lock);
return (ret); return (ret);
} }
EXPORT_SYMBOL(twl4030_set_gpio_card_detect); #endif
/* MODULE FUNCTIONS */ /* MODULE FUNCTIONS */
...@@ -590,7 +604,7 @@ static int twl4030_gpio_unmask_thread(void *data) ...@@ -590,7 +604,7 @@ static int twl4030_gpio_unmask_thread(void *data)
gpio_pending_unmask = 0; gpio_pending_unmask = 0;
local_irq_enable(); local_irq_enable();
for (irq = TWL4030_GPIO_IRQ_BASE; 0 != gpio_unmask; for (irq = twl4030_gpio_irq_base; 0 != gpio_unmask;
gpio_unmask >>= 1, irq++) { gpio_unmask >>= 1, irq++) {
if (gpio_unmask & 0x1) if (gpio_unmask & 0x1)
twl4030_gpio_unmask(irq); twl4030_gpio_unmask(irq);
...@@ -677,12 +691,12 @@ static void do_twl4030_gpio_module_irq(unsigned int irq, irq_desc_t *desc) ...@@ -677,12 +691,12 @@ static void do_twl4030_gpio_module_irq(unsigned int irq, irq_desc_t *desc)
kstat_cpu(cpu).irqs[irq]++; kstat_cpu(cpu).irqs[irq]++;
local_irq_enable(); local_irq_enable();
down(&gpio_sem); mutex_lock(&gpio_lock);
if (gpio_read_isr(&gpio_isr)) if (gpio_read_isr(&gpio_isr))
gpio_isr = 0; gpio_isr = 0;
up(&gpio_sem); mutex_unlock(&gpio_lock);
for (gpio_irq = TWL4030_GPIO_IRQ_BASE; 0 != gpio_isr; for (gpio_irq = twl4030_gpio_irq_base; 0 != gpio_isr;
gpio_isr >>= 1, gpio_irq++) { gpio_isr >>= 1, gpio_irq++) {
if (gpio_isr & 0x1) { if (gpio_isr & 0x1) {
irq_desc_t *d = irq_desc + gpio_irq; irq_desc_t *d = irq_desc + gpio_irq;
...@@ -698,19 +712,60 @@ static void do_twl4030_gpio_module_irq(unsigned int irq, irq_desc_t *desc) ...@@ -698,19 +712,60 @@ static void do_twl4030_gpio_module_irq(unsigned int irq, irq_desc_t *desc)
} }
} }
/* TWL4030 Initialization module */ /*----------------------------------------------------------------------*/
static int __init gpio_twl4030_init(void)
static int twl_direction_in(struct gpio_chip *chip, unsigned offset)
{
return twl4030_set_gpio_direction(offset, 1);
}
static int twl_get(struct gpio_chip *chip, unsigned offset)
{
int status = twl4030_get_gpio_datain(offset);
return (status < 0) ? 0 : status;
}
static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
{
twl4030_set_gpio_dataout(offset, value);
return twl4030_set_gpio_direction(offset, 0);
}
static void twl_set(struct gpio_chip *chip, unsigned offset, int value)
{ {
twl4030_set_gpio_dataout(offset, value);
}
static struct gpio_chip twl_gpiochip = {
.label = "twl4030",
.owner = THIS_MODULE,
.direction_input = twl_direction_in,
.get = twl_get,
.direction_output = twl_direction_out,
.set = twl_set,
.can_sleep = 1,
};
/*----------------------------------------------------------------------*/
static int gpio_twl4030_remove(struct platform_device *pdev);
static int __devinit gpio_twl4030_probe(struct platform_device *pdev)
{
struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;
int ret; int ret;
int irq = 0; int irq = 0;
/* init the global locking sem */
sema_init(&gpio_sem, 1);
/* All GPIO interrupts are initially masked */ /* All GPIO interrupts are initially masked */
gpio_pending_unmask = 0; gpio_pending_unmask = 0;
gpio_imr_shadow = GPIO_32_MASK; gpio_imr_shadow = GPIO_32_MASK;
ret = gpio_write_imr(gpio_imr_shadow); ret = gpio_write_imr(gpio_imr_shadow);
twl4030_gpio_irq_base = pdata->irq_base;
twl4030_gpio_irq_end = pdata->irq_end;
/* REVISIT skip most of this if the irq range is empty... */
if (!ret) { if (!ret) {
/* /*
* Create a kernel thread to handle deferred unmasking of gpio * Create a kernel thread to handle deferred unmasking of gpio
...@@ -719,50 +774,88 @@ static int __init gpio_twl4030_init(void) ...@@ -719,50 +774,88 @@ static int __init gpio_twl4030_init(void)
gpio_unmask_thread = kthread_create(twl4030_gpio_unmask_thread, gpio_unmask_thread = kthread_create(twl4030_gpio_unmask_thread,
NULL, "twl4030 gpio"); NULL, "twl4030 gpio");
if (!gpio_unmask_thread) { if (!gpio_unmask_thread) {
printk(KERN_ERR dev_err(&pdev->dev,
"%s: could not create twl4030 gpio unmask" "could not create twl4030 gpio unmask"
" thread!\n", __func__); " thread!\n");
ret = -ENOMEM; ret = -ENOMEM;
} }
} }
if (!ret) { if (!ret) {
/* install an irq handler for each of the gpio interrupts */ /* install an irq handler for each of the gpio interrupts */
for (irq = TWL4030_GPIO_IRQ_BASE; irq < TWL4030_GPIO_IRQ_END; for (irq = twl4030_gpio_irq_base; irq < twl4030_gpio_irq_end;
irq++) { irq++) {
set_irq_chip(irq, &twl4030_gpio_irq_chip); set_irq_chip(irq, &twl4030_gpio_irq_chip);
set_irq_handler(irq, do_twl4030_gpio_irq); set_irq_handler(irq, do_twl4030_gpio_irq);
set_irq_flags(irq, IRQF_VALID); set_irq_flags(irq, IRQF_VALID);
} }
/* gpio module IRQ */
irq = platform_get_irq(pdev, 0);
/* /*
* Install an irq handler to demultiplex the gpio module * Install an irq handler to demultiplex the gpio module
* interrupt. * interrupt.
*/ */
set_irq_chip(TWL4030_MODIRQ_GPIO, set_irq_chip(irq, &twl4030_gpio_module_irq_chip);
&twl4030_gpio_module_irq_chip); set_irq_chained_handler(irq, do_twl4030_gpio_module_irq);
set_irq_chained_handler(TWL4030_MODIRQ_GPIO,
do_twl4030_gpio_module_irq);
wake_up_process(gpio_unmask_thread); wake_up_process(gpio_unmask_thread);
dev_info(&pdev->dev, "IRQ %d chains IRQs %d..%d\n", irq,
twl4030_gpio_irq_base, twl4030_gpio_irq_end - 1);
}
if (!ret) {
twl_gpiochip.base = pdata->gpio_base;
twl_gpiochip.ngpio = TWL4030_GPIO_MAX;
twl_gpiochip.dev = &pdev->dev;
ret = gpiochip_add(&twl_gpiochip);
if (ret < 0) {
dev_err(&pdev->dev,
"could not register gpiochip, %d\n",
ret);
twl_gpiochip.ngpio = 0;
gpio_twl4030_remove(pdev);
} else if (pdata->setup) {
int status;
status = pdata->setup(&pdev->dev,
pdata->gpio_base, TWL4030_GPIO_MAX);
if (status)
dev_dbg(&pdev->dev, "setup --> %d\n", status);
}
} }
printk(KERN_INFO "TWL4030 GPIO Demux: IRQ Range %d to %d,"
" Initialization %s\n", TWL4030_GPIO_IRQ_BASE,
TWL4030_GPIO_IRQ_END, (ret) ? "Failed" : "Success");
return ret; return ret;
} }
/* TWL GPIO exit module */ static int __devexit gpio_twl4030_remove(struct platform_device *pdev)
static void __exit gpio_twl4030_exit(void)
{ {
struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;
int status;
int irq; int irq;
if (pdata->teardown) {
status = pdata->teardown(&pdev->dev,
pdata->gpio_base, TWL4030_GPIO_MAX);
if (status) {
dev_dbg(&pdev->dev, "teardown --> %d\n", status);
return status;
}
}
status = gpiochip_remove(&twl_gpiochip);
if (status < 0)
return status;
/* uninstall the gpio demultiplexing interrupt handler */ /* uninstall the gpio demultiplexing interrupt handler */
set_irq_handler(TWL4030_MODIRQ_GPIO, NULL); irq = platform_get_irq(pdev, 0);
set_irq_flags(TWL4030_MODIRQ_GPIO, 0); set_irq_handler(irq, NULL);
set_irq_flags(irq, 0);
/* uninstall the irq handler for each of the gpio interrupts */ /* uninstall the irq handler for each of the gpio interrupts */
for (irq = TWL4030_GPIO_IRQ_BASE; irq < TWL4030_GPIO_IRQ_END; irq++) { for (irq = twl4030_gpio_irq_base; irq < twl4030_gpio_irq_end; irq++) {
set_irq_handler(irq, NULL); set_irq_handler(irq, NULL);
set_irq_flags(irq, 0); set_irq_flags(irq, 0);
} }
...@@ -772,12 +865,32 @@ static void __exit gpio_twl4030_exit(void) ...@@ -772,12 +865,32 @@ static void __exit gpio_twl4030_exit(void)
kthread_stop(gpio_unmask_thread); kthread_stop(gpio_unmask_thread);
gpio_unmask_thread = NULL; gpio_unmask_thread = NULL;
} }
return 0;
} }
/* Note: this hardware lives inside an I2C-based multi-function device. */
MODULE_ALIAS("platform:twl4030_gpio");
static struct platform_driver gpio_twl4030_driver = {
.driver.name = "twl4030_gpio",
.driver.owner = THIS_MODULE,
.probe = gpio_twl4030_probe,
.remove = __devexit_p(gpio_twl4030_remove),
};
static int __init gpio_twl4030_init(void)
{
return platform_driver_register(&gpio_twl4030_driver);
}
module_init(gpio_twl4030_init); module_init(gpio_twl4030_init);
static void __exit gpio_twl4030_exit(void)
{
platform_driver_unregister(&gpio_twl4030_driver);
}
module_exit(gpio_twl4030_exit); module_exit(gpio_twl4030_exit);
MODULE_ALIAS("i2c:twl4030-gpio");
MODULE_AUTHOR("Texas Instruments, Inc."); MODULE_AUTHOR("Texas Instruments, Inc.");
MODULE_DESCRIPTION("GPIO interface for TWL4030"); MODULE_DESCRIPTION("GPIO interface for TWL4030");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment