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