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