fix serial flash support (#6442)
[openwrt/openwrt.git] / target / linux / brcm-2.4 / files / arch / mips / bcm947xx / sflash.c
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
3 *
4 * Copyright 2007, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
11 *
12 * $Id$
13 */
14
15 #include <typedefs.h>
16 #include <osl.h>
17 #include "include/bcmutils.h"
18 #include <sbutils.h>
19 #include <sbconfig.h>
20 #include <sbchipc.h>
21 #include <bcmdevs.h>
22 #include <sflash.h>
23
24 /* Private global state */
25 static struct sflash sflash;
26
27 /* Issue a serial flash command */
28 static INLINE void
29 sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode)
30 {
31 W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode);
32 while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY);
33 }
34
35 /* Initialize serial flash access */
36 struct sflash *
37 sflash_init(sb_t *sbh, chipcregs_t *cc)
38 {
39 uint32 id, id2;
40 osl_t *osh;
41
42 ASSERT(sbh);
43
44 osh = sb_osh(sbh);
45
46 bzero(&sflash, sizeof(sflash));
47
48 sflash.type = sbh->cccaps & CC_CAP_FLASH_MASK;
49
50 switch (sflash.type) {
51 case SFLASH_ST:
52 /* Probe for ST chips */
53 sflash_cmd(osh, cc, SFLASH_ST_DP);
54 sflash_cmd(osh, cc, SFLASH_ST_RES);
55 id = R_REG(osh, &cc->flashdata);
56 switch (id) {
57 case 0x11:
58 /* ST M25P20 2 Mbit Serial Flash */
59 sflash.blocksize = 64 * 1024;
60 sflash.numblocks = 4;
61 break;
62 case 0x12:
63 /* ST M25P40 4 Mbit Serial Flash */
64 sflash.blocksize = 64 * 1024;
65 sflash.numblocks = 8;
66 break;
67 case 0x13:
68 /* ST M25P80 8 Mbit Serial Flash */
69 sflash.blocksize = 64 * 1024;
70 sflash.numblocks = 16;
71 break;
72 case 0x14:
73 /* ST M25P16 16 Mbit Serial Flash */
74 sflash.blocksize = 64 * 1024;
75 sflash.numblocks = 32;
76 break;
77 case 0x15:
78 /* ST M25P32 32 Mbit Serial Flash */
79 sflash.blocksize = 64 * 1024;
80 sflash.numblocks = 64;
81 break;
82 case 0x16:
83 /* ST M25P64 64 Mbit Serial Flash */
84 sflash.blocksize = 64 * 1024;
85 sflash.numblocks = 128;
86 break;
87 case 0xbf:
88 W_REG(osh, &cc->flashaddress, 1);
89 sflash_cmd(osh, cc, SFLASH_ST_RES);
90 id2 = R_REG(osh, &cc->flashdata);
91 if (id2 == 0x44) {
92 /* SST M25VF80 4 Mbit Serial Flash */
93 sflash.blocksize = 64 * 1024;
94 sflash.numblocks = 8;
95 }
96 break;
97 }
98 break;
99
100 case SFLASH_AT:
101 /* Probe for Atmel chips */
102 sflash_cmd(osh, cc, SFLASH_AT_STATUS);
103 id = R_REG(osh, &cc->flashdata) & 0x3c;
104 switch (id) {
105 case 0xc:
106 /* Atmel AT45DB011 1Mbit Serial Flash */
107 sflash.blocksize = 256;
108 sflash.numblocks = 512;
109 break;
110 case 0x14:
111 /* Atmel AT45DB021 2Mbit Serial Flash */
112 sflash.blocksize = 256;
113 sflash.numblocks = 1024;
114 break;
115 case 0x1c:
116 /* Atmel AT45DB041 4Mbit Serial Flash */
117 sflash.blocksize = 256;
118 sflash.numblocks = 2048;
119 break;
120 case 0x24:
121 /* Atmel AT45DB081 8Mbit Serial Flash */
122 sflash.blocksize = 256;
123 sflash.numblocks = 4096;
124 break;
125 case 0x2c:
126 /* Atmel AT45DB161 16Mbit Serial Flash */
127 sflash.blocksize = 512;
128 sflash.numblocks = 4096;
129 break;
130 case 0x34:
131 /* Atmel AT45DB321 32Mbit Serial Flash */
132 sflash.blocksize = 512;
133 sflash.numblocks = 8192;
134 break;
135 case 0x3c:
136 /* Atmel AT45DB642 64Mbit Serial Flash */
137 sflash.blocksize = 1024;
138 sflash.numblocks = 8192;
139 break;
140 }
141 break;
142 }
143
144 sflash.size = sflash.blocksize * sflash.numblocks;
145 return sflash.size ? &sflash : NULL;
146 }
147
148 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
149 int
150 sflash_read(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, uchar *buf)
151 {
152 uint8 *from, *to;
153 int cnt, i;
154 osl_t *osh;
155
156 ASSERT(sbh);
157
158 if (!len)
159 return 0;
160
161 if ((offset + len) > sflash.size)
162 return -22;
163
164 if ((len >= 4) && (offset & 3))
165 cnt = 4 - (offset & 3);
166 else if ((len >= 4) && ((uintptr)buf & 3))
167 cnt = 4 - ((uintptr)buf & 3);
168 else
169 cnt = len;
170
171 osh = sb_osh(sbh);
172
173 from = (uint8 *)(uintptr)OSL_UNCACHED(SB_FLASH2 + offset);
174 to = (uint8 *)buf;
175
176 if (cnt < 4) {
177 for (i = 0; i < cnt; i ++) {
178 *to = R_REG(osh, from);
179 from ++;
180 to ++;
181 }
182 return cnt;
183 }
184
185 while (cnt >= 4) {
186 *(uint32 *)to = R_REG(osh, (uint32 *)from);
187 from += 4;
188 to += 4;
189 cnt -= 4;
190 }
191
192 return (len - cnt);
193 }
194
195 /* Poll for command completion. Returns zero when complete. */
196 int
197 sflash_poll(sb_t *sbh, chipcregs_t *cc, uint offset)
198 {
199 osl_t *osh;
200
201 ASSERT(sbh);
202
203 osh = sb_osh(sbh);
204
205 if (offset >= sflash.size)
206 return -22;
207
208 switch (sflash.type) {
209 case SFLASH_ST:
210 /* Check for ST Write In Progress bit */
211 sflash_cmd(osh, cc, SFLASH_ST_RDSR);
212 return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP;
213 case SFLASH_AT:
214 /* Check for Atmel Ready bit */
215 sflash_cmd(osh, cc, SFLASH_AT_STATUS);
216 return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY);
217 }
218
219 return 0;
220 }
221
222 /* Write len bytes starting at offset into buf. Returns number of bytes
223 * written. Caller should poll for completion.
224 */
225 int
226 sflash_write(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
227 {
228 struct sflash *sfl;
229 int ret = 0;
230 bool is4712b0;
231 uint32 page, byte, mask;
232 osl_t *osh;
233
234 ASSERT(sbh);
235
236 osh = sb_osh(sbh);
237
238 if (!len)
239 return 0;
240
241 if ((offset + len) > sflash.size)
242 return -22;
243
244 sfl = &sflash;
245 switch (sfl->type) {
246 case SFLASH_ST:
247 is4712b0 = (sbh->chip == BCM4712_CHIP_ID) && (sbh->chiprev == 3);
248 /* Enable writes */
249 sflash_cmd(osh, cc, SFLASH_ST_WREN);
250 if (is4712b0) {
251 mask = 1 << 14;
252 W_REG(osh, &cc->flashaddress, offset);
253 W_REG(osh, &cc->flashdata, *buf++);
254 /* Set chip select */
255 OR_REG(osh, &cc->gpioout, mask);
256 /* Issue a page program with the first byte */
257 sflash_cmd(osh, cc, SFLASH_ST_PP);
258 ret = 1;
259 offset++;
260 len--;
261 while (len > 0) {
262 if ((offset & 255) == 0) {
263 /* Page boundary, drop cs and return */
264 AND_REG(osh, &cc->gpioout, ~mask);
265 if (!sflash_poll(sbh, cc, offset)) {
266 /* Flash rejected command */
267 return -11;
268 }
269 return ret;
270 } else {
271 /* Write single byte */
272 sflash_cmd(osh, cc, *buf++);
273 }
274 ret++;
275 offset++;
276 len--;
277 }
278 /* All done, drop cs if needed */
279 if ((offset & 255) != 1) {
280 /* Drop cs */
281 AND_REG(osh, &cc->gpioout, ~mask);
282 if (!sflash_poll(sbh, cc, offset)) {
283 /* Flash rejected command */
284 return -12;
285 }
286 }
287 } else if ( (sbh->ccrev >= 20) && (len != 1) ) {
288 //} else if ( sbh->ccrev >= 20 ) { /* foxconn modified by EricHuang, 05/24/2007 */
289 W_REG(NULL, &cc->flashaddress, offset);
290 W_REG(NULL, &cc->flashdata, *buf++);
291 /* Issue a page program with CSA bit set */
292 sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP);
293 ret = 1;
294 offset++;
295 len--;
296 while (len > 0) {
297 if ((offset & 255) == 0) {
298 /* Page boundary, poll droping cs and return */
299 W_REG(NULL, &cc->flashcontrol, 0);
300 /* wklin added start, 06/08/2007 */
301 W_REG(NULL, &cc->flashcontrol, 0);
302 OSL_DELAY(1);
303 /* wklin added end, 06/08/2007 */
304 /* wklin rmeoved start, 06/08/2007 */
305 #if 0
306 if (!sflash_poll(sbh, cc, offset)) {
307 /* Flash rejected command */
308 return -11;
309 }
310 #endif
311 /* wklin removed end, 06/08/2007 */
312 return ret;
313 } else {
314 /* Write single byte */
315 sflash_cmd(osh, cc, SFLASH_ST_CSA | *buf++);
316 }
317 ret++;
318 offset++;
319 len--;
320 }
321 /* All done, drop cs if needed */
322 if ((offset & 255) != 1) {
323 /* Drop cs, poll */
324 W_REG(NULL, &cc->flashcontrol, 0);
325 /* wklin added start, 06/08/2007 */
326 W_REG(NULL, &cc->flashcontrol, 0);
327 OSL_DELAY(1);
328 /* wklin added end, 06/08/2007 */
329 /* wklin removed start, 06/08/2007 */
330 #if 0
331 if (!sflash_poll(sbh, cc, offset)) {
332 /* Flash rejected command */
333 return -12;
334 }
335 #endif
336 /* wklin removed end, 06/08/2007 */
337 }
338 } else {
339 ret = 1;
340 W_REG(osh, &cc->flashaddress, offset);
341 W_REG(osh, &cc->flashdata, *buf);
342 /* Page program */
343 sflash_cmd(osh, cc, SFLASH_ST_PP);
344 }
345 break;
346 case SFLASH_AT:
347 mask = sfl->blocksize - 1;
348 page = (offset & ~mask) << 1;
349 byte = offset & mask;
350 /* Read main memory page into buffer 1 */
351 if (byte || (len < sfl->blocksize)) {
352 W_REG(osh, &cc->flashaddress, page);
353 sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD);
354 /* 250 us for AT45DB321B */
355 SPINWAIT(sflash_poll(sbh, cc, offset), 1000);
356 ASSERT(!sflash_poll(sbh, cc, offset));
357 }
358 /* Write into buffer 1 */
359 for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
360 W_REG(osh, &cc->flashaddress, byte++);
361 W_REG(osh, &cc->flashdata, *buf++);
362 sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE);
363 }
364 /* Write buffer 1 into main memory page */
365 W_REG(osh, &cc->flashaddress, page);
366 sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM);
367 break;
368 }
369
370 return ret;
371 }
372
373 /* Erase a region. Returns number of bytes scheduled for erasure.
374 * Caller should poll for completion.
375 */
376 int
377 sflash_erase(sb_t *sbh, chipcregs_t *cc, uint offset)
378 {
379 struct sflash *sfl;
380 osl_t *osh;
381
382 ASSERT(sbh);
383
384 osh = sb_osh(sbh);
385
386 if (offset >= sflash.size)
387 return -22;
388
389 sfl = &sflash;
390 switch (sfl->type) {
391 case SFLASH_ST:
392 sflash_cmd(osh, cc, SFLASH_ST_WREN);
393 W_REG(osh, &cc->flashaddress, offset);
394 sflash_cmd(osh, cc, SFLASH_ST_SE);
395 return sfl->blocksize;
396 case SFLASH_AT:
397 W_REG(osh, &cc->flashaddress, offset << 1);
398 sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE);
399 return sfl->blocksize;
400 }
401
402 return 0;
403 }
404
405 /*
406 * writes the appropriate range of flash, a NULL buf simply erases
407 * the region of flash
408 */
409 int
410 sflash_commit(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
411 {
412 struct sflash *sfl;
413 uchar *block = NULL, *cur_ptr, *blk_ptr;
414 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
415 uint blk_offset, blk_len, copied;
416 int bytes, ret = 0;
417 osl_t *osh;
418
419 ASSERT(sbh);
420
421 osh = sb_osh(sbh);
422
423 /* Check address range */
424 if (len <= 0)
425 return 0;
426
427 sfl = &sflash;
428 if ((offset + len) > sfl->size)
429 return -1;
430
431 blocksize = sfl->blocksize;
432 mask = blocksize - 1;
433
434 /* Allocate a block of mem */
435 if (!(block = MALLOC(osh, blocksize)))
436 return -1;
437
438 while (len) {
439 /* Align offset */
440 cur_offset = offset & ~mask;
441 cur_length = blocksize;
442 cur_ptr = block;
443
444 remainder = blocksize - (offset & mask);
445 if (len < remainder)
446 cur_retlen = len;
447 else
448 cur_retlen = remainder;
449
450 /* buf == NULL means erase only */
451 if (buf) {
452 /* Copy existing data into holding block if necessary */
453 if ((offset & mask) || (len < blocksize)) {
454 blk_offset = cur_offset;
455 blk_len = cur_length;
456 blk_ptr = cur_ptr;
457
458 /* Copy entire block */
459 while (blk_len) {
460 copied = sflash_read(sbh, cc, blk_offset, blk_len, blk_ptr);
461 blk_offset += copied;
462 blk_len -= copied;
463 blk_ptr += copied;
464 }
465 }
466
467 /* Copy input data into holding block */
468 memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
469 }
470
471 /* Erase block */
472 if ((ret = sflash_erase(sbh, cc, (uint) cur_offset)) < 0)
473 goto done;
474 while (sflash_poll(sbh, cc, (uint) cur_offset));
475
476 /* buf == NULL means erase only */
477 if (!buf) {
478 offset += cur_retlen;
479 len -= cur_retlen;
480 continue;
481 }
482
483 /* Write holding block */
484 while (cur_length > 0) {
485 if ((bytes = sflash_write(sbh, cc,
486 (uint) cur_offset,
487 (uint) cur_length,
488 (uchar *) cur_ptr)) < 0) {
489 ret = bytes;
490 goto done;
491 }
492 while (sflash_poll(sbh, cc, (uint) cur_offset));
493 cur_offset += bytes;
494 cur_length -= bytes;
495 cur_ptr += bytes;
496 }
497
498 offset += cur_retlen;
499 len -= cur_retlen;
500 buf += cur_retlen;
501 }
502
503 ret = len;
504 done:
505 if (block)
506 MFREE(osh, block, blocksize);
507 return ret;
508 }