Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6
[safe/jmp/linux-2.6] / drivers / pcmcia / rsrc_nonstatic.c
index 19cecb5..a6eb7b5 100644 (file)
@@ -55,8 +55,8 @@ struct resource_map {
 
 struct socket_data {
        struct resource_map             mem_db;
+       struct resource_map             mem_db_valid;
        struct resource_map             io_db;
-       unsigned int                    rsrc_mem_probe;
 };
 
 #define MEM_PROBE_LOW  (1 << 0)
@@ -124,8 +124,10 @@ static int add_interval(struct resource_map *map, u_long base, u_long num)
        struct resource_map *p, *q;
 
        for (p = map; ; p = p->next) {
-               if ((p != map) && (p->base+p->num-1 >= base))
-                       return -1;
+               if ((p != map) && (p->base+p->num >= base)) {
+                       p->num = max(num + base - p->base, p->num);
+                       return 0;
+               }
                if ((p->next == map) || (p->next->base > base+num-1))
                        break;
        }
@@ -212,7 +214,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
                return;
        }
        for (i = base, most = 0; i < base+num; i += 8) {
-               res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
+               res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
                if (!res)
                        continue;
                hole = inb(i);
@@ -229,9 +231,14 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
 
        bad = any = 0;
        for (i = base; i < base+num; i += 8) {
-               res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
-               if (!res)
+               res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
+               if (!res) {
+                       if (!any)
+                               printk(" excluding");
+                       if (!bad)
+                               bad = any = i;
                        continue;
+               }
                for (j = 0; j < 8; j++)
                        if (inb(i+j) != most)
                                break;
@@ -251,6 +258,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
        }
        if (bad) {
                if ((num > 16) && (bad == base) && (i == base+num)) {
+                       sub_interval(&s_data->io_db, bad, i-bad);
                        printk(" nothing: probe failed.\n");
                        return;
                } else {
@@ -355,6 +363,7 @@ static int do_validate_mem(struct pcmcia_socket *s,
                                         struct resource *res,
                                         unsigned int *value))
 {
+       struct socket_data *s_data = s->resource_data;
        struct resource *res1, *res2;
        unsigned int info1 = 1, info2 = 1;
        int ret = -EINVAL;
@@ -380,6 +389,12 @@ static int do_validate_mem(struct pcmcia_socket *s,
        if ((ret) || (info1 != info2) || (info1 == 0))
                return -EINVAL;
 
+       if (validate && !s->fake_cis) {
+               /* move it to the validated data set */
+               add_interval(&s_data->mem_db_valid, base, size);
+               sub_interval(&s_data->mem_db, base, size);
+       }
+
        return 0;
 }
 
@@ -489,6 +504,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
        if (probe_mask & MEM_PROBE_HIGH) {
                if (inv_probe(s_data->mem_db.next, s) > 0)
                        return 0;
+               if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
+                       return 0;
                dev_printk(KERN_NOTICE, &s->dev,
                           "cs: warning: no high memory space available!\n");
                return -ENODEV;
@@ -563,21 +580,18 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
 {
        struct socket_data *s_data = s->resource_data;
        unsigned int probe_mask = MEM_PROBE_LOW;
-       int ret = 0;
+       int ret;
 
-       if (!probe_mem)
+       if (!probe_mem || !(s->state & SOCKET_PRESENT))
                return 0;
 
        if (s->features & SS_CAP_PAGE_REGS)
                probe_mask = MEM_PROBE_HIGH;
 
-       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;
-               }
-       }
+       ret = validate_mem(s, probe_mask);
+
+       if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
+               return 0;
 
        return ret;
 }
@@ -588,52 +602,49 @@ struct pcmcia_align_data {
        struct resource_map     *map;
 };
 
-static void
-pcmcia_common_align(void *align_data, struct resource *res,
-                       resource_size_t size, resource_size_t align)
+static resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data,
+                                       resource_size_t start)
 {
-       struct pcmcia_align_data *data = align_data;
-       resource_size_t start;
+       resource_size_t ret;
        /*
         * 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;
+       ret = (start & ~align_data->mask) + align_data->offset;
+       if (ret < start)
+               ret += align_data->mask + 1;
+       return ret;
 }
 
-static void
-pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
-               resource_size_t align)
+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;
        struct resource_map *m;
+       resource_size_t start;
 
-       pcmcia_common_align(data, res, size, align);
+       start = pcmcia_common_align(data, res->start);
 
        for (m = data->map->next; m != data->map; m = m->next) {
-               unsigned long start = m->base;
-               unsigned long end = m->base + m->num - 1;
+               unsigned long map_start = m->base;
+               unsigned long map_end = m->base + m->num - 1;
 
                /*
                 * 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 (start < map_start)
+                       start = pcmcia_common_align(data, map_start);
 
                /*
                 * If we're above the area which was passed in, there's
                 * no point proceeding.
                 */
-               if (res->start >= res->end)
+               if (start >= res->end)
                        break;
 
-               if ((res->start + size - 1) <= end)
+               if ((start + size - 1) <= map_end)
                        break;
        }
 
@@ -641,7 +652,9 @@ pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
         * If we failed to find something suitable, ensure we fail.
         */
        if (m == data->map)
-               res->start = res->end;
+               start = res->end;
+
+       return start;
 }
 
 /*
@@ -721,15 +734,15 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
        struct socket_data *s_data = s->resource_data;
        struct pcmcia_align_data data;
        unsigned long min, max;
-       int ret, i;
+       int ret, i, j;
 
        low = low || !(s->features & SS_CAP_PAGE_REGS);
 
        data.mask = align - 1;
        data.offset = base & data.mask;
-       data.map = &s_data->mem_db;
 
        for (i = 0; i < 2; i++) {
+               data.map = &s_data->mem_db_valid;
                if (low) {
                        max = 0x100000UL;
                        min = base < max ? base : 0;
@@ -738,15 +751,23 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
                        min = 0x100000UL + base;
                }
 
+               for (j = 0; j < 2; j++) {
 #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
+                       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(&iomem_resource, res, num, min,
-                                               max, 1, pcmcia_align, &data);
+                       {
+                               ret = allocate_resource(&iomem_resource,
+                                                       res, num, min, max, 1,
+                                                       pcmcia_align, &data);
+                       }
+                       if (ret == 0)
+                               break;
+                       data.map = &s_data->mem_db;
+               }
                if (ret == 0 || low)
                        break;
                low = 1;
@@ -789,9 +810,18 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned
 static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
 {
        struct socket_data *data = s->resource_data;
-       unsigned long size = end - start + 1;
+       unsigned long size;
        int ret = 0;
 
+#if defined(CONFIG_X86)
+       /* on x86, avoid anything < 0x100 for it is often used for
+        * legacy platform devices */
+       if (start < 0x100)
+               start = 0x100;
+#endif
+
+       size = end - start + 1;
+
        if (end < start)
                return -EINVAL;
 
@@ -841,8 +871,7 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
                return -EINVAL;
 #endif
 
-       for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
-               res = s->cb_dev->bus->resource[i];
+       pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
                if (!res)
                        continue;
 
@@ -850,10 +879,8 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
                        if (res == &ioport_resource)
                                continue;
                        dev_printk(KERN_INFO, &s->cb_dev->dev,
-                                  "pcmcia: parent PCI bridge I/O "
-                                  "window: 0x%llx - 0x%llx\n",
-                                  (unsigned long long)res->start,
-                                  (unsigned long long)res->end);
+                                  "pcmcia: parent PCI bridge window: %pR\n",
+                                  res);
                        if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
                                done |= IORESOURCE_IO;
 
@@ -863,10 +890,8 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
                        if (res == &iomem_resource)
                                continue;
                        dev_printk(KERN_INFO, &s->cb_dev->dev,
-                                  "pcmcia: parent PCI bridge Memory "
-                                  "window: 0x%llx - 0x%llx\n",
-                                  (unsigned long long)res->start,
-                                  (unsigned long long)res->end);
+                                  "pcmcia: parent PCI bridge window: %pR\n",
+                                  res);
                        if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
                                done |= IORESOURCE_MEM;
                }
@@ -899,6 +924,7 @@ static int nonstatic_init(struct pcmcia_socket *s)
                return -ENOMEM;
 
        data->mem_db.next = &data->mem_db;
+       data->mem_db_valid.next = &data->mem_db_valid;
        data->io_db.next = &data->io_db;
 
        s->resource_data = (void *) data;
@@ -913,6 +939,10 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s)
        struct socket_data *data = s->resource_data;
        struct resource_map *p, *q;
 
+       for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
+               q = p->next;
+               kfree(p);
+       }
        for (p = data->mem_db.next; p != &data->mem_db; p = q) {
                q = p->next;
                kfree(p);
@@ -1008,6 +1038,16 @@ static ssize_t show_mem_db(struct device *dev,
        mutex_lock(&s->ops_mutex);
        data = s->resource_data;
 
+       for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
+            p = p->next) {
+               if (ret > (PAGE_SIZE - 10))
+                       continue;
+               ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
+                               "0x%08lx - 0x%08lx\n",
+                               ((unsigned long) p->base),
+                               ((unsigned long) p->base + p->num - 1));
+       }
+
        for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
                if (ret > (PAGE_SIZE - 10))
                        continue;