[PATCH] 2.6.14-rc3 ixp4xx_copy_from little endian/alignment

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I'm resubmitting this because originally we just submitted it to
linux-arm-kernel and I believe this means it hasn't been seen in
the right places to get it into the MM patch set.  This patch and
the ones I will be submitting immediately afterwards or that we have
submitted before are sufficient to allow a bootable, working,
kernel on the LinkSys NSLU2 in either little-endian or big-endian
mode.  The patches have been tested against both 2.6.14 and
2.6.14-rc5-mm1 with a variety of rootfs's (arm and thumb uclibc,
arm glibc).

John Bowler <[email protected]>

>From: Alessandro Zummo
  this short patch will modify the ixp4xx mtd code in order to fix
 a couple of problems we have verified while working
 with the NSLU2. 
 
 - word-wide accesses vs byte-wide
 - little endian support
 
 There's an insightful description in the first lines of the
 patch.
 
 The author, John Bowler, has put a lot of efforts in this patch,
 verifying that this is the cleanest and shortest patch that
 can fix those problem.
 
 John and others wroted at least three different solutions
 analyzing both the current code, our needs and the 
 philosophical aspects of this code.. and I'm not kidding!
 
 The patch has been tested to work on the NSLU2 in both
 LE and BE mode.
 
 Signed-off-by: John Bowler <[email protected]>
 Signed-off-by: Alessandro Zummo <[email protected]>

--- linux-2.6.13/drivers/mtd/maps/ixp4xx.c	2005-10-07 15:55:08.958509801 -0700
+++ linux-2.6.13/drivers/mtd/maps/ixp4xx.c	2005-10-07 19:06:22.352484966 -0700
@@ -22,6 +22,7 @@
 #include <linux/string.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/cfi_endian.h>
 #include <linux/mtd/partitions.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
@@ -30,18 +31,45 @@
 
 #include <linux/reboot.h>
 
+/* On a little-endian IXP4XX system (tested on NSLU2) contrary to the
+ * Intel documentation LDRH/STRH appears to XOR the address with 10b.
+ * This causes the cfi commands (sent to the command address, 0xAA for
+ * 16 bit flash) to fail.  This is fixed here by XOR'ing the address
+ * before use with 10b.  The cost of this is that the flash layout ends
+ * up with pdp-endiannes (on an LE syste), however this is not a problem
+ * as the access code consistently only accesses half words - so the
+ * endianness is not determinable on stuff which is written and read
+ * consistently in the little endian world.
+ *
+ * For flash data from the big-endian world, however, the results are
+ * weird - the pdp-endianness results in the data apparently being
+ * 2-byte swapped (as in dd conv=swab).  To work round this the 16
+ * bit values are written and read using cpu_to_cfi16 and cfi16_to_cpu,
+ * by default these are no-ops, but if the MTD driver is configed with
+ * CONFIG_MTD_CFI_BE_BYTE_SWAP the macros will byte swap the data,
+ * resulting in a consistently BE view of the flash on both BE (no
+ * op) and LE systems.  This config setting also causes the command
+ * data from the CFI implementation to get swapped - as is required
+ * so that this code will *unswap* it and give the correct command
+ * data to the flash.
+ */
 #ifndef __ARMEB__
 #define	BYTE0(h)	((h) & 0xFF)
 #define	BYTE1(h)	(((h) >> 8) & 0xFF)
+#define	FLASHWORD(a)	(*(__u16*)((u32)(a) ^ 2))
 #else
 #define	BYTE0(h)	(((h) >> 8) & 0xFF)
 #define	BYTE1(h)	((h) & 0xFF)
+#define	FLASHWORD(a)	(*(__u16*)(a))
 #endif
 
+#define FLASHW(a)	cfi16_to_cpu(FLASHWORD(a))
+#define FLASHSET(a,v)	(FLASHWORD(a) = cpu_to_cfi16(v))
+
 static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs)
 {
 	map_word val;
-	val.x[0] = *(__u16 *) (map->map_priv_1 + ofs);
+	val.x[0] = FLASHW(map->map_priv_1 + ofs);
 	return val;
 }
 
@@ -53,19 +81,23 @@
 static void ixp4xx_copy_from(struct map_info *map, void *to,
 			     unsigned long from, ssize_t len)
 {
-	int i;
-	u8 *dest = (u8 *) to;
-	u16 *src = (u16 *) (map->map_priv_1 + from);
-	u16 data;
+	if (len <= 0)
+		return;
 
-	for (i = 0; i < (len / 2); i++) {
-		data = src[i];
-		dest[i * 2] = BYTE0(data);
-		dest[i * 2 + 1] = BYTE1(data);
+	u8 *dest = (u8 *) to;
+	u8 *src  = (u8 *) (map->map_priv_1 + from);
+	if (from & 1)
+		*dest++ = BYTE1(FLASHW(src-1)), ++src, --len;
+
+	while (len >= 2) {
+		u16 data = FLASHW(src); src += 2;
+		*dest++ = BYTE0(data);
+		*dest++ = BYTE1(data);
+		len -= 2;
 	}
 
-	if (len & 1)
-		dest[len - 1] = BYTE0(src[i]);
+	if (len > 0)
+		*dest++ = BYTE0(FLASHW(src));
 }
 
 /* 
@@ -75,7 +107,7 @@
 static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr)
 {
 	if (!(adr & 1))
-	       *(__u16 *) (map->map_priv_1 + adr) = d.x[0];
+	       FLASHSET(map->map_priv_1 + adr, d.x[0]);
 }
 
 /* 
@@ -83,7 +115,7 @@
  */
 static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
 {
-       *(__u16 *) (map->map_priv_1 + adr) = d.x[0];
+       FLASHSET(map->map_priv_1 + adr, d.x[0]);
 }
 
 struct ixp4xx_flash_info {

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux