bk://bk.arm.linux.org.uk/linux-2.6-pcmcia
rmk@flint.arm.linux.org.uk|ChangeSet|20040410222954|00978 rmk

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/04/10 23:29:54+01:00 rmk@flint.arm.linux.org.uk 
#   [PCMCIA] Re-export pci_bus_alloc_resource() from PCI code.
#   
#   Since PCMCIA now uses this, re-export it.
# 
# include/linux/pci.h
#   2004/04/10 23:27:59+01:00 rmk@flint.arm.linux.org.uk +6 -0
#   Add pci_bus_alloc_resource prototype.
# 
# drivers/pci/bus.c
#   2004/04/10 23:27:59+01:00 rmk@flint.arm.linux.org.uk +1 -0
#   Export pci_bus_alloc_resource
# 
# ChangeSet
#   2004/04/10 14:38:50+01:00 rmk@flint.arm.linux.org.uk 
#   [PCMCIA] Use kernel resource core as primary resource allocator.
#   
#   Turn the resource management on its head.  Rather than using PCMCIA's
#   resource database as the primary object to allocate resources, use
#   Linux's standard resource allocation instead.
#    
#   When we have a socket on a PCI bus, we always use the PCI resource
#   allocation functions rather than the kernels core resource allocation,
#   so that we can take account of any bridges.
# 
# drivers/pcmcia/rsrc_mgr.c
#   2004/04/10 14:37:06+01:00 rmk@flint.arm.linux.org.uk +132 -80
#   Turn the resource management on its head.  Rather than using PCMCIA's
#   resource database as the primary object to allocate resources, use
#   Linux's standard resource allocation instead.
#    
#   When we have a socket on a PCI bus, we always use the PCI resource
#   allocation functions rather than the kernels core resource allocation,
#   so that we can take account of any bridges.
# 
# ChangeSet
#   2004/04/10 14:33:03+01:00 rmk@flint.arm.linux.org.uk 
#   [PCMCIA] Remove racy check_io_resource()
#   
#   Convert do_io_probe() to use claim/free_region() rather than the
#   racy check_io_resource().  Remove check_io_resource() and the now
#   unused resource_parent() functions.
# 
# drivers/pcmcia/rsrc_mgr.c
#   2004/04/10 14:31:20+01:00 rmk@flint.arm.linux.org.uk +9 -41
#   Convert do_io_probe() to use claim/free_region() rather than the
#   racy check_io_resource().  Remove check_io_resource() and the now
#   unused resource_parent() functions.
# 
# ChangeSet
#   2004/04/10 14:29:05+01:00 rmk@flint.arm.linux.org.uk 
#   [PCMCIA] Fix resource handling for memory probe
#   
#   Add claim_region and free_region to claim/free resource regions.
#   This ensures that we only attempt to probe memory regions which
#   are directly related to the socket in question, rather than any
#   memory region we happen to be able to request.
#   
#   This also fixes a memory leak where we don't kfree the resources
#   we previously allocated.
# 
# drivers/pcmcia/rsrc_mgr.c
#   2004/04/10 14:27:24+01:00 rmk@flint.arm.linux.org.uk +38 -12
#   Add claim_region and free_region to claim/free resource regions.
# 
diff -Nru a/drivers/pci/bus.c b/drivers/pci/bus.c
--- a/drivers/pci/bus.c	Sat Apr 10 15:36:54 2004
+++ b/drivers/pci/bus.c	Sat Apr 10 15:36:54 2004
@@ -135,5 +135,6 @@
 	}
 }
 
+EXPORT_SYMBOL(pci_bus_alloc_resource);
 EXPORT_SYMBOL(pci_bus_add_devices);
 EXPORT_SYMBOL(pci_enable_bridges);
diff -Nru a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
--- a/drivers/pcmcia/rsrc_mgr.c	Sat Apr 10 15:36:54 2004
+++ b/drivers/pcmcia/rsrc_mgr.c	Sat Apr 10 15:36:54 2004
@@ -107,44 +107,8 @@
 
 ======================================================================*/
 
-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 @@
 	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 @@
 #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 @@
     }   
     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 @@
 
     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 @@
 	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 @@
 	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 @@
 
 #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
@@ -569,66 +595,86 @@
 int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t 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;
+
+	if (align == 0)
+		align = 0x10000UL;
+
+	data.mask = align - 1;
+	data.offset = *base & data.mask;
+	data.map = &io_db;
 
-    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;
+#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;
-
-    low = low || !(s->features & SS_CAP_PAGE_REGS);
+	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);
+
+	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;
+		}
 
-    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;
+#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 (!align)
-		    break;
-	    }
+		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 -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	Sat Apr 10 15:36:54 2004
+++ b/include/linux/pci.h	Sat Apr 10 15:36:54 2004
@@ -676,6 +676,12 @@
 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 */
