summaryrefslogtreecommitdiffstats
path: root/target/linux/brcm47xx/patches-3.6/070-bcma-add-functions-to-write-to-serial-flash.patch
blob: 98cd795a5260b40871ecb5dc775e9d4afab818f9 (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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
--- a/drivers/bcma/driver_chipcommon_sflash.c
+++ b/drivers/bcma/driver_chipcommon_sflash.c
@@ -1,15 +1,22 @@
 /*
  * Broadcom specific AMBA
  * ChipCommon serial flash interface
+ * Copyright 2011, Jonas Gorski <jonas.gorski@gmail.com>
+ * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2010, Broadcom Corporation
  *
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
 #include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_driver_chipcommon.h>
 
 #include "bcma_private.h"
 
+#define NUM_RETRIES	3
+
 static struct resource bcma_sflash_resource = {
 	.name	= "bcma_sflash",
 	.start	= BCMA_SOC_FLASH2,
@@ -18,7 +25,7 @@ static struct resource bcma_sflash_resou
 };
 
 struct platform_device bcma_sflash_dev = {
-	.name		= "bcma_sflash",
+	.name		= "bcm47xx-sflash",
 	.resource	= &bcma_sflash_resource,
 	.num_resources	= 1,
 };
@@ -30,7 +37,7 @@ struct bcma_sflash_tbl_e {
 	u16 numblocks;
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
 	{ "M25P20", 0x11, 0x10000, 4, },
 	{ "M25P40", 0x12, 0x10000, 8, },
 
@@ -41,7 +48,7 @@ static struct bcma_sflash_tbl_e bcma_sfl
 	{ 0 },
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
 	{ "SST25WF512", 1, 0x1000, 16, },
 	{ "SST25VF512", 0x48, 0x1000, 16, },
 	{ "SST25WF010", 2, 0x1000, 32, },
@@ -59,7 +66,7 @@ static struct bcma_sflash_tbl_e bcma_sfl
 	{ 0 },
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
 	{ "AT45DB011", 0xc, 256, 512, },
 	{ "AT45DB021", 0x14, 256, 1024, },
 	{ "AT45DB041", 0x1c, 256, 2048, },
@@ -84,12 +91,230 @@ static void bcma_sflash_cmd(struct bcma_
 	bcma_err(cc->core->bus, "SFLASH control command failed (timeout)!\n");
 }
 
+static void bcma_sflash_write_u8(struct bcma_drv_cc *cc, u32 offset, u8 byte)
+{
+	bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset);
+	bcma_cc_write32(cc, BCMA_CC_FLASHDATA, byte);
+}
+
+/* Read len bytes starting at offset into buf. Returns number of bytes read. */
+static int bcma_sflash_read(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf)
+{
+	u8 *from, *to;
+	u32 cnt, i;
+	struct bcma_drv_cc *cc = dev->bcc;
+
+	if (!len)
+		return 0;
+
+	if ((offset + len) > cc->sflash.size)
+		return -EINVAL;
+
+	if ((len >= 4) && (offset & 3))
+		cnt = 4 - (offset & 3);
+	else if ((len >= 4) && ((u32)buf & 3))
+		cnt = 4 - ((u32)buf & 3);
+	else
+		cnt = len;
+
+	from = (u8 *)KSEG0ADDR(BCMA_SOC_FLASH2 + offset);
+
+	to = (u8 *)buf;
+
+	if (cnt < 4) {
+		for (i = 0; i < cnt; i++) {
+			*to = readb(from);
+			from++;
+			to++;
+		}
+		return cnt;
+	}
+
+	while (cnt >= 4) {
+		*(u32 *)to = readl(from);
+		from += 4;
+		to += 4;
+		cnt -= 4;
+	}
+
+	return len - cnt;
+}
+
+/* Poll for command completion. Returns zero when complete. */
+static int bcma_sflash_poll(struct bcm47xx_sflash *dev, u32 offset)
+{
+	struct bcma_drv_cc *cc = dev->bcc;
+
+	if (offset >= cc->sflash.size)
+		return -22;
+
+	switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
+	case BCMA_CC_FLASHT_STSER:
+		/* Check for ST Write In Progress bit */
+		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RDSR);
+		return bcma_cc_read32(cc, BCMA_CC_FLASHDATA)
+				& BCMA_CC_FLASHDATA_ST_WIP;
+	case BCMA_CC_FLASHT_ATSER:
+		/* Check for Atmel Ready bit */
+		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_STATUS);
+		return !(bcma_cc_read32(cc, BCMA_CC_FLASHDATA)
+				& BCMA_CC_FLASHDATA_AT_READY);
+	}
+
+	return 0;
+}
+
+
+static int sflash_st_write(struct bcm47xx_sflash *dev, u32 offset, u32 len,
+			   const u8 *buf)
+{
+	int written = 1;
+	struct bcma_drv_cc *cc = dev->bcc;
+
+	/* Enable writes */
+	bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_WREN);
+	bcma_sflash_write_u8(cc, offset, *buf++);
+	/* Issue a page program with CSA bit set */
+	bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_CSA | BCMA_CC_FLASHCTL_ST_PP);
+	offset++;
+	len--;
+	while (len > 0) {
+		if ((offset & 255) == 0) {
+			/* Page boundary, poll droping cs and return */
+			bcma_cc_write32(cc, BCMA_CC_FLASHCTL, 0);
+			udelay(1);
+			if (!bcma_sflash_poll(dev, offset)) {
+				/* Flash rejected command */
+				return -EAGAIN;
+			}
+			return written;
+		} else {
+			/* Write single byte */
+			bcma_sflash_cmd(cc,
+					BCMA_CC_FLASHCTL_ST_CSA |
+					*buf++);
+		}
+		written++;
+		offset++;
+		len--;
+	}
+	/* All done, drop cs & poll */
+	bcma_cc_write32(cc, BCMA_CC_FLASHCTL, 0);
+	udelay(1);
+	if (!bcma_sflash_poll(dev, offset)) {
+		/* Flash rejected command */
+		return -EAGAIN;
+	}
+	return written;
+}
+
+static int sflash_at_write(struct bcm47xx_sflash *dev, u32 offset, u32 len,
+			   const u8 *buf)
+{
+	struct bcma_drv_cc *cc = dev->bcc;
+	u32 page, byte, mask;
+	int ret = 0;
+
+	mask = dev->blocksize - 1;
+	page = (offset & ~mask) << 1;
+	byte = offset & mask;
+	/* Read main memory page into buffer 1 */
+	if (byte || (len < dev->blocksize)) {
+		int i = 100;
+		bcma_cc_write32(cc, BCMA_CC_FLASHADDR, page);
+		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_LOAD);
+		/* 250 us for AT45DB321B */
+		while (i > 0 && bcma_sflash_poll(dev, offset)) {
+			udelay(10);
+			i--;
+		}
+		BUG_ON(!bcma_sflash_poll(dev, offset));
+	}
+	/* Write into buffer 1 */
+	for (ret = 0; (ret < (int)len) && (byte < dev->blocksize); ret++) {
+		bcma_sflash_write_u8(cc, byte++, *buf++);
+		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_WRITE);
+	}
+	/* Write buffer 1 into main memory page */
+	bcma_cc_write32(cc, BCMA_CC_FLASHADDR, page);
+	bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_PROGRAM);
+
+	return ret;
+}
+
+/* Write len bytes starting at offset into buf. Returns number of bytes
+ * written. Caller should poll for completion.
+ */
+static int bcma_sflash_write(struct bcm47xx_sflash *dev, u32 offset, u32 len,
+		      const u8 *buf)
+{
+	int ret = 0, tries = NUM_RETRIES;
+	struct bcma_drv_cc *cc = dev->bcc;
+
+	if (!len)
+		return 0;
+
+	if ((offset + len) > cc->sflash.size)
+		return -EINVAL;
+
+	switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
+	case BCMA_CC_FLASHT_STSER:
+		do {
+			ret = sflash_st_write(dev, offset, len, buf);
+			tries--;
+		} while (ret == -EAGAIN && tries > 0);
+
+		if (ret == -EAGAIN && tries == 0) {
+			bcma_info(cc->core->bus, "ST Flash rejected write\n");
+			ret = -EIO;
+		}
+		break;
+	case BCMA_CC_FLASHT_ATSER:
+		ret = sflash_at_write(dev, offset, len, buf);
+		break;
+	}
+
+	return ret;
+}
+
+/* Erase a region. Returns number of bytes scheduled for erasure.
+ * Caller should poll for completion.
+ */
+static int bcma_sflash_erase(struct bcm47xx_sflash *dev, u32 offset)
+{
+	struct bcma_drv_cc *cc = dev->bcc;
+
+	if (offset >= cc->sflash.size)
+		return -EINVAL;
+
+	switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
+	case BCMA_CC_FLASHT_STSER:
+		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_WREN);
+		bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset);
+		/* Newer flashes have "sub-sectors" which can be erased independently
+		 * with a new command: ST_SSE. The ST_SE command erases 64KB just as
+		 * before.
+		 */
+		if (dev->blocksize < (64 * 1024))
+			bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_SSE);
+		else
+			bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_SE);
+		return dev->blocksize;
+	case BCMA_CC_FLASHT_ATSER:
+		bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset << 1);
+		bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_PAGE_ERASE);
+		return dev->blocksize;
+	}
+
+	return 0;
+}
+
 /* Initialize serial flash access */
 int bcma_sflash_init(struct bcma_drv_cc *cc)
 {
 	struct bcma_bus *bus = cc->core->bus;
-	struct bcma_sflash *sflash = &cc->sflash;
-	struct bcma_sflash_tbl_e *e;
+	struct bcm47xx_sflash *sflash = &cc->sflash;
+	const struct bcma_sflash_tbl_e *e;
 	u32 id, id2;
 
 	switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
@@ -150,6 +375,12 @@ int bcma_sflash_init(struct bcma_drv_cc
 	sflash->numblocks = e->numblocks;
 	sflash->size = sflash->blocksize * sflash->numblocks;
 	sflash->present = true;
+	sflash->read = bcma_sflash_read;
+	sflash->poll = bcma_sflash_poll;
+	sflash->write = bcma_sflash_write;
+	sflash->erase = bcma_sflash_erase;
+	sflash->type = BCM47XX_SFLASH_BCMA;
+	sflash->bcc = cc;
 
 	bcma_info(bus, "Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n",
 		  e->name, sflash->size / 1024, sflash->blocksize,
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -4,6 +4,8 @@
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 
+#include <linux/mtd/bcm47xx_sflash.h>
+
 /** ChipCommon core registers. **/
 #define BCMA_CC_ID			0x0000
 #define  BCMA_CC_ID_ID			0x0000FFFF
@@ -519,17 +521,6 @@ struct bcma_pflash {
 	u32 window_size;
 };
 
-#ifdef CONFIG_BCMA_SFLASH
-struct bcma_sflash {
-	bool present;
-	u32 window;
-	u32 blocksize;
-	u16 numblocks;
-	u32 size;
-
-	struct mtd_info *mtd;
-};
-#endif
 
 #ifdef CONFIG_BCMA_NFLASH
 struct mtd_info;
@@ -564,7 +555,7 @@ struct bcma_drv_cc {
 #ifdef CONFIG_BCMA_DRIVER_MIPS
 	struct bcma_pflash pflash;
 #ifdef CONFIG_BCMA_SFLASH
-	struct bcma_sflash sflash;
+	struct bcm47xx_sflash sflash;
 #endif
 #ifdef CONFIG_BCMA_NFLASH
 	struct bcma_nflash nflash;