diff options
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r-- | drivers/pcmcia/Kconfig | 12 | ||||
-rw-r--r-- | drivers/pcmcia/Makefile | 1 | ||||
-rw-r--r-- | drivers/pcmcia/cistpl.c | 31 | ||||
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 6 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 3 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_mgr.c | 5 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_pci.c | 173 |
7 files changed, 217 insertions, 14 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 910e90bf16c6..8843a678f200 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -69,7 +69,8 @@ config YENTA tristate "CardBus yenta-compatible bridge support" depends on PCI select CARDBUS if !EXPERT - select PCCARD_NONSTATIC if PCMCIA != n + select PCCARD_NONSTATIC if PCMCIA != n && ISA + select PCCARD_PCI if PCMCIA !=n && !ISA ---help--- This option enables support for CardBus host bridges. Virtually all modern PCMCIA bridges are CardBus compatible. A "bridge" is @@ -109,7 +110,8 @@ config YENTA_TOSHIBA config PD6729 tristate "Cirrus PD6729 compatible bridge support" depends on PCMCIA && PCI - select PCCARD_NONSTATIC + select PCCARD_NONSTATIC if PCMCIA != n && ISA + select PCCARD_PCI if PCMCIA !=n && !ISA help This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge device, found in some older laptops and PCMCIA card readers. @@ -117,7 +119,8 @@ config PD6729 config I82092 tristate "i82092 compatible bridge support" depends on PCMCIA && PCI - select PCCARD_NONSTATIC + select PCCARD_NONSTATIC if PCMCIA != n && ISA + select PCCARD_PCI if PCMCIA !=n && !ISA help This provides support for the Intel I82092AA PCI-to-PCMCIA bridge device, found in some older laptops and more commonly in evaluation boards for the @@ -287,6 +290,9 @@ config ELECTRA_CF Say Y here to support the CompactFlash controller on the PA Semi Electra eval board. +config PCCARD_PCI + bool + config PCCARD_NONSTATIC bool diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 27e94b30cf96..f1a7ca04d89e 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia.o pcmcia_rsrc-y += rsrc_mgr.o pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o +pcmcia_rsrc-$(CONFIG_PCCARD_PCI) += rsrc_pci.o obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 884a984216fe..64d0515b76bd 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -168,9 +168,12 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, } else { u_int inc = 1, card_offset, flags; - if (addr > CISTPL_MAX_CIS_SIZE) + if (addr > CISTPL_MAX_CIS_SIZE) { dev_dbg(&s->dev, "attempt to read CIS mem at addr %#x", addr); + memset(ptr, 0xff, len); + return -1; + } flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); if (attr) { @@ -1383,7 +1386,7 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) if (!s) return -EINVAL; - if (s->functions) { + if (s->functions || !(s->state & SOCKET_PRESENT)) { WARN_ON(1); return -EINVAL; } @@ -1448,10 +1451,26 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) done: /* invalidate CIS cache on failure */ if (!dev_ok || !ident_ok || !count) { - mutex_lock(&s->ops_mutex); - destroy_cis_cache(s); - mutex_unlock(&s->ops_mutex); - ret = -EIO; +#if defined(CONFIG_MTD_PCMCIA_ANONYMOUS) + /* Set up as an anonymous card. If we don't have anonymous + memory support then just error the card as there is no + point trying to second guess. + + Note: some cards have just a device entry, it may be + worth extending support to cover these in future */ + if (!dev_ok || !ident_ok) { + dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n"); + pcmcia_replace_cis(s, "\xFF", 1); + count = 1; + ret = 0; + } else +#endif + { + mutex_lock(&s->ops_mutex); + destroy_cis_cache(s); + mutex_unlock(&s->ops_mutex); + ret = -EIO; + } } if (info) diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 7f1953f78b12..e86cd6b31773 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -80,9 +80,9 @@ 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); +extern struct resource *pcmcia_make_resource(resource_size_t start, + resource_size_t end, + unsigned long flags, const char *name); /* * Stuff internal to module "pcmcia_core": diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 757119b87146..d3baf0bfca9f 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -667,6 +667,9 @@ static void pcmcia_requery(struct pcmcia_socket *s) { int has_pfc; + if (!(s->state & SOCKET_PRESENT)) + return; + if (s->functions == 0) { pcmcia_card_add(s); return; diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index aa628ed0e9f4..df2cb70aef5b 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -30,8 +30,9 @@ int static_init(struct pcmcia_socket *s) return 0; } -struct resource *pcmcia_make_resource(unsigned long start, unsigned long end, - int flags, const char *name) +struct resource *pcmcia_make_resource(resource_size_t start, + resource_size_t end, + unsigned long flags, const char *name) { struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); diff --git a/drivers/pcmcia/rsrc_pci.c b/drivers/pcmcia/rsrc_pci.c new file mode 100644 index 000000000000..1f67b3ba70fb --- /dev/null +++ b/drivers/pcmcia/rsrc_pci.c @@ -0,0 +1,173 @@ +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> + +#include <pcmcia/ss.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; + return start; +} + +static struct resource *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; + int ret; + + data.mask = align - 1; + data.offset = base & data.mask; + + ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, + base, 0, pcmcia_align, &data); + if (ret != 0) { + kfree(res); + res = NULL; + } + return res; +} + +static int res_pci_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align, struct resource **parent) +{ + 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 = 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; + *parent = res; + return 0; + } + + /* Try to extend top of window */ + try = res->end + 1; + if ((*base == 0) || (*base == try)) { + ret = adjust_resource(s->io[i].res, res->start, + resource_size(res) + num); + if (ret) + continue; + *base = try; + s->io[i].InUse += num; + *parent = res; + return 0; + } + + /* Try to extend bottom of window */ + try = res->start - num; + if ((*base == 0) || (*base == try)) { + ret = adjust_resource(s->io[i].res, + res->start - num, + resource_size(res) + num); + if (ret) + continue; + *base = try; + s->io[i].InUse += num; + *parent = res; + return 0; + } + } + return -EINVAL; +} + +static struct resource *res_pci_find_mem(u_long base, u_long num, + u_long align, int low, struct pcmcia_socket *s) +{ + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM, + dev_name(&s->dev)); + struct pcmcia_align_data data; + unsigned long min; + int ret; + + if (align < 0x20000) + align = 0x20000; + data.mask = align - 1; + data.offset = base & data.mask; + + min = 0; + if (!low) + min = 0x100000UL; + + ret = pci_bus_alloc_resource(s->cb_dev->bus, + res, num, 1, min, 0, + pcmcia_align, &data); + + if (ret != 0) { + kfree(res); + res = NULL; + } + return res; +} + + +static int res_pci_init(struct pcmcia_socket *s) +{ + if (!s->cb_dev || !(s->features & SS_CAP_PAGE_REGS)) { + dev_err(&s->dev, "not supported by res_pci\n"); + return -EOPNOTSUPP; + } + return 0; +} + +struct pccard_resource_ops pccard_nonstatic_ops = { + .validate_mem = NULL, + .find_io = res_pci_find_io, + .find_mem = res_pci_find_mem, + .init = res_pci_init, + .exit = NULL, +}; +EXPORT_SYMBOL(pccard_nonstatic_ops); |