diff options
Diffstat (limited to 'drivers/pcmcia/soc_common.c')
-rw-r--r-- | drivers/pcmcia/soc_common.c | 193 |
1 files changed, 127 insertions, 66 deletions
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index a0a9c2aa8d7..e0433f57196 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -32,6 +32,7 @@ #include <linux/cpufreq.h> +#include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -49,6 +50,8 @@ #include "soc_common.h" +static irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev); + #ifdef CONFIG_PCMCIA_DEBUG static int pc_debug; @@ -104,6 +107,93 @@ void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, } EXPORT_SYMBOL(soc_common_pcmcia_get_timing); +static void __soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt, + unsigned int nr) +{ + unsigned int i; + + for (i = 0; i < nr; i++) { + if (skt->stat[i].irq) + free_irq(skt->stat[i].irq, skt); + if (gpio_is_valid(skt->stat[i].gpio)) + gpio_free(skt->stat[i].gpio); + } + + if (skt->ops->hw_shutdown) + skt->ops->hw_shutdown(skt); +} + +static void soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + __soc_pcmcia_hw_shutdown(skt, ARRAY_SIZE(skt->stat)); +} + +static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret = 0, i; + + if (skt->ops->hw_init) { + ret = skt->ops->hw_init(skt); + if (ret) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(skt->stat); i++) { + if (gpio_is_valid(skt->stat[i].gpio)) { + int irq; + + ret = gpio_request_one(skt->stat[i].gpio, GPIOF_IN, + skt->stat[i].name); + if (ret) { + __soc_pcmcia_hw_shutdown(skt, i); + return ret; + } + + irq = gpio_to_irq(skt->stat[i].gpio); + + if (i == SOC_STAT_RDY) + skt->socket.pci_irq = irq; + else + skt->stat[i].irq = irq; + } + + if (skt->stat[i].irq) { + ret = request_irq(skt->stat[i].irq, + soc_common_pcmcia_interrupt, + IRQF_TRIGGER_NONE, + skt->stat[i].name, skt); + if (ret) { + if (gpio_is_valid(skt->stat[i].gpio)) + gpio_free(skt->stat[i].gpio); + __soc_pcmcia_hw_shutdown(skt, i); + return ret; + } + } + } + + return ret; +} + +static void soc_pcmcia_hw_enable(struct soc_pcmcia_socket *skt) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(skt->stat); i++) + if (skt->stat[i].irq) { + irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_RISING); + irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_BOTH); + } +} + +static void soc_pcmcia_hw_disable(struct soc_pcmcia_socket *skt) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(skt->stat); i++) + if (skt->stat[i].irq) + irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_NONE); +} + static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) { struct pcmcia_state state; @@ -111,6 +201,22 @@ static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) memset(&state, 0, sizeof(struct pcmcia_state)); + /* Make battery voltage state report 'good' */ + state.bvd1 = 1; + state.bvd2 = 1; + + /* CD is active low by default */ + if (gpio_is_valid(skt->stat[SOC_STAT_CD].gpio)) + state.detect = !gpio_get_value(skt->stat[SOC_STAT_CD].gpio); + + /* RDY and BVD are active high by default */ + if (gpio_is_valid(skt->stat[SOC_STAT_RDY].gpio)) + state.ready = !!gpio_get_value(skt->stat[SOC_STAT_RDY].gpio); + if (gpio_is_valid(skt->stat[SOC_STAT_BVD1].gpio)) + state.bvd1 = !!gpio_get_value(skt->stat[SOC_STAT_BVD1].gpio); + if (gpio_is_valid(skt->stat[SOC_STAT_BVD2].gpio)) + state.bvd2 = !!gpio_get_value(skt->stat[SOC_STAT_BVD2].gpio); + skt->ops->socket_state(skt, &state); stat = state.detect ? SS_DETECT : 0; @@ -188,6 +294,7 @@ static int soc_common_pcmcia_sock_init(struct pcmcia_socket *sock) debug(skt, 2, "initializing socket\n"); if (skt->ops->socket_init) skt->ops->socket_init(skt); + soc_pcmcia_hw_enable(skt); return 0; } @@ -207,6 +314,7 @@ static int soc_common_pcmcia_suspend(struct pcmcia_socket *sock) debug(skt, 2, "suspending socket\n"); + soc_pcmcia_hw_disable(skt); if (skt->ops->socket_suspend) skt->ops->socket_suspend(skt); @@ -526,69 +634,6 @@ static struct pccard_operations soc_common_pcmcia_operations = { }; -int soc_pcmcia_request_irqs(struct soc_pcmcia_socket *skt, - struct pcmcia_irqs *irqs, int nr) -{ - int i, res = 0; - - for (i = 0; i < nr; i++) { - if (irqs[i].sock != skt->nr) - continue; - res = request_irq(irqs[i].irq, soc_common_pcmcia_interrupt, - IRQF_DISABLED, irqs[i].str, skt); - if (res) - break; - irq_set_irq_type(irqs[i].irq, IRQ_TYPE_NONE); - } - - if (res) { - printk(KERN_ERR "PCMCIA: request for IRQ%d failed (%d)\n", - irqs[i].irq, res); - - while (i--) - if (irqs[i].sock == skt->nr) - free_irq(irqs[i].irq, skt); - } - return res; -} -EXPORT_SYMBOL(soc_pcmcia_request_irqs); - -void soc_pcmcia_free_irqs(struct soc_pcmcia_socket *skt, - struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) - free_irq(irqs[i].irq, skt); -} -EXPORT_SYMBOL(soc_pcmcia_free_irqs); - -void soc_pcmcia_disable_irqs(struct soc_pcmcia_socket *skt, - struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) - irq_set_irq_type(irqs[i].irq, IRQ_TYPE_NONE); -} -EXPORT_SYMBOL(soc_pcmcia_disable_irqs); - -void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, - struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) { - irq_set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_RISING); - irq_set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_BOTH); - } -} -EXPORT_SYMBOL(soc_pcmcia_enable_irqs); - - static LIST_HEAD(soc_pcmcia_sockets); static DEFINE_MUTEX(soc_pcmcia_sockets_lock); @@ -635,6 +680,21 @@ module_exit(soc_pcmcia_cpufreq_unregister); #endif +void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt, + struct pcmcia_low_level *ops, struct device *dev) +{ + int i; + + skt->ops = ops; + skt->socket.owner = ops->owner; + skt->socket.dev.parent = dev; + skt->socket.pci_irq = NO_IRQ; + + for (i = 0; i < ARRAY_SIZE(skt->stat); i++) + skt->stat[i].gpio = -EINVAL; +} +EXPORT_SYMBOL(soc_pcmcia_init_one); + void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt) { mutex_lock(&soc_pcmcia_sockets_lock); @@ -642,8 +702,9 @@ void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt) pcmcia_unregister_socket(&skt->socket); - skt->ops->hw_shutdown(skt); + soc_pcmcia_hw_shutdown(skt); + /* should not be required; violates some lowlevel drivers */ soc_common_pcmcia_config_skt(skt, &dead_socket); list_del(&skt->node); @@ -700,7 +761,7 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) */ skt->ops->set_timing(skt); - ret = skt->ops->hw_init(skt); + ret = soc_pcmcia_hw_init(skt); if (ret) goto out_err_6; @@ -733,7 +794,7 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) pcmcia_unregister_socket(&skt->socket); out_err_7: - skt->ops->hw_shutdown(skt); + soc_pcmcia_hw_shutdown(skt); out_err_6: list_del(&skt->node); mutex_unlock(&soc_pcmcia_sockets_lock); |