diff options
Diffstat (limited to 'drivers/pcmcia/soc_common.c')
| -rw-r--r-- | drivers/pcmcia/soc_common.c | 341 |
1 files changed, 208 insertions, 133 deletions
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index 689e3c02edb..a2bc6ee1702 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -31,24 +31,26 @@ ======================================================================*/ -#include <linux/module.h> -#include <linux/moduleparam.h> +#include <linux/cpufreq.h> +#include <linux/gpio.h> #include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> #include <linux/kernel.h> -#include <linux/timer.h> #include <linux/mm.h> +#include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/mutex.h> -#include <linux/interrupt.h> -#include <linux/irq.h> #include <linux/spinlock.h> -#include <linux/cpufreq.h> +#include <linux/timer.h> #include <mach/hardware.h> -#include <asm/io.h> -#include <asm/system.h> #include "soc_common.h" +static irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev); + #ifdef CONFIG_PCMCIA_DEBUG static int pc_debug; @@ -57,18 +59,25 @@ module_param(pc_debug, int, 0644); void soc_pcmcia_debug(struct soc_pcmcia_socket *skt, const char *func, int lvl, const char *fmt, ...) { + struct va_format vaf; va_list args; if (pc_debug > lvl) { - printk(KERN_DEBUG "skt%u: %s: ", skt->nr, func); va_start(args, fmt); - vprintk(fmt, args); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_DEBUG "skt%u: %s: %pV", skt->nr, func, &vaf); + va_end(args); } } +EXPORT_SYMBOL(soc_pcmcia_debug); #endif -#define to_soc_pcmcia_socket(x) container_of(x, struct soc_pcmcia_socket, socket) +#define to_soc_pcmcia_socket(x) \ + container_of(x, struct soc_pcmcia_socket, socket) static unsigned short calc_speed(unsigned short *spds, int num, unsigned short dflt) @@ -85,14 +94,105 @@ calc_speed(unsigned short *spds, int num, unsigned short dflt) return speed; } -void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, struct soc_pcmcia_timing *timing) +void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, + struct soc_pcmcia_timing *timing) { - timing->io = calc_speed(skt->spd_io, MAX_IO_WIN, SOC_PCMCIA_IO_ACCESS); - timing->mem = calc_speed(skt->spd_mem, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); - timing->attr = calc_speed(skt->spd_attr, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); + timing->io = + calc_speed(skt->spd_io, MAX_IO_WIN, SOC_PCMCIA_IO_ACCESS); + timing->mem = + calc_speed(skt->spd_mem, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); + timing->attr = + calc_speed(skt->spd_attr, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); } 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; @@ -100,6 +200,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; @@ -131,8 +247,8 @@ static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) * * Convert PCMCIA socket state to our socket configure structure. */ -static int -soc_common_pcmcia_config_skt(struct soc_pcmcia_socket *skt, socket_state_t *state) +static int soc_common_pcmcia_config_skt( + struct soc_pcmcia_socket *skt, socket_state_t *state) { int ret; @@ -144,10 +260,11 @@ soc_common_pcmcia_config_skt(struct soc_pcmcia_socket *skt, socket_state_t *stat */ if (skt->irq_state != 1 && state->io_irq) { skt->irq_state = 1; - set_irq_type(skt->socket.pci_irq, IRQ_TYPE_EDGE_FALLING); + irq_set_irq_type(skt->socket.pci_irq, + IRQ_TYPE_EDGE_FALLING); } else if (skt->irq_state == 1 && state->io_irq == 0) { skt->irq_state = 0; - set_irq_type(skt->socket.pci_irq, IRQ_TYPE_NONE); + irq_set_irq_type(skt->socket.pci_irq, IRQ_TYPE_NONE); } skt->cs_state = *state; @@ -174,8 +291,9 @@ static int soc_common_pcmcia_sock_init(struct pcmcia_socket *sock) struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); debug(skt, 2, "initializing socket\n"); - - skt->ops->socket_init(skt); + if (skt->ops->socket_init) + skt->ops->socket_init(skt); + soc_pcmcia_hw_enable(skt); return 0; } @@ -195,7 +313,9 @@ static int soc_common_pcmcia_suspend(struct pcmcia_socket *sock) debug(skt, 2, "suspending socket\n"); - skt->ops->socket_suspend(skt); + soc_pcmcia_hw_disable(skt); + if (skt->ops->socket_suspend) + skt->ops->socket_suspend(skt); return 0; } @@ -298,24 +418,24 @@ soc_common_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status) * of power configuration, reset, &c. We also record the value of * `state' in order to regurgitate it to the PCMCIA core later. */ -static int -soc_common_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state) +static int soc_common_pcmcia_set_socket( + struct pcmcia_socket *sock, socket_state_t *state) { struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); - debug(skt, 2, "mask: %s%s%s%s%s%sflags: %s%s%s%s%s%sVcc %d Vpp %d irq %d\n", - (state->csc_mask==0)?"<NONE> ":"", - (state->csc_mask&SS_DETECT)?"DETECT ":"", - (state->csc_mask&SS_READY)?"READY ":"", - (state->csc_mask&SS_BATDEAD)?"BATDEAD ":"", - (state->csc_mask&SS_BATWARN)?"BATWARN ":"", - (state->csc_mask&SS_STSCHG)?"STSCHG ":"", - (state->flags==0)?"<NONE> ":"", - (state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"", - (state->flags&SS_IOCARD)?"IOCARD ":"", - (state->flags&SS_RESET)?"RESET ":"", - (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", - (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"", + debug(skt, 2, "mask: %s%s%s%s%s%s flags: %s%s%s%s%s%s Vcc %d Vpp %d irq %d\n", + (state->csc_mask == 0) ? "<NONE> " : "", + (state->csc_mask & SS_DETECT) ? "DETECT " : "", + (state->csc_mask & SS_READY) ? "READY " : "", + (state->csc_mask & SS_BATDEAD) ? "BATDEAD " : "", + (state->csc_mask & SS_BATWARN) ? "BATWARN " : "", + (state->csc_mask & SS_STSCHG) ? "STSCHG " : "", + (state->flags == 0) ? "<NONE> " : "", + (state->flags & SS_PWR_AUTO) ? "PWR_AUTO " : "", + (state->flags & SS_IOCARD) ? "IOCARD " : "", + (state->flags & SS_RESET) ? "RESET " : "", + (state->flags & SS_SPKR_ENA) ? "SPKR_ENA " : "", + (state->flags & SS_OUTPUT_ENA) ? "OUTPUT_ENA " : "", state->Vcc, state->Vpp, state->io_irq); return soc_common_pcmcia_config_skt(skt, state); @@ -330,8 +450,8 @@ soc_common_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state) * * Returns: 0 on success, -1 on error */ -static int -soc_common_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *map) +static int soc_common_pcmcia_set_io_map( + struct pcmcia_socket *sock, struct pccard_io_map *map) { struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); unsigned short speed = map->speed; @@ -340,14 +460,14 @@ soc_common_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *m map->map, map->speed, (unsigned long long)map->start, (unsigned long long)map->stop); debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", - (map->flags==0)?"<NONE>":"", - (map->flags&MAP_ACTIVE)?"ACTIVE ":"", - (map->flags&MAP_16BIT)?"16BIT ":"", - (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", - (map->flags&MAP_0WS)?"0WS ":"", - (map->flags&MAP_WRPROT)?"WRPROT ":"", - (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"", - (map->flags&MAP_PREFETCH)?"PREFETCH ":""); + (map->flags == 0) ? "<NONE>" : "", + (map->flags & MAP_ACTIVE) ? "ACTIVE " : "", + (map->flags & MAP_16BIT) ? "16BIT " : "", + (map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "", + (map->flags & MAP_0WS) ? "0WS " : "", + (map->flags & MAP_WRPROT) ? "WRPROT " : "", + (map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "", + (map->flags & MAP_PREFETCH) ? "PREFETCH " : ""); if (map->map >= MAX_IO_WIN) { printk(KERN_ERR "%s(): map (%d) out of range\n", __func__, @@ -384,8 +504,8 @@ soc_common_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *m * * Returns: 0 on success, -ERRNO on error */ -static int -soc_common_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *map) +static int soc_common_pcmcia_set_mem_map( + struct pcmcia_socket *sock, struct pccard_mem_map *map) { struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); struct resource *res; @@ -394,14 +514,14 @@ soc_common_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map debug(skt, 2, "map %u speed %u card_start %08x\n", map->map, map->speed, map->card_start); debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", - (map->flags==0)?"<NONE>":"", - (map->flags&MAP_ACTIVE)?"ACTIVE ":"", - (map->flags&MAP_16BIT)?"16BIT ":"", - (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", - (map->flags&MAP_0WS)?"0WS ":"", - (map->flags&MAP_WRPROT)?"WRPROT ":"", - (map->flags&MAP_ATTRIB)?"ATTRIB ":"", - (map->flags&MAP_USE_WAIT)?"USE_WAIT ":""); + (map->flags == 0) ? "<NONE>" : "", + (map->flags & MAP_ACTIVE) ? "ACTIVE " : "", + (map->flags & MAP_16BIT) ? "16BIT " : "", + (map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "", + (map->flags & MAP_0WS) ? "0WS " : "", + (map->flags & MAP_WRPROT) ? "WRPROT " : "", + (map->flags & MAP_ATTRIB) ? "ATTRIB " : "", + (map->flags & MAP_USE_WAIT) ? "USE_WAIT " : ""); if (map->map >= MAX_WIN) return -EINVAL; @@ -456,8 +576,8 @@ static struct bittbl conf_bits[] = { { SS_OUTPUT_ENA, "SS_OUTPUT_ENA" }, }; -static void -dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, int sz) +static void dump_bits(char **p, const char *prefix, + unsigned int val, struct bittbl *bits, int sz) { char *b = *p; int i; @@ -475,13 +595,14 @@ dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, i * * Returns: the number of characters added to the buffer */ -static ssize_t show_status(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_status( + struct device *dev, struct device_attribute *attr, char *buf) { struct soc_pcmcia_socket *skt = container_of(dev, struct soc_pcmcia_socket, socket.dev); char *p = buf; - p+=sprintf(p, "slot : %d\n", skt->nr); + p += sprintf(p, "slot : %d\n", skt->nr); dump_bits(&p, "status", skt->status, status_bits, ARRAY_SIZE(status_bits)); @@ -490,12 +611,12 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr, ch dump_bits(&p, "cs_flags", skt->cs_state.flags, conf_bits, ARRAY_SIZE(conf_bits)); - p+=sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc); - p+=sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp); - p+=sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, + p += sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc); + p += sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp); + p += sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, skt->socket.pci_irq); if (skt->ops->show_timing) - p+=skt->ops->show_timing(skt, p); + p += skt->ops->show_timing(skt, p); return p-buf; } @@ -512,69 +633,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; - 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) - 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) { - set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_RISING); - 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); @@ -588,7 +646,7 @@ soc_pcmcia_notifier(struct notifier_block *nb, unsigned long val, void *data) mutex_lock(&soc_pcmcia_sockets_lock); list_for_each_entry(skt, &soc_pcmcia_sockets, node) - if ( skt->ops->frequency_change ) + if (skt->ops->frequency_change) ret += skt->ops->frequency_change(skt, val, freqs); mutex_unlock(&soc_pcmcia_sockets_lock); @@ -614,12 +672,28 @@ fs_initcall(soc_pcmcia_cpufreq_register); static void soc_pcmcia_cpufreq_unregister(void) { - cpufreq_unregister_notifier(&soc_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + cpufreq_unregister_notifier(&soc_pcmcia_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); } 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); @@ -627,8 +701,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); @@ -685,7 +760,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; @@ -718,7 +793,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); |
