X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpcmcia%2Frsrc_nonstatic.c;h=c3176b16b7bea5223aba2031251196cd88440533;hb=refs%2Fheads%2Fvserver;hp=5876bab7c14c80354515e9c35e73135f33701e49;hpb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;p=linux-2.6.git diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 5876bab7c..c3176b16b 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -12,7 +12,6 @@ * (C) 1999 David A. Hinds */ -#include #include #include #include @@ -61,7 +60,7 @@ struct socket_data { unsigned int rsrc_mem_probe; }; -static DECLARE_MUTEX(rsrc_sem); +static DEFINE_MUTEX(rsrc_mutex); #define MEM_PROBE_LOW (1 << 0) #define MEM_PROBE_HIGH (1 << 1) @@ -73,12 +72,11 @@ static DECLARE_MUTEX(rsrc_sem); ======================================================================*/ static struct resource * -make_resource(unsigned long b, unsigned long n, int flags, char *name) +make_resource(resource_size_t b, resource_size_t n, int flags, char *name) { - struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL); + struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); if (res) { - memset(res, 0, sizeof(*res)); res->name = name; res->start = b; res->end = b + n - 1; @@ -88,8 +86,8 @@ make_resource(unsigned long b, unsigned long n, int flags, char *name) } static struct resource * -claim_region(struct pcmcia_socket *s, unsigned long base, unsigned long size, - int type, char *name) +claim_region(struct pcmcia_socket *s, resource_size_t base, + resource_size_t size, int type, char *name) { struct resource *res, *parent; @@ -200,12 +198,11 @@ static void do_io_probe(struct pcmcia_socket *s, kio_addr_t base, kio_addr_t num base, base+num-1); /* First, what does a floating port look like? */ - b = kmalloc(256, GFP_KERNEL); + b = kzalloc(256, GFP_KERNEL); if (!b) { printk(KERN_ERR "do_io_probe: unable to kmalloc 256 bytes"); return; } - memset(b, 0, 256); for (i = base, most = 0; i < base+num; i += 8) { res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe"); if (!res) @@ -372,6 +369,9 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) base, base+num-1); bad = fail = 0; step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); + /* don't allow too large steps */ + if (step > 0x800000) + step = 0x800000; /* cis_readable wants to map 2x map_size */ if (step < 2 * s->map_size) step = 2 * s->map_size; @@ -404,99 +404,111 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s) { - struct socket_data *s_data = s->resource_data; - u_long ok; - if (m == &s_data->mem_db) - return 0; - ok = inv_probe(m->next, s); - if (ok) { - if (m->base >= 0x100000) - sub_interval(&s_data->mem_db, m->base, m->num); - return ok; - } - if (m->base < 0x100000) - return 0; - return do_mem_probe(m->base, m->num, s); + struct socket_data *s_data = s->resource_data; + u_long ok; + if (m == &s_data->mem_db) + return 0; + ok = inv_probe(m->next, s); + if (ok) { + if (m->base >= 0x100000) + sub_interval(&s_data->mem_db, m->base, m->num); + return ok; + } + if (m->base < 0x100000) + return 0; + return do_mem_probe(m->base, m->num, s); } -static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) +static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) { - struct resource_map *m, mm; - static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 }; - u_long b, i, ok = 0; - struct socket_data *s_data = s->resource_data; + struct resource_map *m, mm; + static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 }; + unsigned long b, i, ok = 0; + struct socket_data *s_data = s->resource_data; - /* We do up to four passes through the list */ - if (probe_mask & MEM_PROBE_HIGH) { - if (inv_probe(s_data->mem_db.next, s) > 0) - return; - printk(KERN_NOTICE "cs: warning: no high memory space " - "available!\n"); - } - if ((probe_mask & MEM_PROBE_LOW) == 0) - return; - for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { - mm = *m; - /* Only probe < 1 MB */ - if (mm.base >= 0x100000) continue; - if ((mm.base | mm.num) & 0xffff) { - ok += do_mem_probe(mm.base, mm.num, s); - continue; + /* We do up to four passes through the list */ + if (probe_mask & MEM_PROBE_HIGH) { + if (inv_probe(s_data->mem_db.next, s) > 0) + return 0; + printk(KERN_NOTICE "cs: warning: no high memory space " + "available!\n"); + return -ENODEV; } - /* Special probe for 64K-aligned block */ - for (i = 0; i < 4; i++) { - b = order[i] << 12; - if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) { - if (ok >= mem_limit) - sub_interval(&s_data->mem_db, b, 0x10000); - else - ok += do_mem_probe(b, 0x10000, s); - } + + for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { + mm = *m; + /* Only probe < 1 MB */ + if (mm.base >= 0x100000) + continue; + if ((mm.base | mm.num) & 0xffff) { + ok += do_mem_probe(mm.base, mm.num, s); + continue; + } + /* Special probe for 64K-aligned block */ + for (i = 0; i < 4; i++) { + b = order[i] << 12; + if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) { + if (ok >= mem_limit) + sub_interval(&s_data->mem_db, b, 0x10000); + else + ok += do_mem_probe(b, 0x10000, s); + } + } } - } + + if (ok > 0) + return 0; + + return -ENODEV; } #else /* CONFIG_PCMCIA_PROBE */ -static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) +static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) { struct resource_map *m, mm; struct socket_data *s_data = s->resource_data; + unsigned long ok = 0; for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { mm = *m; - if (do_mem_probe(mm.base, mm.num, s)) - break; + ok += do_mem_probe(mm.base, mm.num, s); } + if (ok > 0) + return 0; + return -ENODEV; } #endif /* CONFIG_PCMCIA_PROBE */ /* - * Locking note: Must be called with skt_sem held! + * Locking note: Must be called with skt_mutex held! */ -static void pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) +static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) { struct socket_data *s_data = s->resource_data; - if (probe_mem) { - unsigned int probe_mask; + unsigned int probe_mask = MEM_PROBE_LOW; + int ret = 0; + + if (!probe_mem) + return 0; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); - probe_mask = MEM_PROBE_LOW; - if (s->features & SS_CAP_PAGE_REGS) - probe_mask = MEM_PROBE_HIGH; + if (s->features & SS_CAP_PAGE_REGS) + probe_mask = MEM_PROBE_HIGH; - if (probe_mask & ~s_data->rsrc_mem_probe) { + if (probe_mask & ~s_data->rsrc_mem_probe) { + if (s->state & SOCKET_PRESENT) + ret = validate_mem(s, probe_mask); + if (!ret) s_data->rsrc_mem_probe |= probe_mask; + } - if (s->state & SOCKET_PRESENT) - validate_mem(s, probe_mask); - } + mutex_unlock(&rsrc_mutex); - up(&rsrc_sem); - } + return ret; } struct pcmcia_align_data { @@ -507,10 +519,10 @@ struct pcmcia_align_data { static void pcmcia_common_align(void *align_data, struct resource *res, - unsigned long size, unsigned long align) + resource_size_t size, resource_size_t align) { struct pcmcia_align_data *data = align_data; - unsigned long start; + resource_size_t start; /* * Ensure that we have the correct start address */ @@ -521,8 +533,8 @@ pcmcia_common_align(void *align_data, struct resource *res, } static void -pcmcia_align(void *align_data, struct resource *res, - unsigned long size, unsigned long align) +pcmcia_align(void *align_data, struct resource *res, resource_size_t size, + resource_size_t align) { struct pcmcia_align_data *data = align_data; struct resource_map *m; @@ -572,7 +584,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star struct socket_data *s_data = s->resource_data; int ret = -ENOMEM; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) { unsigned long start = m->base; unsigned long end = m->base + m->num - 1; @@ -583,7 +595,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ret = adjust_resource(res, r_start, r_end - r_start + 1); break; } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return ret; } @@ -601,7 +613,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ======================================================================*/ -struct resource *nonstatic_find_io_region(unsigned long base, int num, +static struct resource *nonstatic_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s) { struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.class_id); @@ -617,7 +629,7 @@ struct resource *nonstatic_find_io_region(unsigned long base, int num, data.offset = base & data.mask; data.map = &s_data->io_db; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); #ifdef CONFIG_PCI if (s->cb_dev) { ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, @@ -626,7 +638,7 @@ struct resource *nonstatic_find_io_region(unsigned long base, int num, #endif ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 1, pcmcia_align, &data); - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); if (ret != 0) { kfree(res); @@ -635,8 +647,8 @@ struct resource *nonstatic_find_io_region(unsigned long base, int num, return res; } -struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long align, - int low, struct pcmcia_socket *s) +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, s->dev.class_id); struct socket_data *s_data = s->resource_data; @@ -659,7 +671,7 @@ struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long alig min = 0x100000UL + base; } - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); #ifdef CONFIG_PCI if (s->cb_dev) { ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, @@ -669,7 +681,7 @@ struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long alig #endif ret = allocate_resource(&iomem_resource, res, num, min, max, 1, pcmcia_align, &data); - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); if (ret == 0 || low) break; low = 1; @@ -683,27 +695,23 @@ struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long alig } -static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) +static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) { - u_long base, num; struct socket_data *data = s->resource_data; - int ret; - - base = adj->resource.memory.Base; - num = adj->resource.memory.Size; - if ((num == 0) || (base+num-1 < base)) - return CS_BAD_SIZE; + unsigned long size = end - start + 1; + int ret = 0; - ret = CS_SUCCESS; + if (end < start) + return -EINVAL; - down(&rsrc_sem); - switch (adj->Action) { + mutex_lock(&rsrc_mutex); + switch (action) { case ADD_MANAGED_RESOURCE: - ret = add_interval(&data->mem_db, base, num); + ret = add_interval(&data->mem_db, start, size); break; case REMOVE_MANAGED_RESOURCE: - ret = sub_interval(&data->mem_db, base, num); - if (ret == CS_SUCCESS) { + ret = sub_interval(&data->mem_db, start, size); + if (!ret) { struct pcmcia_socket *socket; down_read(&pcmcia_socket_list_rwsem); list_for_each_entry(socket, &pcmcia_socket_list, socket_list) @@ -712,47 +720,46 @@ static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) } break; default: - ret = CS_UNSUPPORTED_FUNCTION; + ret = -EINVAL; } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return ret; } -static int adjust_io(struct pcmcia_socket *s, adjust_t *adj) +static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) { struct socket_data *data = s->resource_data; - kio_addr_t base, num; - int ret = CS_SUCCESS; - - base = adj->resource.io.BasePort; - num = adj->resource.io.NumPorts; - if ((base < 0) || (base > 0xffff)) - return CS_BAD_BASE; - if ((num <= 0) || (base+num > 0x10000) || (base+num <= base)) - return CS_BAD_SIZE; - - down(&rsrc_sem); - switch (adj->Action) { + unsigned long size = end - start + 1; + int ret = 0; + + if (end < start) + return -EINVAL; + + if (end > IO_SPACE_LIMIT) + return -EINVAL; + + mutex_lock(&rsrc_mutex); + switch (action) { case ADD_MANAGED_RESOURCE: - if (add_interval(&data->io_db, base, num) != 0) { - ret = CS_IN_USE; + if (add_interval(&data->io_db, start, size) != 0) { + ret = -EBUSY; break; } #ifdef CONFIG_PCMCIA_PROBE if (probe_io) - do_io_probe(s, base, num); + do_io_probe(s, start, size); #endif break; case REMOVE_MANAGED_RESOURCE: - sub_interval(&data->io_db, base, num); + sub_interval(&data->io_db, start, size); break; default: - ret = CS_UNSUPPORTED_FUNCTION; + ret = -EINVAL; break; } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return ret; } @@ -760,29 +767,101 @@ static int adjust_io(struct pcmcia_socket *s, adjust_t *adj) static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj) { + unsigned long end; + switch (adj->Resource) { case RES_MEMORY_RANGE: - return adjust_memory(s, adj); + end = adj->resource.memory.Base + adj->resource.memory.Size - 1; + return adjust_memory(s, adj->Action, adj->resource.memory.Base, end); case RES_IO_RANGE: - return adjust_io(s, adj); + end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1; + return adjust_io(s, adj->Action, adj->resource.io.BasePort, end); } return CS_UNSUPPORTED_FUNCTION; } +#ifdef CONFIG_PCI +static int nonstatic_autoadd_resources(struct pcmcia_socket *s) +{ + struct resource *res; + int i, done = 0; + + if (!s->cb_dev || !s->cb_dev->bus) + 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 (s->cb_dev->bus->number == 0) + return -EINVAL; +#endif + + for (i=0; i < PCI_BUS_NUM_RESOURCES; i++) { + res = s->cb_dev->bus->resource[i]; + if (!res) + continue; + + if (res->flags & IORESOURCE_IO) { + if (res == &ioport_resource) + continue; + printk(KERN_INFO "pcmcia: parent PCI bridge I/O " + "window: 0x%llx - 0x%llx\n", + (unsigned long long)res->start, + (unsigned long long)res->end); + if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end)) + done |= IORESOURCE_IO; + + } + + if (res->flags & IORESOURCE_MEM) { + if (res == &iomem_resource) + continue; + printk(KERN_INFO "pcmcia: parent PCI bridge Memory " + "window: 0x%llx - 0x%llx\n", + (unsigned long long)res->start, + (unsigned long long)res->end); + if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end)) + done |= IORESOURCE_MEM; + } + } + + /* if we got at least one of IO, and one of MEM, we can be glad and + * activate the PCMCIA subsystem */ + if (done == (IORESOURCE_MEM | IORESOURCE_IO)) + s->resource_setup_done = 1; + + return 0; +} + +#else + +static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s) +{ + return -ENODEV; +} + +#endif + + static int nonstatic_init(struct pcmcia_socket *s) { struct socket_data *data; - data = kmalloc(sizeof(struct socket_data), GFP_KERNEL); + data = kzalloc(sizeof(struct socket_data), GFP_KERNEL); if (!data) return -ENOMEM; - memset(data, 0, sizeof(struct socket_data)); data->mem_db.next = &data->mem_db; data->io_db.next = &data->io_db; s->resource_data = (void *) data; + nonstatic_autoadd_resources(s); + return 0; } @@ -791,7 +870,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) struct socket_data *data = s->resource_data; struct resource_map *p, *q; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); for (p = data->mem_db.next; p != &data->mem_db; p = q) { q = p->next; kfree(p); @@ -800,7 +879,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) q = p->next; kfree(p); } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); } @@ -825,7 +904,7 @@ static ssize_t show_io_db(struct class_device *class_dev, char *buf) struct resource_map *p; ssize_t ret = 0; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); data = s->resource_data; for (p = data->io_db.next; p != &data->io_db; p = p->next) { @@ -837,7 +916,7 @@ static ssize_t show_io_db(struct class_device *class_dev, char *buf) ((unsigned long) p->base + p->num - 1)); } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return (ret); } @@ -845,30 +924,26 @@ static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size { struct pcmcia_socket *s = class_get_devdata(class_dev); unsigned long start_addr, end_addr; - unsigned int add = 1; - adjust_t adj; + unsigned int add = ADD_MANAGED_RESOURCE; ssize_t ret = 0; ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); if (ret != 2) { ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); - add = 0; + add = REMOVE_MANAGED_RESOURCE; if (ret != 2) { ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); - add = 1; + add = ADD_MANAGED_RESOURCE; if (ret != 2) return -EINVAL; } } - if (end_addr <= start_addr) + if (end_addr < start_addr) return -EINVAL; - adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE; - adj.Resource = RES_IO_RANGE; - adj.resource.io.BasePort = start_addr; - adj.resource.io.NumPorts = end_addr - start_addr + 1; - - ret = adjust_io(s, &adj); + ret = adjust_io(s, add, start_addr, end_addr); + if (!ret) + s->resource_setup_new = 1; return ret ? ret : count; } @@ -881,7 +956,7 @@ static ssize_t show_mem_db(struct class_device *class_dev, char *buf) struct resource_map *p; ssize_t ret = 0; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); data = s->resource_data; for (p = data->mem_db.next; p != &data->mem_db; p = p->next) { @@ -893,7 +968,7 @@ static ssize_t show_mem_db(struct class_device *class_dev, char *buf) ((unsigned long) p->base + p->num - 1)); } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return (ret); } @@ -901,30 +976,26 @@ static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, siz { struct pcmcia_socket *s = class_get_devdata(class_dev); unsigned long start_addr, end_addr; - unsigned int add = 1; - adjust_t adj; + unsigned int add = ADD_MANAGED_RESOURCE; ssize_t ret = 0; ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); if (ret != 2) { ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); - add = 0; + add = REMOVE_MANAGED_RESOURCE; if (ret != 2) { ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); - add = 1; + add = ADD_MANAGED_RESOURCE; if (ret != 2) return -EINVAL; } } - if (end_addr <= start_addr) + if (end_addr < start_addr) return -EINVAL; - adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE; - adj.Resource = RES_MEMORY_RANGE; - adj.resource.memory.Base = start_addr; - adj.resource.memory.Size = end_addr - start_addr + 1; - - ret = adjust_memory(s, &adj); + ret = adjust_memory(s, add, start_addr, end_addr); + if (!ret) + s->resource_setup_new = 1; return ret ? ret : count; } @@ -936,7 +1007,8 @@ static struct class_device_attribute *pccard_rsrc_attributes[] = { NULL, }; -static int __devinit pccard_sysfs_add_rsrc(struct class_device *class_dev) +static int __devinit pccard_sysfs_add_rsrc(struct class_device *class_dev, + struct class_interface *class_intf) { struct pcmcia_socket *s = class_get_devdata(class_dev); struct class_device_attribute **attr; @@ -953,7 +1025,8 @@ static int __devinit pccard_sysfs_add_rsrc(struct class_device *class_dev) return ret; } -static void __devexit pccard_sysfs_remove_rsrc(struct class_device *class_dev) +static void __devexit pccard_sysfs_remove_rsrc(struct class_device *class_dev, + struct class_interface *class_intf) { struct pcmcia_socket *s = class_get_devdata(class_dev); struct class_device_attribute **attr;