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 }
This page took 0.065542 seconds and 5 git commands to generate.