[PATCH 09/49] pcmcia: rsrc_nonstatic io memory probe improvements

Dominik Brodowski linux at dominikbrodowski.net
Sat Feb 27 05:42:04 EST 2010


Add a lot of documentation to the rsrc_nonstatic io memory probe
functions. Also, add a first memory probe call -- just checking
whether request_resource() succeeds -- upon adding of resources.

Tested-by: Wolfram Sang <w.sang at pengutronix.de>
Signed-off-by: Dominik Brodowski <linux at dominikbrodowski.net>
---
 drivers/pcmcia/rsrc_nonstatic.c |  174 +++++++++++++++++++++++++-------------
 1 files changed, 114 insertions(+), 60 deletions(-)

diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index b886385..120d5ad 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -264,18 +264,15 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
 }
 #endif
 
-/*======================================================================
-
-    This is tricky... when we set up CIS memory, we try to validate
-    the memory window space allocations.
-
-======================================================================*/
+/*======================================================================*/
 
-/* Validation function for cards with a valid CIS */
+/**
+ * readable() - iomem validation function for cards with a valid CIS
+ */
 static int readable(struct pcmcia_socket *s, struct resource *res,
 		    unsigned int *count)
 {
-	int ret = -1;
+	int ret = -EINVAL;
 
 	s->cis_mem.res = res;
 	s->cis_virt = ioremap(res->start, s->map_size);
@@ -286,13 +283,16 @@ static int readable(struct pcmcia_socket *s, struct resource *res,
 		s->cis_virt = NULL;
 	}
 	s->cis_mem.res = NULL;
-	if ((ret != 0) || (*count == 0))
-		return 0;
-	return 1;
+	if ((ret) || (*count == 0))
+		return -EINVAL;
+	return 0;
 }
 
-/* Validation function for simple memory cards */
-static int checksum(struct pcmcia_socket *s, struct resource *res)
+/**
+ * checksum() - iomem validation function for simple memory cards
+ */
+static int checksum(struct pcmcia_socket *s, struct resource *res,
+		    unsigned int *value)
 {
 	pccard_mem_map map;
 	int i, a = 0, b = -1, d;
@@ -320,61 +320,83 @@ static int checksum(struct pcmcia_socket *s, struct resource *res)
 		iounmap(virt);
 	}
 
-	return (b == -1) ? -1 : (a>>1);
-}
-
-static int
-cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
-{
-	struct resource *res1, *res2;
-	unsigned int info1, info2;
-	int ret = 0;
-
-	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
-	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
-			"PCMCIA memprobe");
-
-	if (res1 && res2) {
-		ret = readable(s, res1, &info1);
-		ret += readable(s, res2, &info2);
-	}
+	if (b == -1)
+		return -EINVAL;
 
-	free_region(res2);
-	free_region(res1);
+	*value = a;
 
-	return (ret == 2) && (info1 == info2);
+	return 0;
 }
 
-static int
-checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
+/**
+ * do_validate_mem() - low level validate a memory region for PCMCIA use
+ * @s:		PCMCIA socket to validate
+ * @base:	start address of resource to check
+ * @size:	size of resource to check
+ * @validate:	validation function to use
+ *
+ * do_validate_mem() splits up the memory region which is to be checked
+ * into two parts. Both are passed to the @validate() function. If
+ * @validate() returns non-zero, or the value parameter to @validate()
+ * is zero, or the value parameter is different between both calls,
+ * the check fails, and -EINVAL is returned. Else, 0 is returned.
+ */
+static int do_validate_mem(struct pcmcia_socket *s,
+			   unsigned long base, unsigned long size,
+			   int validate (struct pcmcia_socket *s,
+					 struct resource *res,
+					 unsigned int *value))
 {
 	struct resource *res1, *res2;
-	int a = -1, b = -1;
+	unsigned int info1 = 1, info2 = 1;
+	int ret = -EINVAL;
 
 	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
 	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
 			"PCMCIA memprobe");
 
 	if (res1 && res2) {
-		a = checksum(s, res1);
-		b = checksum(s, res2);
+		ret = 0;
+		if (validate) {
+			ret = validate(s, res1, &info1);
+			ret += validate(s, res2, &info2);
+		}
 	}
 
 	free_region(res2);
 	free_region(res1);
 
-	return (a == b) && (a >= 0);
-}
+	dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
+		base, base+size-1, res1, res2, ret, info1, info2);
 
-/*======================================================================
+	if ((ret) || (info1 != info2) || (info1 == 0))
+		return -EINVAL;
 
-    The memory probe.  If the memory list includes a 64K-aligned block
-    below 1MB, we probe in 64K chunks, and as soon as we accumulate at
-    least mem_limit free space, we quit.
+	return 0;
+}
 
-======================================================================*/
 
-static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
+/**
+ * do_mem_probe() - validate a memory region for PCMCIA use
+ * @s:		PCMCIA socket to validate
+ * @base:	start address of resource to check
+ * @num:	size of resource to check
+ * @validate:	validation function to use
+ * @fallback:	validation function to use if validate fails
+ *
+ * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
+ * To do so, the area is split up into sensible parts, and then passed
+ * into the @validate() function. Only if @validate() and @fallback() fail,
+ * the area is marked as unavaibale for use by the PCMCIA subsystem. The
+ * function returns the size of the usable memory area.
+ */
+static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
+			int validate (struct pcmcia_socket *s,
+				      struct resource *res,
+				      unsigned int *value),
+			int fallback (struct pcmcia_socket *s,
+				      struct resource *res,
+				      unsigned int *value))
 {
 	struct socket_data *s_data = s->resource_data;
 	u_long i, j, bad, fail, step;
@@ -392,15 +414,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
 	for (i = j = base; i < base+num; i = j + step) {
 		if (!fail) {
 			for (j = i; j < base+num; j += step) {
-				if (cis_readable(s, j, step))
+				if (!do_validate_mem(s, j, step, validate))
 					break;
 			}
 			fail = ((i == base) && (j == base+num));
 		}
-		if (fail) {
-			for (j = i; j < base+num; j += 2*step)
-				if (checksum_match(s, j, step) &&
-					checksum_match(s, j + step, step))
+		if ((fail) && (fallback)) {
+			for (j = i; j < base+num; j += step)
+				if (!do_validate_mem(s, j, step, fallback))
 					break;
 		}
 		if (i != j) {
@@ -415,8 +436,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
 	return num - bad;
 }
 
+
 #ifdef CONFIG_PCMCIA_PROBE
 
+/**
+ * inv_probe() - top-to-bottom search for one usuable high memory area
+ * @s:		PCMCIA socket to validate
+ * @m:		resource_map to check
+ */
 static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
 {
 	struct socket_data *s_data = s->resource_data;
@@ -431,9 +458,18 @@ static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
 	}
 	if (m->base < 0x100000)
 		return 0;
-	return do_mem_probe(m->base, m->num, s);
+	return do_mem_probe(s, m->base, m->num, readable, checksum);
 }
 
+/**
+ * validate_mem() - memory probe function
+ * @s:		PCMCIA socket to validate
+ * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
+ *
+ * The memory probe.  If the memory list includes a 64K-aligned block
+ * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
+ * least mem_limit free space, we quit. Returns 0 on usuable ports.
+ */
 static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 {
 	struct resource_map *m, mm;
@@ -456,7 +492,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 		if (mm.base >= 0x100000)
 			continue;
 		if ((mm.base | mm.num) & 0xffff) {
-			ok += do_mem_probe(mm.base, mm.num, s);
+			ok += do_mem_probe(s, mm.base, mm.num, readable,
+					   checksum);
 			continue;
 		}
 		/* Special probe for 64K-aligned block */
@@ -466,7 +503,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 				if (ok >= mem_limit)
 					sub_interval(&s_data->mem_db, b, 0x10000);
 				else
-					ok += do_mem_probe(b, 0x10000, s);
+					ok += do_mem_probe(s, b, 0x10000,
+							   readable, checksum);
 			}
 		}
 	}
@@ -479,6 +517,13 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 
 #else /* CONFIG_PCMCIA_PROBE */
 
+/**
+ * validate_mem() - memory probe function
+ * @s:		PCMCIA socket to validate
+ * @probe_mask: ignored
+ *
+ * Returns 0 on usuable ports.
+ */
 static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 {
 	struct resource_map *m, mm;
@@ -487,7 +532,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 
 	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
 		mm = *m;
-		ok += do_mem_probe(mm.base, mm.num, s);
+		ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
 	}
 	if (ok > 0)
 		return 0;
@@ -497,7 +542,13 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 #endif /* CONFIG_PCMCIA_PROBE */
 
 
-/*
+/**
+ * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
+ * @s:		PCMCIA socket to validate
+ *
+ * This is tricky... when we set up CIS memory, we try to validate
+ * the memory window space allocations.
+ *
  * Locking note: Must be called with skt_mutex held!
  */
 static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
@@ -515,10 +566,11 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
 		probe_mask = MEM_PROBE_HIGH;
 
 	if (probe_mask & ~s_data->rsrc_mem_probe) {
-		if (s->state & SOCKET_PRESENT)
+		if (s->state & SOCKET_PRESENT) {
 			ret = validate_mem(s, probe_mask);
-		if (!ret)
-			s_data->rsrc_mem_probe |= probe_mask;
+			if (!ret)
+				s_data->rsrc_mem_probe |= probe_mask;
+		}
 	}
 
 	mutex_unlock(&rsrc_mutex);
@@ -723,6 +775,8 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned
 	switch (action) {
 	case ADD_MANAGED_RESOURCE:
 		ret = add_interval(&data->mem_db, start, size);
+		if (!ret)
+			do_mem_probe(s, start, size, NULL, NULL);
 		break;
 	case REMOVE_MANAGED_RESOURCE:
 		ret = sub_interval(&data->mem_db, start, size);
-- 
1.6.3.3




More information about the linux-pcmcia mailing list