diff options
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r-- | drivers/pcmcia/Kconfig | 25 | ||||
-rw-r--r-- | drivers/pcmcia/Makefile | 10 | ||||
-rw-r--r-- | drivers/pcmcia/bfin_cf_pcmcia.c | 2 | ||||
-rw-r--r-- | drivers/pcmcia/cardbus.c | 1 | ||||
-rw-r--r-- | drivers/pcmcia/cistpl.c | 125 | ||||
-rw-r--r-- | drivers/pcmcia/cs.c | 1 | ||||
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 22 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 34 | ||||
-rw-r--r-- | drivers/pcmcia/electra_cf.c | 9 | ||||
-rw-r--r-- | drivers/pcmcia/m8xx_pcmcia.c | 7 | ||||
-rw-r--r-- | drivers/pcmcia/omap_cf.c | 2 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_cis.c | 356 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_ioctl.c | 40 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_resource.c | 634 | ||||
-rw-r--r-- | drivers/pcmcia/pxa2xx_vpac270.c | 229 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_iodyn.c | 172 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_mgr.c | 112 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_nonstatic.c | 164 | ||||
-rw-r--r-- | drivers/pcmcia/yenta_socket.c | 7 |
19 files changed, 1152 insertions, 800 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index d189e4743e6..d0f5ad30607 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -49,26 +49,6 @@ config PCMCIA_LOAD_CIS If unsure, say Y. -config PCMCIA_IOCTL - bool "PCMCIA control ioctl (obsolete)" - depends on PCMCIA && ARM && !SMP && !PREEMPT - default y - help - If you say Y here, the deprecated ioctl interface to the PCMCIA - subsystem will be built. It is needed by the deprecated pcmcia-cs - tools (cardmgr, cardctl) to function properly. - - You should use the new pcmciautils package instead (see - <file:Documentation/Changes> for location and details). - - This config option will most likely be removed from kernel 2.6.35, - the associated code from kernel 2.6.36. - - As the PCMCIA ioctl is not locking safe, it depends on !SMP and - !PREEMPT. - - If unsure, say N. - config CARDBUS bool "32-bit CardBus support" depends on PCI @@ -234,7 +214,8 @@ config PCMCIA_PXA2XX depends on ARM && ARCH_PXA && PCMCIA depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ - || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2) + || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ + || MACH_VPAC270) select PCMCIA_SOC_COMMON help Say Y here to include support for the PXA2xx PCMCIA controller @@ -317,7 +298,7 @@ config ELECTRA_CF PA Semi Electra eval board. config PCCARD_NONSTATIC - tristate + bool config PCCARD_IODYN bool diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 381b031d9d7..d006e8beab9 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -2,15 +2,18 @@ # Makefile for the kernel pcmcia subsystem (c/o David Hinds) # -pcmcia_core-y += cs.o rsrc_mgr.o socket_sysfs.o +pcmcia_core-y += cs.o socket_sysfs.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o obj-$(CONFIG_PCCARD) += pcmcia_core.o -pcmcia-y += ds.o pcmcia_resource.o cistpl.o +pcmcia-y += ds.o pcmcia_resource.o cistpl.o pcmcia_cis.o pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o obj-$(CONFIG_PCMCIA) += pcmcia.o -obj-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o +pcmcia_rsrc-y += rsrc_mgr.o +pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o +pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o +obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o # socket drivers @@ -66,6 +69,7 @@ pxa2xx-obj-$(CONFIG_MACH_PALMTC) += pxa2xx_palmtc.o pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o +pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c index 9e84d039de4..eae9cbe37a3 100644 --- a/drivers/pcmcia/bfin_cf_pcmcia.c +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -113,7 +113,7 @@ static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp) if (bfin_cf_present(cf->cd_pfx)) { *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; - s->irq.AssignedIRQ = 0; + s->pcmcia_irq = 0; s->pci_irq = cf->irq; } else diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index e6ab2a47d8c..9a58862f140 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -94,7 +94,6 @@ int __ref cb_alloc(struct pcmcia_socket *s) pci_enable_bridges(bus); pci_bus_add_devices(bus); - s->irq.AssignedIRQ = s->pci_irq; return 0; } diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 854959cada3..8844bc3e311 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -129,6 +129,8 @@ static void __iomem *set_cis_map(struct pcmcia_socket *s, /** * pcmcia_read_cis_mem() - low-level function to read CIS memory + * + * must be called with ops_mutex held */ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) @@ -138,7 +140,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len); - mutex_lock(&s->ops_mutex); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed locations in common memory */ @@ -153,7 +154,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); memset(ptr, 0xff, len); - mutex_unlock(&s->ops_mutex); return -1; } @@ -184,7 +184,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); memset(ptr, 0xff, len); - mutex_unlock(&s->ops_mutex); return -1; } end = sys + s->map_size; @@ -198,7 +197,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, addr = 0; } } - mutex_unlock(&s->ops_mutex); dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n", *(u_char *)(ptr+0), *(u_char *)(ptr+1), *(u_char *)(ptr+2), *(u_char *)(ptr+3)); @@ -209,7 +207,8 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, /** * pcmcia_write_cis_mem() - low-level function to write CIS memory * - * Probably only useful for writing one-byte registers. + * Probably only useful for writing one-byte registers. Must be called + * with ops_mutex held. */ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) @@ -220,7 +219,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len); - mutex_lock(&s->ops_mutex); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed locations in common memory */ @@ -234,7 +232,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, ((cis_width) ? MAP_16BIT : 0)); if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); - mutex_unlock(&s->ops_mutex); return; /* FIXME: Error */ } @@ -260,7 +257,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, sys = set_cis_map(s, card_offset, flags); if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); - mutex_unlock(&s->ops_mutex); return; /* FIXME: error */ } @@ -275,7 +271,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, addr = 0; } } - mutex_unlock(&s->ops_mutex); } @@ -314,7 +309,6 @@ static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, return 0; } } - mutex_unlock(&s->ops_mutex); ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr); @@ -326,11 +320,11 @@ static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, cis->len = len; cis->attr = attr; memcpy(cis->cache, ptr, len); - mutex_lock(&s->ops_mutex); list_add(&cis->node, &s->cis_cache); - mutex_unlock(&s->ops_mutex); } } + mutex_unlock(&s->ops_mutex); + return ret; } @@ -386,6 +380,7 @@ int verify_cis_cache(struct pcmcia_socket *s) "no memory for verifying CIS\n"); return -ENOMEM; } + mutex_lock(&s->ops_mutex); list_for_each_entry(cis, &s->cis_cache, node) { int len = cis->len; @@ -395,10 +390,12 @@ int verify_cis_cache(struct pcmcia_socket *s) ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); if (ret || memcmp(buf, cis->cache, len) != 0) { kfree(buf); + mutex_unlock(&s->ops_mutex); return -1; } } kfree(buf); + mutex_unlock(&s->ops_mutex); return 0; } @@ -1362,106 +1359,6 @@ EXPORT_SYMBOL(pcmcia_parse_tuple); /** - * pccard_read_tuple() - internal CIS tuple access - * @s: the struct pcmcia_socket where the card is inserted - * @function: the device function we loop for - * @code: which CIS code shall we look for? - * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) - * - * pccard_read_tuple() reads out one tuple and attempts to parse it - */ -int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, - cisdata_t code, void *parse) -{ - tuple_t tuple; - cisdata_t *buf; - int ret; - - buf = kmalloc(256, GFP_KERNEL); - if (buf == NULL) { - dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); - return -ENOMEM; - } - tuple.DesiredTuple = code; - tuple.Attributes = 0; - if (function == BIND_FN_ALL) - tuple.Attributes = TUPLE_RETURN_COMMON; - ret = pccard_get_first_tuple(s, function, &tuple); - if (ret != 0) - goto done; - tuple.TupleData = buf; - tuple.TupleOffset = 0; - tuple.TupleDataMax = 255; - ret = pccard_get_tuple_data(s, &tuple); - if (ret != 0) - goto done; - ret = pcmcia_parse_tuple(&tuple, parse); -done: - kfree(buf); - return ret; -} - - -/** - * pccard_loop_tuple() - loop over tuples in the CIS - * @s: the struct pcmcia_socket where the card is inserted - * @function: the device function we loop for - * @code: which CIS code shall we look for? - * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) - * @priv_data: private data to be passed to the loop_tuple function. - * @loop_tuple: function to call for each CIS entry of type @function. IT - * gets passed the raw tuple, the paresed tuple (if @parse is - * set) and @priv_data. - * - * pccard_loop_tuple() loops over all CIS entries of type @function, and - * calls the @loop_tuple function for each entry. If the call to @loop_tuple - * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. - */ -int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, - cisdata_t code, cisparse_t *parse, void *priv_data, - int (*loop_tuple) (tuple_t *tuple, - cisparse_t *parse, - void *priv_data)) -{ - tuple_t tuple; - cisdata_t *buf; - int ret; - - buf = kzalloc(256, GFP_KERNEL); - if (buf == NULL) { - dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); - return -ENOMEM; - } - - tuple.TupleData = buf; - tuple.TupleDataMax = 255; - tuple.TupleOffset = 0; - tuple.DesiredTuple = code; - tuple.Attributes = 0; - - ret = pccard_get_first_tuple(s, function, &tuple); - while (!ret) { - if (pccard_get_tuple_data(s, &tuple)) - goto next_entry; - - if (parse) - if (pcmcia_parse_tuple(&tuple, parse)) - goto next_entry; - - ret = loop_tuple(&tuple, parse, priv_data); - if (!ret) - break; - -next_entry: - ret = pccard_get_next_tuple(s, function, &tuple); - } - - kfree(buf); - return ret; -} - - -/** * pccard_validate_cis() - check whether card has a sensible CIS * @s: the struct pcmcia_socket we are to check * @info: returns the number of tuples in the (valid) CIS, or 0 @@ -1634,7 +1531,7 @@ static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, } -static ssize_t pccard_show_cis(struct kobject *kobj, +static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -1665,7 +1562,7 @@ static ssize_t pccard_show_cis(struct kobject *kobj, } -static ssize_t pccard_store_cis(struct kobject *kobj, +static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index c3383750e33..976d80706ea 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -337,7 +337,6 @@ static void socket_shutdown(struct pcmcia_socket *s) s->socket = dead_socket; s->ops->init(s); s->ops->set_socket(s, &s->socket); - s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; kfree(s->fake_cis); s->fake_cis = NULL; diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index f95864c2191..4126a75445e 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -52,13 +52,11 @@ struct cis_cache_entry { struct pccard_resource_ops { int (*validate_mem) (struct pcmcia_socket *s); - int (*adjust_io_region) (struct resource *res, - unsigned long r_start, - unsigned long r_end, - struct pcmcia_socket *s); - struct resource* (*find_io) (unsigned long base, int num, - unsigned long align, - struct pcmcia_socket *s); + int (*find_io) (struct pcmcia_socket *s, + unsigned int attr, + unsigned int *base, + unsigned int num, + unsigned int align); struct resource* (*find_mem) (unsigned long base, unsigned long num, unsigned long align, int low, struct pcmcia_socket *s); @@ -89,6 +87,14 @@ struct pccard_resource_ops { /* + * Stuff internal to module "pcmcia_rsrc": + */ +extern int static_init(struct pcmcia_socket *s); +extern struct resource *pcmcia_make_resource(unsigned long start, + unsigned long end, + int flags, const char *name); + +/* * Stuff internal to module "pcmcia_core": */ @@ -149,6 +155,8 @@ extern struct resource *pcmcia_find_mem_region(u_long base, int low, struct pcmcia_socket *s); +void pcmcia_cleanup_irq(struct pcmcia_socket *s); +int pcmcia_setup_irq(struct pcmcia_device *p_dev); /* cistpl.c */ extern struct bin_attribute pccard_cis_attr; diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 041eee43fd8..7ef7adee5e4 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -371,8 +371,6 @@ static int pcmcia_device_remove(struct device *dev) if (p_drv->remove) p_drv->remove(p_dev); - p_dev->dev_node = NULL; - /* check for proper unloading */ if (p_dev->_irq || p_dev->_io || p_dev->_locked) dev_printk(KERN_INFO, dev, @@ -479,15 +477,6 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) } -/* device_add_lock is needed to avoid double registration by cardmgr and kernel. - * Serializes pcmcia_device_add; will most likely be removed in future. - * - * While it has the caveat that adding new PCMCIA devices inside(!) device_register() - * won't work, this doesn't matter much at the moment: the driver core doesn't - * support it either. - */ -static DEFINE_MUTEX(device_add_lock); - struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev, *tmp_dev; @@ -497,8 +486,6 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu if (!s) return NULL; - mutex_lock(&device_add_lock); - pr_debug("adding device to %d, function %d\n", s->sock, function); p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL); @@ -538,8 +525,8 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu /* * p_dev->function_config must be the same for all card functions. - * Note that this is serialized by the device_add_lock, so that - * only one such struct will be created. + * Note that this is serialized by ops_mutex, so that only one + * such struct will be created. */ list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) if (p_dev->func == tmp_dev->func) { @@ -552,28 +539,31 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu /* Add to the list in pcmcia_bus_socket */ list_add(&p_dev->socket_device_list, &s->devices_list); - mutex_unlock(&s->ops_mutex); + if (pcmcia_setup_irq(p_dev)) + dev_warn(&p_dev->dev, + "IRQ setup failed -- device might not work\n"); if (!p_dev->function_config) { dev_dbg(&p_dev->dev, "creating config_t\n"); p_dev->function_config = kzalloc(sizeof(struct config_t), GFP_KERNEL); - if (!p_dev->function_config) + if (!p_dev->function_config) { + mutex_unlock(&s->ops_mutex); goto err_unreg; + } kref_init(&p_dev->function_config->ref); } + mutex_unlock(&s->ops_mutex); dev_printk(KERN_NOTICE, &p_dev->dev, - "pcmcia: registering new device %s\n", - p_dev->devname); + "pcmcia: registering new device %s (IRQ: %d)\n", + p_dev->devname, p_dev->irq); pcmcia_device_query(p_dev); if (device_register(&p_dev->dev)) goto err_unreg; - mutex_unlock(&device_add_lock); - return p_dev; err_unreg: @@ -591,7 +581,6 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu kfree(p_dev->devname); kfree(p_dev); err_put: - mutex_unlock(&device_add_lock); pcmcia_put_socket(s); return NULL; @@ -1258,6 +1247,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) handle_event(skt, event); mutex_lock(&s->ops_mutex); destroy_cis_cache(s); + pcmcia_cleanup_irq(s); mutex_unlock(&s->ops_mutex); break; diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index 2e59fe947d2..f94d8281cfb 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -185,7 +185,7 @@ static int __devinit electra_cf_probe(struct of_device *ofdev, const struct of_device_id *match) { struct device *device = &ofdev->dev; - struct device_node *np = ofdev->node; + struct device_node *np = ofdev->dev.of_node; struct electra_cf_socket *cf; struct resource mem, io; int status; @@ -357,8 +357,11 @@ static const struct of_device_id electra_cf_match[] = { MODULE_DEVICE_TABLE(of, electra_cf_match); static struct of_platform_driver electra_cf_driver = { - .name = (char *)driver_name, - .match_table = electra_cf_match, + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + .of_match_table = electra_cf_match, + }, .probe = electra_cf_probe, .remove = electra_cf_remove, }; diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index 41cc954a5ff..1a648b90b63 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -1298,8 +1298,11 @@ static const struct of_device_id m8xx_pcmcia_match[] = { MODULE_DEVICE_TABLE(of, m8xx_pcmcia_match); static struct of_platform_driver m8xx_pcmcia_driver = { - .name = driver_name, - .match_table = m8xx_pcmcia_match, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + .match_table = m8xx_pcmcia_match, + }, .probe = m8xx_probe, .remove = m8xx_remove, }; diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index a7cfc7964c7..0ad06a3bd56 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -117,7 +117,7 @@ static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp) *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; cf = container_of(s, struct omap_cf_socket, socket); - s->irq.AssignedIRQ = 0; + s->pcmcia_irq = 0; s->pci_irq = cf->irq; } else *sp = 0; diff --git a/drivers/pcmcia/pcmcia_cis.c b/drivers/pcmcia/pcmcia_cis.c new file mode 100644 index 00000000000..4a65eaf96b0 --- /dev/null +++ b/drivers/pcmcia/pcmcia_cis.c @@ -0,0 +1,356 @@ +/* + * PCMCIA high-level CIS access functions + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Copyright (C) 1999 David A. Hinds + * Copyright (C) 2004-2009 Dominik Brodowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ss.h> +#include <pcmcia/cs.h> +#include <pcmcia/ds.h> +#include "cs_internal.h" + + +/** + * pccard_read_tuple() - internal CIS tuple access + * @s: the struct pcmcia_socket where the card is inserted + * @function: the device function we loop for + * @code: which CIS code shall we look for? + * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) + * + * pccard_read_tuple() reads out one tuple and attempts to parse it + */ +int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, + cisdata_t code, void *parse) +{ + tuple_t tuple; + cisdata_t *buf; + int ret; + + buf = kmalloc(256, GFP_KERNEL); + if (buf == NULL) { + dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); + return -ENOMEM; + } + tuple.DesiredTuple = code; + tuple.Attributes = 0; + if (function == BIND_FN_ALL) + tuple.Attributes = TUPLE_RETURN_COMMON; + ret = pccard_get_first_tuple(s, function, &tuple); + if (ret != 0) + goto done; + tuple.TupleData = buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + ret = pccard_get_tuple_data(s, &tuple); + if (ret != 0) + goto done; + ret = pcmcia_parse_tuple(&tuple, parse); +done: + kfree(buf); + return ret; +} + + +/** + * pccard_loop_tuple() - loop over tuples in the CIS + * @s: the struct pcmcia_socket where the card is inserted + * @function: the device function we loop for + * @code: which CIS code shall we look for? + * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) + * @priv_data: private data to be passed to the loop_tuple function. + * @loop_tuple: function to call for each CIS entry of type @function. IT + * gets passed the raw tuple, the paresed tuple (if @parse is + * set) and @priv_data. + * + * pccard_loop_tuple() loops over all CIS entries of type @function, and + * calls the @loop_tuple function for each entry. If the call to @loop_tuple + * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. + */ +int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, + cisdata_t code, cisparse_t *parse, void *priv_data, + int (*loop_tuple) (tuple_t *tuple, + cisparse_t *parse, + void *priv_data)) +{ + tuple_t tuple; + cisdata_t *buf; + int ret; + + buf = kzalloc(256, GFP_KERNEL); + if (buf == NULL) { + dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); + return -ENOMEM; + } + + tuple.TupleData = buf; + tuple.TupleDataMax = 255; + tuple.TupleOffset = 0; + tuple.DesiredTuple = code; + tuple.Attributes = 0; + + ret = pccard_get_first_tuple(s, function, &tuple); + while (!ret) { + if (pccard_get_tuple_data(s, &tuple)) + goto next_entry; + + if (parse) + if (pcmcia_parse_tuple(&tuple, parse)) + goto next_entry; + + ret = loop_tuple(&tuple, parse, priv_data); + if (!ret) + break; + +next_entry: + ret = pccard_get_next_tuple(s, function, &tuple); + } + + kfree(buf); + return ret; +} + +struct pcmcia_cfg_mem { + struct pcmcia_device *p_dev; + void *priv_data; + int (*conf_check) (struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data); + cisparse_t parse; + cistpl_cftable_entry_t dflt; +}; + +/** + * pcmcia_do_loop_config() - internal helper for pcmcia_loop_config() + * + * pcmcia_do_loop_config() is the internal callback for the call from + * pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred + * by a struct pcmcia_cfg_mem. + */ +static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv) +{ + cistpl_cftable_entry_t *cfg = &parse->cftable_entry; + struct pcmcia_cfg_mem *cfg_mem = priv; + + /* default values */ + cfg_mem->p_dev->conf.ConfigIndex = cfg->index; + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) + cfg_mem->dflt = *cfg; + + return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt, + cfg_mem->p_dev->socket->socket.Vcc, + cfg_mem->priv_data); +} + +/** + * pcmcia_loop_config() - loop over configuration options + * @p_dev: the struct pcmcia_device which we need to loop for. + * @conf_check: function to call for each configuration option. + * It gets passed the struct pcmcia_device, the CIS data + * describing the configuration option, and private data + * being passed to pcmcia_loop_config() + * @priv_data: private data to be passed to the conf_check function. + * + * pcmcia_loop_config() loops over all configuration options, and calls + * the driver-specific conf_check() for each one, checking whether + * it is a valid one. Returns 0 on success or errorcode otherwise. + */ +int pcmcia_loop_config(struct pcmcia_device *p_dev, + int (*conf_check) (struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data), + void *priv_data) +{ + struct pcmcia_cfg_mem *cfg_mem; + int ret; + + cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL); + if (cfg_mem == NULL) + return -ENOMEM; + + cfg_mem->p_dev = p_dev; + cfg_mem->conf_check = conf_check; + cfg_mem->priv_data = priv_data; + + ret = pccard_loop_tuple(p_dev->socket, p_dev->func, + CISTPL_CFTABLE_ENTRY, &cfg_mem->parse, + cfg_mem, pcmcia_do_loop_config); + + kfree(cfg_mem); + return ret; +} +EXPORT_SYMBOL(pcmcia_loop_config); + + +struct pcmcia_loop_mem { + struct pcmcia_device *p_dev; + void *priv_data; + int (*loop_tuple) (struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data); +}; + +/** + * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config() + * + * pcmcia_do_loop_tuple() is the internal callback for the call from + * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred + * by a struct pcmcia_cfg_mem. + */ +static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv) +{ + struct pcmcia_loop_mem *loop = priv; + + return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data); +}; + +/** + * pcmcia_loop_tuple() - loop over tuples in the CIS + * @p_dev: the struct pcmcia_device which we need to loop for. + * @code: which CIS code shall we look for? + * @priv_data: private data to be passed to the loop_tuple function. + * @loop_tuple: function to call for each CIS entry of type @function. IT + * gets passed the raw tuple and @priv_data. + * + * pcmcia_loop_tuple() loops over all CIS entries of type @function, and + * calls the @loop_tuple function for each entry. If the call to @loop_tuple + * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. + */ +int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, + int (*loop_tuple) (struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data), + void *priv_data) +{ + struct pcmcia_loop_mem loop = { + .p_dev = p_dev, + .loop_tuple = loop_tuple, + .priv_data = priv_data}; + + return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL, + &loop, pcmcia_do_loop_tuple); +} +EXPORT_SYMBOL(pcmcia_loop_tuple); + + +struct pcmcia_loop_get { + size_t len; + cisdata_t **buf; +}; + +/** + * pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple() + * + * pcmcia_do_get_tuple() is the internal callback for the call from + * pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in + * the first tuple, return 0 unconditionally. Create a memory buffer large + * enough to hold the content of the tuple, and fill it with the tuple data. + * The caller is responsible to free the buffer. + */ +static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple, + void *priv) +{ + struct pcmcia_loop_get *get = priv; + + *get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL); + if (*get->buf) { + get->len = tuple->TupleDataLen; + memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen); + } else + dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n"); + return 0; +} + +/** + * pcmcia_get_tuple() - get first tuple from CIS + * @p_dev: the struct pcmcia_device which we need to loop for. + * @code: which CIS code shall we look for? + * @buf: pointer to store the buffer to. + * + * pcmcia_get_tuple() gets the content of the first CIS entry of type @code. + * It returns the buffer length (or zero). The caller is responsible to free + * the buffer passed in @buf. + */ +size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code, + unsigned char **buf) +{ + struct pcmcia_loop_get get = { + .len = 0, + .buf = buf, + }; + + *get.buf = NULL; + pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get); + + return get.len; +} +EXPORT_SYMBOL(pcmcia_get_tuple); + + +/** + * pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis() + * + * pcmcia_do_get_mac() is the internal callback for the call from + * pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the + * tuple contains a proper LAN_NODE_ID of length 6, and copy the data + * to struct net_device->dev_addr[i]. + */ +static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple, + void *priv) +{ + struct net_device *dev = priv; + int i; + + if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) + return -EINVAL; + if (tuple->TupleDataLen < ETH_ALEN + 2) { + dev_warn(&p_dev->dev, "Invalid CIS tuple length for " + "LAN_NODE_ID\n"); + return -EINVAL; + } + + if (tuple->TupleData[1] != ETH_ALEN) { + dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n"); + return -EINVAL; + } + for (i = 0; i < 6; i++) + dev->dev_addr[i] = tuple->TupleData[i+2]; + return 0; +} + +/** + * pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE + * @p_dev: the struct pcmcia_device for which we want the address. + * @dev: a properly prepared struct net_device to store the info to. + * + * pcmcia_get_mac_from_cis() reads out the hardware MAC address from + * CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which + * must be set up properly by the driver (see examples!). + */ +int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev) +{ + return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev); +} +EXPORT_SYMBOL(pcmcia_get_mac_from_cis); + diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index 7631faa0cad..d007a2a0383 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -301,7 +301,9 @@ static int pccard_get_status(struct pcmcia_socket *s, (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { u_char reg; if (c->CardValues & PRESENT_PIN_REPLACE) { + mutex_lock(&s->ops_mutex); pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); + mutex_unlock(&s->ops_mutex); status->CardState |= (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; status->CardState |= @@ -315,7 +317,9 @@ static int pccard_get_status(struct pcmcia_socket *s, status->CardState |= CS_EVENT_READY_CHANGE; } if (c->CardValues & PRESENT_EXT_STATUS) { + mutex_lock(&s->ops_mutex); pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); + mutex_unlock(&s->ops_mutex); status->CardState |= (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; } @@ -351,7 +355,7 @@ static int pccard_get_configuration_info(struct pcmcia_socket *s, if (s->state & SOCKET_CARDBUS_CONFIG) { config->Attributes = CONF_VALID_CLIENT; config->IntType = INT_CARDBUS; - config->AssignedIRQ = s->irq.AssignedIRQ; + config->AssignedIRQ = s->pcmcia_irq; if (config->AssignedIRQ) config->Attributes |= CONF_ENABLE_IRQ; if (s->io[0].res) { @@ -391,7 +395,7 @@ static int pccard_get_configuration_info(struct pcmcia_socket *s, config->ExtStatus = c->ExtStatus; config->Present = config->CardValues = c->CardValues; config->IRQAttributes = c->irq.Attributes; - config->AssignedIRQ = s->irq.AssignedIRQ; + config->AssignedIRQ = s->pcmcia_irq; config->BasePort1 = c->io.BasePort1; config->NumPorts1 = c->io.NumPorts1; config->Attributes1 = c->io.Attributes1; @@ -571,7 +575,6 @@ static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s) static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first) { - dev_node_t *node; struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; int ret = 0; @@ -633,21 +636,13 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int goto err_put; } - if (first) - node = p_dev->dev_node; - else - for (node = p_dev->dev_node; node; node = node->next) - if (node == bind_info->next) - break; - if (!node) { + if (!first) { ret = -ENODEV; goto err_put; } - strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN); - bind_info->major = node->major; - bind_info->minor = node->minor; - bind_info->next = node->next; + strlcpy(bind_info->name, dev_name(&p_dev->dev), DEV_NAME_LEN); + bind_info->next = NULL; err_put: pcmcia_put_dev(p_dev); @@ -818,8 +813,7 @@ static u_int ds_poll(struct file *file, poll_table *wait) /*====================================================================*/ -static int ds_ioctl(struct inode *inode, struct file *file, - u_int cmd, u_long arg) +static int ds_ioctl(struct file *file, u_int cmd, u_long arg) { struct pcmcia_socket *s; void __user *uarg = (char __user *)arg; @@ -1026,13 +1020,25 @@ free_out: return err; } /* ds_ioctl */ +static long ds_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = ds_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + + /*====================================================================*/ static const struct file_operations ds_fops = { .owner = THIS_MODULE, .open = ds_open, .release = ds_release, - .ioctl = ds_ioctl, + .unlocked_ioctl = ds_unlocked_ioctl, .read = ds_read, .write = ds_write, .poll = ds_poll, diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 7c3d03bb4f3..29f91fac1df 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -23,6 +23,8 @@ #include <linux/netdevice.h> #include <linux/slab.h> +#include <asm/irq.h> + #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> @@ -38,29 +40,6 @@ static int io_speed; module_param(io_speed, int, 0444); -#ifdef CONFIG_PCMCIA_PROBE -#include <asm/irq.h> -/* mask of IRQs already reserved by other cards, we should avoid using them */ -static u8 pcmcia_used_irq[NR_IRQS]; -#endif - -static int pcmcia_adjust_io_region(struct resource *res, unsigned long start, - unsigned long end, struct pcmcia_socket *s) -{ - if (s->resource_ops->adjust_io_region) - return s->resource_ops->adjust_io_region(res, start, end, s); - return -ENOMEM; -} - -static struct resource *pcmcia_find_io_region(unsigned long base, int num, - unsigned long align, - struct pcmcia_socket *s) -{ - if (s->resource_ops->find_io) - return s->resource_ops->find_io(base, num, align, s); - return NULL; -} - int pcmcia_validate_mem(struct pcmcia_socket *s) { if (s->resource_ops->validate_mem) @@ -86,8 +65,7 @@ struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, static int alloc_io_space(struct pcmcia_socket *s, u_int attr, unsigned int *base, unsigned int num, u_int lines) { - int i; - unsigned int try, align; + unsigned int align; align = (*base) ? (lines ? 1<<lines : 0) : 1; if (align && (align < num)) { @@ -104,50 +82,8 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr, *base, align); align = 0; } - if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { - *base = s->io_offset | (*base & 0x0fff); - return 0; - } - /* Check for an already-allocated window that must conflict with - * what was asked for. It is a hack because it does not catch all - * potential conflicts, just the most obvious ones. - */ - for (i = 0; i < MAX_IO_WIN; i++) - if ((s->io[i].res) && *base && - ((s->io[i].res->start & (align-1)) == *base)) - return 1; - for (i = 0; i < MAX_IO_WIN; i++) { - if (!s->io[i].res) { - s->io[i].res = pcmcia_find_io_region(*base, num, align, s); - if (s->io[i].res) { - *base = s->io[i].res->start; - s->io[i].res->flags = (s->io[i].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS); - s->io[i].InUse = num; - break; - } else - return 1; - } else if ((s->io[i].res->flags & IORESOURCE_BITS) != (attr & IORESOURCE_BITS)) - continue; - /* Try to extend top of window */ - try = s->io[i].res->end + 1; - if ((*base == 0) || (*base == try)) - if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start, - s->io[i].res->end + num, s) == 0) { - *base = try; - s->io[i].InUse += num; - break; - } - /* Try to extend bottom of window */ - try = s->io[i].res->start - num; - if ((*base == 0) || (*base == try)) - if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num, - s->io[i].res->end, s) == 0) { - *base = try; - s->io[i].InUse += num; - break; - } - } - return (i == MAX_IO_WIN); + + return s->resource_ops->find_io(s, attr, base, num, align); } /* alloc_io_space */ @@ -187,6 +123,7 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, config_t *c; int addr; u_char val; + int ret = 0; if (!p_dev || !p_dev->function_config) return -EINVAL; @@ -203,11 +140,10 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, } addr = (c->ConfigBase + reg->Offset) >> 1; - mutex_unlock(&s->ops_mutex); switch (reg->Action) { case CS_READ: - pcmcia_read_cis_mem(s, 1, addr, 1, &val); + ret = pcmcia_read_cis_mem(s, 1, addr, 1, &val); reg->Value = val; break; case CS_WRITE: @@ -216,10 +152,11 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, break; default: dev_dbg(&s->dev, "Invalid conf register request\n"); - return -EINVAL; + ret = -EINVAL; break; } - return 0; + mutex_unlock(&s->ops_mutex); + return ret; } /* pcmcia_access_configuration_register */ EXPORT_SYMBOL(pcmcia_access_configuration_register); @@ -275,19 +212,9 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, goto unlock; } - if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { - if (mod->Attributes & CONF_ENABLE_IRQ) { - c->Attributes |= CONF_ENABLE_IRQ; - s->socket.io_irq = s->irq.AssignedIRQ; - } else { - c->Attributes &= ~CONF_ENABLE_IRQ; - s->socket.io_irq = 0; - } - s->ops->set_socket(s, &s->socket); - } - - if (mod->Attributes & CONF_VCC_CHANGE_VALID) { - dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n"); + if (mod->Attributes & (CONF_IRQ_CHANGE_VALID | CONF_VCC_CHANGE_VALID)) { + dev_dbg(&s->dev, + "changing Vcc or IRQ is not allowed at this time\n"); ret = -EINVAL; goto unlock; } @@ -422,52 +349,6 @@ out: } /* pcmcia_release_io */ -static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) -{ - struct pcmcia_socket *s = p_dev->socket; - config_t *c; - int ret = -EINVAL; - - mutex_lock(&s->ops_mutex); - - c = p_dev->function_config; - - if (!p_dev->_irq) - goto out; - - p_dev->_irq = 0; - - if (c->state & CONFIG_LOCKED) - goto out; - - if (c->irq.Attributes != req->Attributes) { - dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n"); - goto out; - } - if (s->irq.AssignedIRQ != req->AssignedIRQ) { - dev_dbg(&s->dev, "IRQ must match assigned one\n"); - goto out; - } - if (--s->irq.Config == 0) { - c->state &= ~CONFIG_IRQ_REQ; - s->irq.AssignedIRQ = 0; - } - - if (req->Handler) - free_irq(req->AssignedIRQ, p_dev->priv); - -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[req->AssignedIRQ]--; -#endif - ret = 0; - -out: - mutex_unlock(&s->ops_mutex); - - return ret; -} /* pcmcia_release_irq */ - - int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh) { struct pcmcia_socket *s = p_dev->socket; @@ -551,12 +432,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (req->Attributes & CONF_ENABLE_SPKR) s->socket.flags |= SS_SPKR_ENA; if (req->Attributes & CONF_ENABLE_IRQ) - s->socket.io_irq = s->irq.AssignedIRQ; + s->socket.io_irq = s->pcmcia_irq; else s->socket.io_irq = 0; s->ops->set_socket(s, &s->socket); s->lock_count++; - mutex_unlock(&s->ops_mutex); /* Set up CIS configuration registers */ base = c->ConfigBase = req->ConfigBase; @@ -574,9 +454,9 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (req->Present & PRESENT_IOBASE_0) c->Option |= COR_ADDR_DECODE; } - if (c->state & CONFIG_IRQ_REQ) - if (!(c->irq.Attributes & IRQ_FORCED_PULSE)) - c->Option |= COR_LEVEL_REQ; + if ((req->Attributes & CONF_ENABLE_IRQ) && + !(req->Attributes & CONF_ENABLE_PULSE_IRQ)) + c->Option |= COR_LEVEL_REQ; pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option); mdelay(40); } @@ -605,7 +485,6 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, /* Configure I/O windows */ if (c->state & CONFIG_IO_REQ) { - mutex_lock(&s->ops_mutex); iomap.speed = io_speed; for (i = 0; i < MAX_IO_WIN; i++) if (s->io[i].res) { @@ -624,11 +503,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, s->ops->set_io_map(s, &iomap); s->io[i].Config++; } - mutex_unlock(&s->ops_mutex); } c->state |= CONFIG_LOCKED; p_dev->_locked = 1; + mutex_unlock(&s->ops_mutex); return 0; } /* pcmcia_request_configuration */ EXPORT_SYMBOL(pcmcia_request_configuration); @@ -706,137 +585,176 @@ out: EXPORT_SYMBOL(pcmcia_request_io); -/** pcmcia_request_irq +/** + * pcmcia_request_irq() - attempt to request a IRQ for a PCMCIA device * - * Request_irq() reserves an irq for this client. + * pcmcia_request_irq() is a wrapper around request_irq which will allow + * the PCMCIA core to clean up the registration in pcmcia_disable_device(). + * Drivers are free to use request_irq() directly, but then they need to + * call free_irq themselfves, too. Also, only IRQF_SHARED capable IRQ + * handlers are allowed. + */ +int __must_check pcmcia_request_irq(struct pcmcia_device *p_dev, + irq_handler_t handler) +{ + int ret; + + if (!p_dev->irq) + return -EINVAL; + + ret = request_irq(p_dev->irq, handler, IRQF_SHARED, + p_dev->devname, p_dev->priv); + if (!ret) + p_dev->_irq = 1; + + return ret; +} +EXPORT_SYMBOL(pcmcia_request_irq); + + +/** + * pcmcia_request_exclusive_irq() - attempt to request an exclusive IRQ first * - * Also, since Linux only reserves irq's when they are actually - * hooked, we don't guarantee that an irq will still be available - * when the configuration is locked. Now that I think about it, - * there might be a way to fix this using a dummy handler. + * pcmcia_request_exclusive_irq() is a wrapper around request_irq which + * attempts first to request an exclusive IRQ. If it fails, it also accepts + * a shared IRQ, but prints out a warning. PCMCIA drivers should allow for + * IRQ sharing and either use request_irq directly (then they need to call + * free_irq themselves, too), or the pcmcia_request_irq() function. */ +int __must_check +__pcmcia_request_exclusive_irq(struct pcmcia_device *p_dev, + irq_handler_t handler) +{ + int ret; + + if (!p_dev->irq) + return -EINVAL; + + ret = request_irq(p_dev->irq, handler, 0, p_dev->devname, p_dev->priv); + if (ret) { + ret = pcmcia_request_irq(p_dev, handler); + dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: " + "request for exclusive IRQ could not be fulfilled.\n"); + dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver " + "needs updating to supported shared IRQ lines.\n"); + } + if (ret) + dev_printk(KERN_INFO, &p_dev->dev, "request_irq() failed\n"); + else + p_dev->_irq = 1; + + return ret; +} /* pcmcia_request_exclusive_irq */ +EXPORT_SYMBOL(__pcmcia_request_exclusive_irq); + #ifdef CONFIG_PCMCIA_PROBE + +/* mask of IRQs already reserved by other cards, we should avoid using them */ +static u8 pcmcia_used_irq[NR_IRQS]; + static irqreturn_t test_action(int cpl, void *dev_id) { return IRQ_NONE; } -#endif -int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) +/** + * pcmcia_setup_isa_irq() - determine whether an ISA IRQ can be used + * @p_dev - the associated PCMCIA device + * + * locking note: must be called with ops_mutex locked. + */ +static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type) { struct pcmcia_socket *s = p_dev->socket; - config_t *c; - int ret = -EINVAL, irq = 0; - int type; + unsigned int try, irq; + u32 mask = s->irq_mask; + int ret = -ENODEV; - mutex_lock(&s->ops_mutex); + for (try = 0; try < 64; try++) { + irq = try % 32; - if (!(s->state & SOCKET_PRESENT)) { - dev_dbg(&s->dev, "No card present\n"); - goto out; - } - c = p_dev->function_config; - if (c->state & CONFIG_LOCKED) { - dev_dbg(&s->dev, "Configuration is locked\n"); - goto out; - } - if (c->state & CONFIG_IRQ_REQ) { - dev_dbg(&s->dev, "IRQ already configured\n"); - goto out; + /* marked as available by driver, not blocked by userspace? */ + if (!((mask >> irq) & 1)) + continue; + + /* avoid an IRQ which is already used by another PCMCIA card */ + if ((try < 32) && pcmcia_used_irq[irq]) + continue; + + /* register the correct driver, if possible, to check whether + * registering a dummy handle works, i.e. if the IRQ isn't + * marked as used by the kernel resource management core */ + ret = request_irq(irq, test_action, type, p_dev->devname, + p_dev); + if (!ret) { + free_irq(irq, p_dev); + p_dev->irq = s->pcmcia_irq = irq; + pcmcia_used_irq[irq]++; + break; + } } - /* Decide what type of interrupt we are registering */ - type = 0; - if (s->functions > 1) /* All of this ought to be handled higher up */ - type = IRQF_SHARED; - else if (req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) - type = IRQF_SHARED; - else - printk(KERN_WARNING "pcmcia: Driver needs updating to support IRQ sharing.\n"); + return ret; +} - /* If the interrupt is already assigned, it must be the same */ - if (s->irq.AssignedIRQ != 0) - irq = s->irq.AssignedIRQ; +void pcmcia_cleanup_irq(struct pcmcia_socket *s) +{ + pcmcia_used_irq[s->pcmcia_irq]--; + s->pcmcia_irq = 0; +} -#ifdef CONFIG_PCMCIA_PROBE - if (!irq) { - int try; - u32 mask = s->irq_mask; - void *data = p_dev; /* something unique to this device */ +#else /* CONFIG_PCMCIA_PROBE */ - for (try = 0; try < 64; try++) { - irq = try % 32; +static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type) +{ + return -EINVAL; +} - /* marked as available by driver, and not blocked by userspace? */ - if (!((mask >> irq) & 1)) - continue; +void pcmcia_cleanup_irq(struct pcmcia_socket *s) +{ + s->pcmcia_irq = 0; + return; +} - /* avoid an IRQ which is already used by a PCMCIA card */ - if ((try < 32) && pcmcia_used_irq[irq]) - continue; +#endif /* CONFIG_PCMCIA_PROBE */ - /* register the correct driver, if possible, of check whether - * registering a dummy handle works, i.e. if the IRQ isn't - * marked as used by the kernel resource management core */ - ret = request_irq(irq, - (req->Handler) ? req->Handler : test_action, - type, - p_dev->devname, - (req->Handler) ? p_dev->priv : data); - if (!ret) { - if (!req->Handler) - free_irq(irq, data); - break; - } - } - } -#endif - /* only assign PCI irq if no IRQ already assigned */ - if (ret && !s->irq.AssignedIRQ) { - if (!s->pci_irq) { - dev_printk(KERN_INFO, &s->dev, "no IRQ found\n"); - goto out; - } - type = IRQF_SHARED; - irq = s->pci_irq; - } - if (ret && req->Handler) { - ret = request_irq(irq, req->Handler, type, - p_dev->devname, p_dev->priv); - if (ret) { - dev_printk(KERN_INFO, &s->dev, - "request_irq() failed\n"); - goto out; - } - } +/** + * pcmcia_setup_irq() - determine IRQ to be used for device + * @p_dev - the associated PCMCIA device + * + * locking note: must be called with ops_mutex locked. + */ +int pcmcia_setup_irq(struct pcmcia_device *p_dev) +{ + struct pcmcia_socket *s = p_dev->socket; - /* Make sure the fact the request type was overridden is passed back */ - if (type == IRQF_SHARED && !(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) { - req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING; - dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: " - "request for exclusive IRQ could not be fulfilled.\n"); - dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver " - "needs updating to supported shared IRQ lines.\n"); + if (p_dev->irq) + return 0; + + /* already assigned? */ + if (s->pcmcia_irq) { + p_dev->irq = s->pcmcia_irq; + return 0; } - c->irq.Attributes = req->Attributes; - s->irq.AssignedIRQ = req->AssignedIRQ = irq; - s->irq.Config++; - c->state |= CONFIG_IRQ_REQ; - p_dev->_irq = 1; + /* prefer an exclusive ISA irq */ + if (!pcmcia_setup_isa_irq(p_dev, 0)) + return 0; -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[irq]++; -#endif + /* but accept a shared ISA irq */ + if (!pcmcia_setup_isa_irq(p_dev, IRQF_SHARED)) + return 0; - ret = 0; -out: - mutex_unlock(&s->ops_mutex); - return ret; -} /* pcmcia_request_irq */ -EXPORT_SYMBOL(pcmcia_request_irq); + /* but use the PCI irq otherwise */ + if (s->pci_irq) { + p_dev->irq = s->pcmcia_irq = s->pci_irq; + return 0; + } + + return -EINVAL; +} /** pcmcia_request_window @@ -939,237 +857,9 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev) { pcmcia_release_configuration(p_dev); pcmcia_release_io(p_dev, &p_dev->io); - pcmcia_release_irq(p_dev, &p_dev->irq); + if (p_dev->_irq) + free_irq(p_dev->irq, p_dev->priv); if (p_dev->win) pcmcia_release_window(p_dev, p_dev->win); } EXPORT_SYMBOL(pcmcia_disable_device); - - -struct pcmcia_cfg_mem { - struct pcmcia_device *p_dev; - void *priv_data; - int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, - void *priv_data); - cisparse_t parse; - cistpl_cftable_entry_t dflt; -}; - -/** - * pcmcia_do_loop_config() - internal helper for pcmcia_loop_config() - * - * pcmcia_do_loop_config() is the internal callback for the call from - * pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred - * by a struct pcmcia_cfg_mem. - */ -static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv) -{ - cistpl_cftable_entry_t *cfg = &parse->cftable_entry; - struct pcmcia_cfg_mem *cfg_mem = priv; - - /* default values */ - cfg_mem->p_dev->conf.ConfigIndex = cfg->index; - if (cfg->flags & CISTPL_CFTABLE_DEFAULT) - cfg_mem->dflt = *cfg; - - return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt, - cfg_mem->p_dev->socket->socket.Vcc, - cfg_mem->priv_data); -} - -/** - * pcmcia_loop_config() - loop over configuration options - * @p_dev: the struct pcmcia_device which we need to loop for. - * @conf_check: function to call for each configuration option. - * It gets passed the struct pcmcia_device, the CIS data - * describing the configuration option, and private data - * being passed to pcmcia_loop_config() - * @priv_data: private data to be passed to the conf_check function. - * - * pcmcia_loop_config() loops over all configuration options, and calls - * the driver-specific conf_check() for each one, checking whether - * it is a valid one. Returns 0 on success or errorcode otherwise. - */ -int pcmcia_loop_config(struct pcmcia_device *p_dev, - int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, - void *priv_data), - void *priv_data) -{ - struct pcmcia_cfg_mem *cfg_mem; - int ret; - - cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL); - if (cfg_mem == NULL) - return -ENOMEM; - - cfg_mem->p_dev = p_dev; - cfg_mem->conf_check = conf_check; - cfg_mem->priv_data = priv_data; - - ret = pccard_loop_tuple(p_dev->socket, p_dev->func, - CISTPL_CFTABLE_ENTRY, &cfg_mem->parse, - cfg_mem, pcmcia_do_loop_config); - - kfree(cfg_mem); - return ret; -} -EXPORT_SYMBOL(pcmcia_loop_config); - - -struct pcmcia_loop_mem { - struct pcmcia_device *p_dev; - void *priv_data; - int (*loop_tuple) (struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv_data); -}; - -/** - * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config() - * - * pcmcia_do_loop_tuple() is the internal callback for the call from - * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred - * by a struct pcmcia_cfg_mem. - */ -static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv) -{ - struct pcmcia_loop_mem *loop = priv; - - return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data); -}; - -/** - * pcmcia_loop_tuple() - loop over tuples in the CIS - * @p_dev: the struct pcmcia_device which we need to loop for. - * @code: which CIS code shall we look for? - * @priv_data: private data to be passed to the loop_tuple function. - * @loop_tuple: function to call for each CIS entry of type @function. IT - * gets passed the raw tuple and @priv_data. - * - * pcmcia_loop_tuple() loops over all CIS entries of type @function, and - * calls the @loop_tuple function for each entry. If the call to @loop_tuple - * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. - */ -int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, - int (*loop_tuple) (struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv_data), - void *priv_data) -{ - struct pcmcia_loop_mem loop = { - .p_dev = p_dev, - .loop_tuple = loop_tuple, - .priv_data = priv_data}; - - return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL, - &loop, pcmcia_do_loop_tuple); -} -EXPORT_SYMBOL(pcmcia_loop_tuple); - - -struct pcmcia_loop_get { - size_t len; - cisdata_t **buf; -}; - -/** - * pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple() - * - * pcmcia_do_get_tuple() is the internal callback for the call from - * pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in - * the first tuple, return 0 unconditionally. Create a memory buffer large - * enough to hold the content of the tuple, and fill it with the tuple data. - * The caller is responsible to free the buffer. - */ -static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple, - void *priv) -{ - struct pcmcia_loop_get *get = priv; - - *get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL); - if (*get->buf) { - get->len = tuple->TupleDataLen; - memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen); - } else - dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n"); - return 0; -} - -/** - * pcmcia_get_tuple() - get first tuple from CIS - * @p_dev: the struct pcmcia_device which we need to loop for. - * @code: which CIS code shall we look for? - * @buf: pointer to store the buffer to. - * - * pcmcia_get_tuple() gets the content of the first CIS entry of type @code. - * It returns the buffer length (or zero). The caller is responsible to free - * the buffer passed in @buf. - */ -size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code, - unsigned char **buf) -{ - struct pcmcia_loop_get get = { - .len = 0, - .buf = buf, - }; - - *get.buf = NULL; - pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get); - - return get.len; -} -EXPORT_SYMBOL(pcmcia_get_tuple); - - -/** - * pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis() - * - * pcmcia_do_get_mac() is the internal callback for the call from - * pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the - * tuple contains a proper LAN_NODE_ID of length 6, and copy the data - * to struct net_device->dev_addr[i]. - */ -static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple, - void *priv) -{ - struct net_device *dev = priv; - int i; - - if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) - return -EINVAL; - if (tuple->TupleDataLen < ETH_ALEN + 2) { - dev_warn(&p_dev->dev, "Invalid CIS tuple length for " - "LAN_NODE_ID\n"); - return -EINVAL; - } - - if (tuple->TupleData[1] != ETH_ALEN) { - dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n"); - return -EINVAL; - } - for (i = 0; i < 6; i++) - dev->dev_addr[i] = tuple->TupleData[i+2]; - return 0; -} - -/** - * pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE - * @p_dev: the struct pcmcia_device for which we want the address. - * @dev: a properly prepared struct net_device to store the info to. - * - * pcmcia_get_mac_from_cis() reads out the hardware MAC address from - * CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which - * must be set up properly by the driver (see examples!). - */ -int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev) -{ - return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev); -} -EXPORT_SYMBOL(pcmcia_get_mac_from_cis); - diff --git a/drivers/pcmcia/pxa2xx_vpac270.c b/drivers/pcmcia/pxa2xx_vpac270.c new file mode 100644 index 00000000000..55627eccee8 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_vpac270.c @@ -0,0 +1,229 @@ +/* + * linux/drivers/pcmcia/pxa2xx_vpac270.c + * + * Driver for Voipac PXA270 PCMCIA and CF sockets + * + * Copyright (C) 2010 + * Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/mach-types.h> + +#include <mach/gpio.h> +#include <mach/vpac270.h> + +#include "soc_common.h" + +static struct pcmcia_irqs cd_irqs[] = { + { + .sock = 0, + .irq = IRQ_GPIO(GPIO84_VPAC270_PCMCIA_CD), + .str = "PCMCIA CD" + }, + { + .sock = 1, + .irq = IRQ_GPIO(GPIO17_VPAC270_CF_CD), + .str = "CF CD" + }, +}; + +static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret; + + if (skt->nr == 0) { + ret = gpio_request(GPIO84_VPAC270_PCMCIA_CD, "PCMCIA CD"); + if (ret) + goto err1; + ret = gpio_direction_input(GPIO84_VPAC270_PCMCIA_CD); + if (ret) + goto err2; + + ret = gpio_request(GPIO35_VPAC270_PCMCIA_RDY, "PCMCIA RDY"); + if (ret) + goto err2; + ret = gpio_direction_input(GPIO35_VPAC270_PCMCIA_RDY); + if (ret) + goto err3; + + ret = gpio_request(GPIO107_VPAC270_PCMCIA_PPEN, "PCMCIA PPEN"); + if (ret) + goto err3; + ret = gpio_direction_output(GPIO107_VPAC270_PCMCIA_PPEN, 0); + if (ret) + goto err4; + + ret = gpio_request(GPIO11_VPAC270_PCMCIA_RESET, "PCMCIA RESET"); + if (ret) + goto err4; + ret = gpio_direction_output(GPIO11_VPAC270_PCMCIA_RESET, 0); + if (ret) + goto err5; + + skt->socket.pci_irq = gpio_to_irq(GPIO35_VPAC270_PCMCIA_RDY); + + return soc_pcmcia_request_irqs(skt, &cd_irqs[0], 1); + +err5: + gpio_free(GPIO11_VPAC270_PCMCIA_RESET); +err4: + gpio_free(GPIO107_VPAC270_PCMCIA_PPEN); +err3: + gpio_free(GPIO35_VPAC270_PCMCIA_RDY); +err2: + gpio_free(GPIO84_VPAC270_PCMCIA_CD); +err1: + return ret; + + } else { + ret = gpio_request(GPIO17_VPAC270_CF_CD, "CF CD"); + if (ret) + goto err6; + ret = gpio_direction_input(GPIO17_VPAC270_CF_CD); + if (ret) + goto err7; + + ret = gpio_request(GPIO12_VPAC270_CF_RDY, "CF RDY"); + if (ret) + goto err7; + ret = gpio_direction_input(GPIO12_VPAC270_CF_RDY); + if (ret) + goto err8; + + ret = gpio_request(GPIO16_VPAC270_CF_RESET, "CF RESET"); + if (ret) + goto err8; + ret = gpio_direction_output(GPIO16_VPAC270_CF_RESET, 0); + if (ret) + goto err9; + + skt->socket.pci_irq = gpio_to_irq(GPIO12_VPAC270_CF_RDY); + + return soc_pcmcia_request_irqs(skt, &cd_irqs[1], 1); + +err9: + gpio_free(GPIO16_VPAC270_CF_RESET); +err8: + gpio_free(GPIO12_VPAC270_CF_RDY); +err7: + gpio_free(GPIO17_VPAC270_CF_CD); +err6: + return ret; + + } +} + +static void vpac270_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + gpio_free(GPIO11_VPAC270_PCMCIA_RESET); + gpio_free(GPIO107_VPAC270_PCMCIA_PPEN); + gpio_free(GPIO35_VPAC270_PCMCIA_RDY); + gpio_free(GPIO84_VPAC270_PCMCIA_CD); + gpio_free(GPIO16_VPAC270_CF_RESET); + gpio_free(GPIO12_VPAC270_CF_RDY); + gpio_free(GPIO17_VPAC270_CF_CD); +} + +static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + if (skt->nr == 0) { + state->detect = !gpio_get_value(GPIO84_VPAC270_PCMCIA_CD); + state->ready = !!gpio_get_value(GPIO35_VPAC270_PCMCIA_RDY); + } else { + state->detect = !gpio_get_value(GPIO17_VPAC270_CF_CD); + state->ready = !!gpio_get_value(GPIO12_VPAC270_CF_RDY); + } + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int +vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + if (skt->nr == 0) { + gpio_set_value(GPIO11_VPAC270_PCMCIA_RESET, + (state->flags & SS_RESET)); + gpio_set_value(GPIO107_VPAC270_PCMCIA_PPEN, + !(state->Vcc == 33 || state->Vcc == 50)); + } else { + gpio_set_value(GPIO16_VPAC270_CF_RESET, + (state->flags & SS_RESET)); + } + + return 0; +} + +static void vpac270_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void vpac270_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level vpac270_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 2, + + .hw_init = vpac270_pcmcia_hw_init, + .hw_shutdown = vpac270_pcmcia_hw_shutdown, + + .socket_state = vpac270_pcmcia_socket_state, + .configure_socket = vpac270_pcmcia_configure_socket, + + .socket_init = vpac270_pcmcia_socket_init, + .socket_suspend = vpac270_pcmcia_socket_suspend, +}; + +static struct platform_device *vpac270_pcmcia_device; + +static int __init vpac270_pcmcia_init(void) +{ + int ret; + + if (!machine_is_vpac270()) + return -ENODEV; + + vpac270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!vpac270_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(vpac270_pcmcia_device, + &vpac270_pcmcia_ops, sizeof(vpac270_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(vpac270_pcmcia_device); + + if (ret) + platform_device_put(vpac270_pcmcia_device); + + return ret; +} + +static void __exit vpac270_pcmcia_exit(void) +{ + platform_device_unregister(vpac270_pcmcia_device); +} + +module_init(vpac270_pcmcia_init); +module_exit(vpac270_pcmcia_exit); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("PCMCIA support for Voipac PXA270"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/rsrc_iodyn.c b/drivers/pcmcia/rsrc_iodyn.c new file mode 100644 index 00000000000..d0bf3502106 --- /dev/null +++ b/drivers/pcmcia/rsrc_iodyn.c @@ -0,0 +1,172 @@ +/* + * rsrc_iodyn.c -- Resource management routines for MEM-static sockets. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/ss.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include "cs_internal.h" + + +struct pcmcia_align_data { + unsigned long mask; + unsigned long offset; +}; + +static resource_size_t pcmcia_align(void *align_data, + const struct resource *res, + resource_size_t size, resource_size_t align) +{ + struct pcmcia_align_data *data = align_data; + resource_size_t start; + + start = (res->start & ~data->mask) + data->offset; + if (start < res->start) + start += data->mask + 1; + +#ifdef CONFIG_X86 + if (res->flags & IORESOURCE_IO) { + if (start & 0x300) + start = (start + 0x3ff) & ~0x3ff; + } +#endif + +#ifdef CONFIG_M68K + if (res->flags & IORESOURCE_IO) { + if ((res->start + size - 1) >= 1024) + start = res->end; + } +#endif + + return start; +} + + +static struct resource *__iodyn_find_io_region(struct pcmcia_socket *s, + unsigned long base, int num, + unsigned long align) +{ + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, + dev_name(&s->dev)); + struct pcmcia_align_data data; + unsigned long min = base; + int ret; + + data.mask = align - 1; + data.offset = base & data.mask; + +#ifdef CONFIG_PCI + if (s->cb_dev) { + ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, + min, 0, pcmcia_align, &data); + } else +#endif + ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, + 1, pcmcia_align, &data); + + if (ret != 0) { + kfree(res); + res = NULL; + } + return res; +} + +static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) +{ + int i, ret = 0; + + /* Check for an already-allocated window that must conflict with + * what was asked for. It is a hack because it does not catch all + * potential conflicts, just the most obvious ones. + */ + for (i = 0; i < MAX_IO_WIN; i++) { + if (!s->io[i].res) + continue; + + if (!*base) + continue; + + if ((s->io[i].res->start & (align-1)) == *base) + return -EBUSY; + } + + for (i = 0; i < MAX_IO_WIN; i++) { + struct resource *res = s->io[i].res; + unsigned int try; + + if (res && (res->flags & IORESOURCE_BITS) != + (attr & IORESOURCE_BITS)) + continue; + + if (!res) { + if (align == 0) + align = 0x10000; + + res = s->io[i].res = __iodyn_find_io_region(s, *base, + num, align); + if (!res) + return -EINVAL; + + *base = res->start; + s->io[i].res->flags = + ((res->flags & ~IORESOURCE_BITS) | + (attr & IORESOURCE_BITS)); + s->io[i].InUse = num; + return 0; + } + + /* Try to extend top of window */ + try = res->end + 1; + if ((*base == 0) || (*base == try)) { + if (adjust_resource(s->io[i].res, res->start, + res->end - res->start + num + 1)) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + + /* Try to extend bottom of window */ + try = res->start - num; + if ((*base == 0) || (*base == try)) { + if (adjust_resource(s->io[i].res, + res->start - num, + res->end - res->start + num + 1)) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + + return -EINVAL; +} + + +struct pccard_resource_ops pccard_iodyn_ops = { + .validate_mem = NULL, + .find_io = iodyn_find_io, + .find_mem = NULL, + .add_io = NULL, + .add_mem = NULL, + .init = static_init, + .exit = NULL, +}; +EXPORT_SYMBOL(pccard_iodyn_ops); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index ffa5f3cae57..142efac3c38 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -22,7 +22,7 @@ #include <pcmcia/cistpl.h> #include "cs_internal.h" -static int static_init(struct pcmcia_socket *s) +int static_init(struct pcmcia_socket *s) { /* the good thing about SS_CAP_STATIC_MAP sockets is * that they don't need a resource database */ @@ -32,118 +32,44 @@ static int static_init(struct pcmcia_socket *s) return 0; } - -struct pccard_resource_ops pccard_static_ops = { - .validate_mem = NULL, - .adjust_io_region = NULL, - .find_io = NULL, - .find_mem = NULL, - .add_io = NULL, - .add_mem = NULL, - .init = static_init, - .exit = NULL, -}; -EXPORT_SYMBOL(pccard_static_ops); - - -#ifdef CONFIG_PCCARD_IODYN - -static struct resource * -make_resource(unsigned long b, unsigned long n, int flags, char *name) +struct resource *pcmcia_make_resource(unsigned long start, unsigned long end, + int flags, const char *name) { struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); if (res) { res->name = name; - res->start = b; - res->end = b + n - 1; + res->start = start; + res->end = start + end - 1; res->flags = flags; } return res; } -struct pcmcia_align_data { - unsigned long mask; - unsigned long offset; -}; - -static resource_size_t pcmcia_align(void *align_data, - const struct resource *res, - resource_size_t size, resource_size_t align) +static int static_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) { - struct pcmcia_align_data *data = align_data; - resource_size_t start; + if (!s->io_offset) + return -EINVAL; + *base = s->io_offset | (*base & 0x0fff); - start = (res->start & ~data->mask) + data->offset; - if (start < res->start) - start += data->mask + 1; - -#ifdef CONFIG_X86 - if (res->flags & IORESOURCE_IO) { - if (start & 0x300) - start = (start + 0x3ff) & ~0x3ff; - } -#endif - -#ifdef CONFIG_M68K - if (res->flags & IORESOURCE_IO) { - if ((res->start + size - 1) >= 1024) - start = res->end; - } -#endif - - return start; -} - - -static int iodyn_adjust_io_region(struct resource *res, unsigned long r_start, - unsigned long r_end, struct pcmcia_socket *s) -{ - return adjust_resource(res, r_start, r_end - r_start + 1); + return 0; } -static struct resource *iodyn_find_io_region(unsigned long base, int num, - unsigned long align, struct pcmcia_socket *s) -{ - struct resource *res = make_resource(0, num, IORESOURCE_IO, - dev_name(&s->dev)); - struct pcmcia_align_data data; - unsigned long min = base; - int ret; - - if (align == 0) - align = 0x10000; - - data.mask = align - 1; - data.offset = base & data.mask; - -#ifdef CONFIG_PCI - if (s->cb_dev) { - ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, - min, 0, pcmcia_align, &data); - } else -#endif - ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, - 1, pcmcia_align, &data); - - if (ret != 0) { - kfree(res); - res = NULL; - } - return res; -} - -struct pccard_resource_ops pccard_iodyn_ops = { +struct pccard_resource_ops pccard_static_ops = { .validate_mem = NULL, - .adjust_io_region = iodyn_adjust_io_region, - .find_io = iodyn_find_io_region, + .find_io = static_find_io, .find_mem = NULL, .add_io = NULL, .add_mem = NULL, .init = static_init, .exit = NULL, }; -EXPORT_SYMBOL(pccard_iodyn_ops); +EXPORT_SYMBOL(pccard_static_ops); + -#endif /* CONFIG_PCCARD_IODYN */ +MODULE_AUTHOR("David A. Hinds, Dominik Brodowski"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("rsrc_nonstatic"); diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index a6eb7b59ba9..dcd1a4ad3d6 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -34,8 +34,10 @@ #include <pcmcia/cistpl.h> #include "cs_internal.h" +/* moved to rsrc_mgr.c MODULE_AUTHOR("David A. Hinds, Dominik Brodowski"); MODULE_LICENSE("GPL"); +*/ /* Parameters that can be set with 'insmod' */ @@ -70,27 +72,13 @@ struct socket_data { ======================================================================*/ static struct resource * -make_resource(resource_size_t b, resource_size_t n, int flags, const char *name) -{ - struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); - - if (res) { - res->name = name; - res->start = b; - res->end = b + n - 1; - res->flags = flags; - } - return res; -} - -static struct resource * claim_region(struct pcmcia_socket *s, resource_size_t base, resource_size_t size, int type, char *name) { struct resource *res, *parent; parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource; - res = make_resource(base, size, type | IORESOURCE_BUSY, name); + res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name); if (res) { #ifdef CONFIG_PCI @@ -661,8 +649,9 @@ pcmcia_align(void *align_data, const struct resource *res, * Adjust an existing IO region allocation, but making sure that we don't * encroach outside the resources which the user supplied. */ -static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start, - unsigned long r_end, struct pcmcia_socket *s) +static int __nonstatic_adjust_io_region(struct pcmcia_socket *s, + unsigned long r_start, + unsigned long r_end) { struct resource_map *m; struct socket_data *s_data = s->resource_data; @@ -675,8 +664,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star if (start > r_start || r_end > end) continue; - ret = adjust_resource(res, r_start, r_end - r_start + 1); - break; + ret = 0; } return ret; @@ -695,18 +683,17 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ======================================================================*/ -static struct resource *nonstatic_find_io_region(unsigned long base, int num, - unsigned long align, struct pcmcia_socket *s) +static struct resource *__nonstatic_find_io_region(struct pcmcia_socket *s, + unsigned long base, int num, + unsigned long align) { - struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev)); + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, + dev_name(&s->dev)); struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min = base; int ret; - if (align == 0) - align = 0x10000; - data.mask = align - 1; data.offset = base & data.mask; data.map = &s_data->io_db; @@ -727,10 +714,97 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, return res; } +static int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) +{ + int i, ret = 0; + + /* Check for an already-allocated window that must conflict with + * what was asked for. It is a hack because it does not catch all + * potential conflicts, just the most obvious ones. + */ + for (i = 0; i < MAX_IO_WIN; i++) { + if (!s->io[i].res) + continue; + + if (!*base) + continue; + + if ((s->io[i].res->start & (align-1)) == *base) + return -EBUSY; + } + + for (i = 0; i < MAX_IO_WIN; i++) { + struct resource *res = s->io[i].res; + unsigned int try; + + if (res && (res->flags & IORESOURCE_BITS) != + (attr & IORESOURCE_BITS)) + continue; + + if (!res) { + if (align == 0) + align = 0x10000; + + res = s->io[i].res = __nonstatic_find_io_region(s, + *base, num, + align); + if (!res) + return -EINVAL; + + *base = res->start; + s->io[i].res->flags = + ((res->flags & ~IORESOURCE_BITS) | + (attr & IORESOURCE_BITS)); + s->io[i].InUse = num; + return 0; + } + + /* Try to extend top of window */ + try = res->end + 1; + if ((*base == 0) || (*base == try)) { + ret = __nonstatic_adjust_io_region(s, res->start, + res->end + num); + if (!ret) { + ret = adjust_resource(s->io[i].res, res->start, + res->end - res->start + num + 1); + if (ret) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + + /* Try to extend bottom of window */ + try = res->start - num; + if ((*base == 0) || (*base == try)) { + ret = __nonstatic_adjust_io_region(s, + res->start - num, + res->end); + if (!ret) { + ret = adjust_resource(s->io[i].res, + res->start - num, + res->end - res->start + num + 1); + if (ret) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + } + + return -EINVAL; +} + + static struct resource *nonstatic_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s) { - struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev)); + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM, + dev_name(&s->dev)); struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min, max; @@ -861,23 +935,42 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s) return -ENODEV; #if defined(CONFIG_X86) - /* If this is the root bus, the risk of hitting - * some strange system devices which aren't protected - * by either ACPI resource tables or properly requested - * resources is too big. Therefore, don't do auto-adding - * of resources at the moment. + /* If this is the root bus, the risk of hitting some strange + * system devices is too high: If a driver isn't loaded, the + * resources are not claimed; even if a driver is loaded, it + * may not request all resources or even the wrong one. We + * can neither trust the rest of the kernel nor ACPI/PNP and + * CRS parsing to get it right. Therefore, use several + * safeguards: + * + * - Do not auto-add resources if the CardBus bridge is on + * the PCI root bus + * + * - Avoid any I/O ports < 0x100. + * + * - On PCI-PCI bridges, only use resources which are set up + * exclusively for the secondary PCI bus: the risk of hitting + * system devices is quite low, as they usually aren't + * connected to the secondary PCI bus. */ if (s->cb_dev->bus->number == 0) return -EINVAL; -#endif + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { + res = s->cb_dev->bus->resource[i]; +#else pci_bus_for_each_resource(s->cb_dev->bus, res, i) { +#endif if (!res) continue; if (res->flags & IORESOURCE_IO) { + /* safeguard against the root resource, where the + * risk of hitting any other device would be too + * high */ if (res == &ioport_resource) continue; + dev_printk(KERN_INFO, &s->cb_dev->dev, "pcmcia: parent PCI bridge window: %pR\n", res); @@ -887,8 +980,12 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s) } if (res->flags & IORESOURCE_MEM) { + /* safeguard against the root resource, where the + * risk of hitting any other device would be too + * high */ if (res == &iomem_resource) continue; + dev_printk(KERN_INFO, &s->cb_dev->dev, "pcmcia: parent PCI bridge window: %pR\n", res); @@ -956,8 +1053,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) struct pccard_resource_ops pccard_nonstatic_ops = { .validate_mem = pcmcia_nonstatic_validate_mem, - .adjust_io_region = nonstatic_adjust_io_region, - .find_io = nonstatic_find_io_region, + .find_io = nonstatic_find_io, .find_mem = nonstatic_find_mem_region, .add_io = adjust_io, .add_mem = adjust_memory, diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 83ace277426..424e576f3ac 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1303,13 +1303,6 @@ static int yenta_dev_suspend_noirq(struct device *dev) pci_read_config_dword(pdev, 17*4, &socket->saved_state[1]); pci_disable_device(pdev); - /* - * Some laptops (IBM T22) do not like us putting the Cardbus - * bridge into D3. At a guess, some other laptop will - * probably require this, so leave it commented out for now. - */ - /* pci_set_power_state(dev, 3); */ - return 0; } |