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

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/05/18 22:02:38+01:00 rmk@flint.arm.linux.org.uk 
#   [PCMCIA] Convert IO resource allocation to use struct resource.
#   
#   This causes PCMCIA to use struct resource internally for IO resources.
#   This means that we can keep track of the resource pointer, expand
#   this resource if necessary, and use release_resource() on it when
#   we're done.
#   
#   This eventually means that we can change these to normal resources
#   which aren't marked busy.
# 
# include/pcmcia/ss.h
#   2004/05/18 22:00:16+01:00 rmk@flint.arm.linux.org.uk +1 -0
#   Add struct resource to io_window_t.
# 
# drivers/pcmcia/rsrc_mgr.c
#   2004/05/18 22:00:16+01:00 rmk@flint.arm.linux.org.uk +32 -7
#   Add adjust_io_region to expand already allocated IO resources.
#   find_io_region now returns a struct resource.
# 
# drivers/pcmcia/cs_internal.h
#   2004/05/18 22:00:15+01:00 rmk@flint.arm.linux.org.uk +3 -1
#   Add and update function prototypes for find_io_region and
#   adjust_io_region.
# 
# drivers/pcmcia/cs.c
#   2004/05/18 22:00:15+01:00 rmk@flint.arm.linux.org.uk +13 -7
#   Use struct resource when manipulating IO regions.
# 
# ChangeSet
#   2004/05/18 21:52:46+01:00 rmk@flint.arm.linux.org.uk 
#   [PCMCIA] Fix a couple of resource bugs.
#   
#   Fix resource database bug where base + num wraps to zero.  Also,
#   ensure that we always take the resource semaphore whenever we
#   allocate a resource.
# 
# drivers/pcmcia/rsrc_mgr.c
#   2004/05/18 21:50:46+01:00 rmk@flint.arm.linux.org.uk +6 -10
#   Fix resource database bug where base + num wraps to zero.
#   Ensure that we always take the resource semaphore whenever we
#   allocate a resource.
# 
diff -Nru a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
--- a/drivers/pcmcia/cs.c	Wed May 19 00:02:22 2004
+++ b/drivers/pcmcia/cs.c	Wed May 19 00:02:22 2004
@@ -789,9 +789,10 @@
 	    return 1;
     for (i = 0; i < MAX_IO_WIN; i++) {
 	if (s->io[i].NumPorts == 0) {
-	    if (find_io_region(base, num, align, name, s) == 0) {
+	    s->io[i].res = find_io_region(*base, num, align, name, s);
+	    if (s->io[i].res) {
 		s->io[i].Attributes = attr;
-		s->io[i].BasePort = *base;
+		s->io[i].BasePort = *base = s->io[i].res->start;
 		s->io[i].NumPorts = s->io[i].InUse = num;
 		break;
 	    } else
@@ -801,7 +802,8 @@
 	/* Try to extend top of window */
 	try = s->io[i].BasePort + s->io[i].NumPorts;
 	if ((*base == 0) || (*base == try))
-	    if (find_io_region(&try, num, 0, name, s) == 0) {
+	    if (adjust_io_region(s->io[i].res, s->io[i].res->start,
+				 s->io[i].res->end + num, s) == 0) {
 		*base = try;
 		s->io[i].NumPorts += num;
 		s->io[i].InUse += num;
@@ -810,7 +812,8 @@
 	/* Try to extend bottom of window */
 	try = s->io[i].BasePort - num;
 	if ((*base == 0) || (*base == try))
-	    if (find_io_region(&try, num, 0, name, s) == 0) {
+	    if (adjust_io_region(s->io[i].res, s->io[i].res->start - num,
+				 s->io[i].res->end, s) == 0) {
 		s->io[i].BasePort = *base = try;
 		s->io[i].NumPorts += num;
 		s->io[i].InUse += num;
@@ -824,15 +827,18 @@
 			     ioaddr_t num)
 {
     int i;
-    if(!(s->features & SS_CAP_STATIC_MAP))
-	release_region(base, num);
+
     for (i = 0; i < MAX_IO_WIN; i++) {
 	if ((s->io[i].BasePort <= base) &&
 	    (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) {
 	    s->io[i].InUse -= num;
 	    /* Free the window if no one else is using it */
-	    if (s->io[i].InUse == 0)
+	    if (s->io[i].InUse == 0) {
 		s->io[i].NumPorts = 0;
+		release_resource(s->io[i].res);
+		kfree(s->io[i].res);
+		s->io[i].res = NULL;
+	    }
 	}
     }
 }
diff -Nru a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
--- a/drivers/pcmcia/cs_internal.h	Wed May 19 00:02:22 2004
+++ b/drivers/pcmcia/cs_internal.h	Wed May 19 00:02:22 2004
@@ -181,8 +181,10 @@
 
 /* In rsrc_mgr */
 void validate_mem(struct pcmcia_socket *s);
-int find_io_region(ioaddr_t *base, ioaddr_t num, unsigned long align,
+struct resource *find_io_region(unsigned long base, int num, unsigned long align,
 		   char *name, struct pcmcia_socket *s);
+int adjust_io_region(struct resource *res, unsigned long r_start,
+		     unsigned long r_end, 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);
 int try_irq(u_int Attributes, int irq, int specific);
diff -Nru a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
--- a/drivers/pcmcia/rsrc_mgr.c	Wed May 19 00:02:22 2004
+++ b/drivers/pcmcia/rsrc_mgr.c	Wed May 19 00:02:22 2004
@@ -550,7 +550,7 @@
 
 	for (m = data->map->next; m != data->map; m = m->next) {
 		unsigned long start = m->base;
-		unsigned long end = m->base + m->num;
+		unsigned long end = m->base + m->num - 1;
 
 		/*
 		 * If the lower resources are not available, try aligning
@@ -569,7 +569,7 @@
 		if (res->start >= res->end)
 			break;
 
-		if ((res->start + size) <= end)
+		if ((res->start + size - 1) <= end)
 			break;
 	}
 
@@ -580,6 +580,32 @@
 		res->start = res->end;
 }
 
+/*
+ * Adjust an existing IO region allocation, but making sure that we don't
+ * encroach outside the resources which the user supplied.
+ */
+int adjust_io_region(struct resource *res, unsigned long r_start,
+		     unsigned long r_end, struct pcmcia_socket *s)
+{
+	resource_map_t *m;
+	int ret = -ENOMEM;
+
+	down(&rsrc_sem);
+	for (m = io_db.next; m != &io_db; m = m->next) {
+		unsigned long start = m->base;
+		unsigned long end = m->base + m->num - 1;
+
+		if (start > r_start || r_end > end)
+			continue;
+
+		ret = adjust_resource(res, r_start, r_end - r_start + 1);
+		break;
+	}
+	up(&rsrc_sem);
+
+	return ret;
+}
+
 /*======================================================================
 
     These find ranges of I/O ports or memory addresses that are not
@@ -593,40 +619,37 @@
     
 ======================================================================*/
 
-int find_io_region(ioaddr_t *base, ioaddr_t num, unsigned long align,
-		   char *name, struct pcmcia_socket *s)
+struct resource *find_io_region(unsigned long base, int num,
+		   unsigned long align, char *name, struct pcmcia_socket *s)
 {
 	struct resource *res = make_resource(0, num, IORESOURCE_IO, name);
 	struct pcmcia_align_data data;
-	unsigned long min = *base;
+	unsigned long min = base;
 	int ret;
 
 	if (align == 0)
 		align = 0x10000;
 
 	data.mask = align - 1;
-	data.offset = *base & data.mask;
+	data.offset = base & data.mask;
 	data.map = &io_db;
 
+	down(&rsrc_sem);
 #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);
-	}
+	up(&rsrc_sem);
 
 	if (ret != 0) {
 		kfree(res);
-	} else {
-		*base = res->start;
+		res = NULL;
 	}
-	return ret;
+	return res;
 }
 
 int find_mem_region(u_long *base, u_long num, u_long align,
@@ -652,6 +675,7 @@
 			min = 0x100000UL + *base;
 		}
 
+		down(&rsrc_sem);
 #ifdef CONFIG_PCI
 		if (s->cb_dev) {
 			ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
@@ -659,12 +683,9 @@
 						     pcmcia_align, &data);
 		} else
 #endif
-		{
-			down(&rsrc_sem);
 			ret = allocate_resource(&iomem_resource, res, num, min,
 						max, 0, pcmcia_align, &data);
-			up(&rsrc_sem);
-		}
+		up(&rsrc_sem);
 		if (ret == 0 || low)
 			break;
 		low = 1;
diff -Nru a/include/pcmcia/ss.h b/include/pcmcia/ss.h
--- a/include/pcmcia/ss.h	Wed May 19 00:02:22 2004
+++ b/include/pcmcia/ss.h	Wed May 19 00:02:22 2004
@@ -145,6 +145,7 @@
 	u_int			Attributes;
 	ioaddr_t		BasePort, NumPorts;
 	ioaddr_t		InUse, Config;
+	struct resource		*res;
 } io_window_t;
 
 #define WINDOW_MAGIC	0xB35C
