[brcm-2.4] fix serial flash support (#6442)
[10.03/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 }