diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-04-12 02:24:49 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-04-12 02:24:49 -0700 |
| commit | f2d816ef30bba85a850166fa3a6404a544f8849f (patch) | |
| tree | 802878a5523d4aed09360034e8cf2c5863d6dba8 | |
| parent | 5566c10d86a29929678d626398a59298e90b7a52 (diff) | |
| parent | 7347d800b0ec77f34ae1069adc93312a15ab549e (diff) | |
Merge bk://bk.arm.linux.org.uk/linux-2.6-pcmcia
into ppc970.osdl.org:/home/torvalds/v2.6/linux
| -rw-r--r-- | drivers/pci/bus.c | 1 | ||||
| -rw-r--r-- | drivers/pcmcia/cs.c | 3 | ||||
| -rw-r--r-- | drivers/pcmcia/cs_internal.h | 2 | ||||
| -rw-r--r-- | drivers/pcmcia/rsrc_mgr.c | 288 | ||||
| -rw-r--r-- | include/linux/pci.h | 6 |
5 files changed, 176 insertions, 124 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 969ec4237fb2..e94a854c40f1 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -135,5 +135,6 @@ void pci_enable_bridges(struct pci_bus *bus) } } +EXPORT_SYMBOL(pci_bus_alloc_resource); EXPORT_SYMBOL(pci_bus_add_devices); EXPORT_SYMBOL(pci_enable_bridges); diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index fed090991090..881561fd7fa9 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -2151,9 +2151,8 @@ static int __init init_pcmcia_cs(void) { printk(KERN_INFO "%s\n", release); printk(KERN_INFO " %s\n", options); - class_register(&pcmcia_socket_class); - return 0; + return class_register(&pcmcia_socket_class); } static void __exit exit_pcmcia_cs(void) diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 655b1704b3f2..3bc76aeef298 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -181,7 +181,7 @@ int copy_memory(memory_handle_t handle, copy_op_t *req); /* In rsrc_mgr */ void validate_mem(struct pcmcia_socket *s); -int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, +int find_io_region(ioaddr_t *base, ioaddr_t num, unsigned long align, char *name, struct pcmcia_socket *s); int find_mem_region(u_long *base, u_long num, u_long align, int low, char *name, struct pcmcia_socket *s); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index b9cb632d8084..892e45110f19 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -107,44 +107,8 @@ static irq_info_t irq_table[NR_IRQS]; ======================================================================*/ -static struct resource *resource_parent(unsigned long b, unsigned long n, - int flags, struct pci_dev *dev) -{ -#ifdef CONFIG_PCI - struct resource res, *pr; - - if (dev != NULL) { - res.start = b; - res.end = b + n - 1; - res.flags = flags; - pr = pci_find_parent_resource(dev, &res); - if (pr) - return pr; - } -#endif /* CONFIG_PCI */ - if (flags & IORESOURCE_MEM) - return &iomem_resource; - return &ioport_resource; -} - -/* FIXME: Fundamentally racy. */ -static inline int check_io_resource(unsigned long b, unsigned long n, - struct pci_dev *dev) -{ - struct resource *region; - - region = __request_region(resource_parent(b, n, IORESOURCE_IO, dev), - b, n, "check_io_resource"); - if (!region) - return -EBUSY; - - release_resource(region); - kfree(region); - return 0; -} - -static struct resource *make_resource(unsigned long b, unsigned long n, - int flags, char *name) +static struct resource * +make_resource(unsigned long b, unsigned long n, int flags, char *name) { struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL); @@ -158,34 +122,34 @@ static struct resource *make_resource(unsigned long b, unsigned long n, return res; } -static int request_io_resource(unsigned long b, unsigned long n, - char *name, struct pci_dev *dev) +static struct resource * +claim_region(struct pcmcia_socket *s, unsigned long base, unsigned long size, + int type, char *name) { - struct resource *res = make_resource(b, n, IORESOURCE_IO, name); - struct resource *pr = resource_parent(b, n, IORESOURCE_IO, dev); - int err = -ENOMEM; + struct resource *res, *parent; + + parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource; + res = make_resource(base, size, type | IORESOURCE_BUSY, name); if (res) { - err = request_resource(pr, res); - if (err) +#ifdef CONFIG_PCI + if (s && s->cb_dev) + parent = pci_find_parent_resource(s->cb_dev, res); +#endif + if (!parent || request_resource(parent, res)) { kfree(res); + res = NULL; + } } - return err; + return res; } -static int request_mem_resource(unsigned long b, unsigned long n, - char *name, struct pci_dev *dev) +static void free_region(struct resource *res) { - struct resource *res = make_resource(b, n, IORESOURCE_MEM, name); - struct resource *pr = resource_parent(b, n, IORESOURCE_MEM, dev); - int err = -ENOMEM; - if (res) { - err = request_resource(pr, res); - if (err) - kfree(res); + release_resource(res); + kfree(res); } - return err; } /*====================================================================== @@ -261,7 +225,7 @@ static int sub_interval(resource_map_t *map, u_long base, u_long num) #ifdef CONFIG_PCMCIA_PROBE static void do_io_probe(ioaddr_t base, ioaddr_t num) { - + struct resource *res; ioaddr_t i, j, bad, any; u_char *b, hole, most; @@ -276,11 +240,13 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num) } memset(b, 0, 256); for (i = base, most = 0; i < base+num; i += 8) { - if (check_io_resource(i, 8, NULL)) + res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe"); + if (!res) continue; hole = inb(i); for (j = 1; j < 8; j++) if (inb(i+j) != hole) break; + free_region(res); if ((j == 8) && (++b[hole] > b[most])) most = hole; if (b[most] == 127) break; @@ -289,10 +255,12 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num) bad = any = 0; for (i = base; i < base+num; i += 8) { - if (check_io_resource(i, 8, NULL)) + res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe"); + if (!res) continue; for (j = 0; j < 8; j++) if (inb(i+j) != most) break; + free_region(res); if (j < 8) { if (!any) printk(" excluding"); @@ -389,18 +357,16 @@ cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size) cisinfo_t info1, info2; int ret = 0; - res1 = request_mem_region(base, size/2, "cs memory probe"); - res2 = request_mem_region(base + size/2, size/2, "cs memory probe"); + res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe"); + res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe"); if (res1 && res2) { ret = readable(s, res1, &info1); ret += readable(s, res2, &info2); } - if (res2) - release_resource(res2); - if (res1) - release_resource(res1); + free_region(res2); + free_region(res1); return (ret == 2) && (info1.Chains == info2.Chains); } @@ -411,18 +377,16 @@ checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size) struct resource *res1, *res2; int a = -1, b = -1; - res1 = request_mem_region(base, size/2, "cs memory probe"); - res2 = request_mem_region(base + size/2, size/2, "cs memory probe"); + res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe"); + res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe"); if (res1 && res2) { a = checksum(s, res1); b = checksum(s, res2); } - if (res2) - release_resource(res2); - if (res1) - release_resource(res1); + free_region(res2); + free_region(res1); return (a == b) && (a >= 0); } @@ -553,6 +517,68 @@ void validate_mem(struct pcmcia_socket *s) #endif /* CONFIG_PCMCIA_PROBE */ +struct pcmcia_align_data { + unsigned long mask; + unsigned long offset; + resource_map_t *map; +}; + +static void +pcmcia_common_align(void *align_data, struct resource *res, + unsigned long size, unsigned long align) +{ + struct pcmcia_align_data *data = align_data; + unsigned long start; + /* + * Ensure that we have the correct start address + */ + start = (res->start & ~data->mask) + data->offset; + if (start < res->start) + start += data->mask + 1; + res->start = start; +} + +static void +pcmcia_align(void *align_data, struct resource *res, + unsigned long size, unsigned long align) +{ + struct pcmcia_align_data *data = align_data; + resource_map_t *m; + + pcmcia_common_align(data, res, size, align); + + for (m = data->map->next; m != data->map; m = m->next) { + unsigned long start = m->base; + unsigned long end = m->base + m->num; + + /* + * If the lower resources are not available, try aligning + * to this entry of the resource database to see if it'll + * fit here. + */ + if (res->start < start) { + res->start = start; + pcmcia_common_align(data, res, size, align); + } + + /* + * If we're above the area which was passed in, there's + * no point proceeding. + */ + if (res->start >= res->end) + break; + + if ((res->start + size) <= end) + break; + } + + /* + * If we failed to find something suitable, ensure we fail. + */ + if (m == data->map) + res->start = res->end; +} + /*====================================================================== These find ranges of I/O ports or memory addresses that are not @@ -566,69 +592,89 @@ void validate_mem(struct pcmcia_socket *s) ======================================================================*/ -int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, +int find_io_region(ioaddr_t *base, ioaddr_t num, unsigned long align, char *name, struct pcmcia_socket *s) { - ioaddr_t try; - resource_map_t *m; - int ret = -1; + struct resource *res = make_resource(0, num, IORESOURCE_IO, name); + struct pcmcia_align_data data; + unsigned long min = *base; + int ret; - down(&rsrc_sem); - for (m = io_db.next; m != &io_db; m = m->next) { - try = (m->base & ~(align-1)) + *base; - for (try = (try >= m->base) ? try : try+align; - (try >= m->base) && (try+num <= m->base+m->num); - try += align) { - if (request_io_resource(try, num, name, s->cb_dev) == 0) { - *base = try; - ret = 0; - goto out; - } - if (!align) - break; + if (align == 0) + align = 0x10000UL; + + data.mask = align - 1; + data.offset = *base & data.mask; + data.map = &io_db; + +#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 + { + down(&rsrc_sem); + ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 0, + pcmcia_align, &data); + up(&rsrc_sem); } - } - out: - up(&rsrc_sem); - return ret; + + if (ret != 0) { + kfree(res); + } else { + *base = res->start; + } + return ret; } int find_mem_region(u_long *base, u_long num, u_long align, int low, char *name, struct pcmcia_socket *s) { - u_long try; - resource_map_t *m; - int ret = -1; + struct resource *res = make_resource(0, num, IORESOURCE_MEM, name); + struct pcmcia_align_data data; + unsigned long min, max; + int ret, i; - low = low || !(s->features & SS_CAP_PAGE_REGS); + low = low || !(s->features & SS_CAP_PAGE_REGS); - down(&rsrc_sem); - while (1) { - for (m = mem_db.next; m != &mem_db; m = m->next) { - /* first pass >1MB, second pass <1MB */ - if ((low != 0) ^ (m->base < 0x100000)) - continue; - - try = (m->base & ~(align-1)) + *base; - for (try = (try >= m->base) ? try : try+align; - (try >= m->base) && (try+num <= m->base+m->num); - try += align) { - if (request_mem_resource(try, num, name, s->cb_dev) == 0) { - *base = try; - ret = 0; - goto out; + data.mask = align - 1; + data.offset = *base & data.mask; + data.map = &mem_db; + + for (i = 0; i < 2; i++) { + if (low) { + max = 0x100000UL; + min = *base < max ? *base : 0; + } else { + max = ~0UL; + min = 0x100000UL + *base; } - if (!align) - break; - } + +#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 + { + down(&rsrc_sem); + ret = allocate_resource(&iomem_resource, res, num, min, + max, 0, pcmcia_align, &data); + up(&rsrc_sem); + } + if (ret == 0 || low) + break; + low = 1; } - if (low) - break; - low++; - } - out: - up(&rsrc_sem); - return ret; + + if (ret != 0) { + kfree(res); + } else { + *base = res->start; + } + return ret; } /*====================================================================== diff --git a/include/linux/pci.h b/include/linux/pci.h index 3f2b4949a65e..a6ba538f2d14 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -676,6 +676,12 @@ int pci_request_region(struct pci_dev *, int, char *); void pci_release_region(struct pci_dev *, int); /* drivers/pci/bus.c */ +int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, + unsigned long size, unsigned long align, + unsigned long min, unsigned int type_mask, + void (*alignf)(void *, struct resource *, + unsigned long, unsigned long), + void *alignf_data); void pci_enable_bridges(struct pci_bus *bus); /* New-style probing supporting hot-pluggable devices */ |
