summaryrefslogtreecommitdiff
path: root/packages/linux/nslu2-kernel/2.6.15/90-ixp4xx-nslu2.patch
blob: d3497850cd083080a4b2507130ebd1f7e48f7aa5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
--- linux-2.6.15/drivers/mtd/maps/ixp4xx.c	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15/drivers/mtd/maps/ixp4xx.c	1970-01-01 00:00:00.000000000 +0000
@@ -28,24 +28,45 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
+#include <linux/mtd/cfi_endian.h>
 
 #include <asm/io.h>
 #include <asm/mach/flash.h>
 
 #include <linux/reboot.h>
 
-#ifndef __ARMEB__
-#define	BYTE0(h)	((h) & 0xFF)
-#define	BYTE1(h)	(((h) >> 8) & 0xFF)
+/*
+ * Read a 16 bit word from flash address 'addr'.
+ * This code ensures that the ixp4xx flash behaves as a consistent byte
+ * stream (same bytes in same order) regardless of whether the CPU is
+ * running in BE or LE mode, and regardless of whether the read16 or
+ * copyfrom method is used to read the flash data.
+ *
+ * When the cpu is in little-endian mode the hardware inverts the low
+ * address lines between the MMU (including the cache) and the flash chip
+ * ('address coherency') so we need to undo the swizzling to ensure commands
+ * and the like end up on the correct flash address.  This requirement
+ * only applies to command operations, however this code cannot distinguish
+ * a command read from a data read, therefore the fix applies to the data
+ * as well as the command and must be implemented consistently throughout
+ * the map driver (here).
+ */
+static inline u16 flash_read16(const void __iomem *virt, unsigned long offset)
+{
+#ifdef __ARMEB__
+	return *(const volatile u16 __iomem *)(virt + offset);
 #else
-#define	BYTE0(h)	(((h) >> 8) & 0xFF)
-#define	BYTE1(h)	((h) & 0xFF)
+	/* Cancel the address coherency invert of address line 1 in the
+	 * hardware on LE systems.
+	 */
+	return *(const volatile u16 __iomem *)(virt + (offset ^ 2));
 #endif
+}
 
 static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs)
 {
 	map_word val;
-	val.x[0] = le16_to_cpu(readw(map->virt + ofs));
+	val.x[0] = cfi16_to_cpu(flash_read16(map->virt, ofs));
 	return val;
 }
 
@@ -53,41 +74,70 @@ static map_word ixp4xx_read16(struct map
  * The IXP4xx expansion bus only allows 16-bit wide acceses
  * when attached to a 16-bit wide device (such as the 28F128J3A),
  * so we can't just memcpy_fromio().
+ *
+ * This code must obtain the correct bytes in the correct order
+ * according to the configuration of the CFI subsystem.
  */
+#if (defined CFI_BIG_ENDIAN) || ((defined CFI_HOST_ENDIAN) && (defined __ARMEB__))
+	/* BE flash */
+#define	BYTE0(h)	(((h) >> 8) & 0xFF)
+#define	BYTE1(h)	((h) & 0xFF)
+#else
+	/* LE flash */
+#define	BYTE0(h)	((h) & 0xFF)
+#define	BYTE1(h)	(((h) >> 8) & 0xFF)
+#endif
+
 static void ixp4xx_copy_from(struct map_info *map, void *to,
 			     unsigned long from, ssize_t len)
 {
-	int i;
-	u8 *dest = (u8 *) to;
-	void __iomem *src = map->virt + from;
-	u16 data;
-
-	for (i = 0; i < (len / 2); i++) {
-		data = le16_to_cpu(readw(src + 2*i));
-		dest[i * 2] = BYTE0(data);
-		dest[i * 2 + 1] = BYTE1(data);
+	u8 *dest = to;
+	const void __iomem * const virt = map->virt;
+
+	if (len <= 0)
+		return;
+
+	if (from & 1) {
+		*dest++ = BYTE1(flash_read16(virt, from-1));
+                ++from;
+		--len;
+	}
+
+	while (len >= 2) {
+		u16 data = flash_read16(virt, from);
+		*dest++ = BYTE0(data);
+		*dest++ = BYTE1(data);
+		from += 2;
+		len -= 2;
 	}
 
-	if (len & 1)
-		dest[len - 1] = BYTE0(le16_to_cpu(readw(src + 2*i)));
+	if (len > 0)
+		*dest++ = BYTE0(flash_read16(virt, from));
 }
 
 /*
- * Unaligned writes are ignored, causing the 8-bit
- * probe to fail and proceed to the 16-bit probe (which succeeds).
+ * Fast write16 function without the probing check below.
  */
-static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr)
+static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
 {
-	if (!(adr & 1))
-		writew(cpu_to_le16(d.x[0]), map->virt + adr);
+#ifdef __ARMEB__
+	*(volatile u16 __iomem*)(map->virt + adr) = cpu_to_cfi16(d.x[0]);
+#else
+	/* Cancel the address coherency invert of address line 1 in the
+	 * hardware on LE systems.
+	 */
+	*(volatile u16 __iomem*)(map->virt + (adr ^ 2)) = cpu_to_cfi16(d.x[0]);
+#endif
 }
 
 /*
- * Fast write16 function without the probing check above
+ * Unaligned writes are ignored, causing the 8-bit
+ * probe to fail and proceed to the 16-bit probe (which succeeds).
  */
-static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
+static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr)
 {
-	writew(cpu_to_le16(d.x[0]), map->virt + adr);
+	if (!(adr & 1))
+		ixp4xx_write16(map, d, adr);
 }
 
 struct ixp4xx_flash_info {