[kernel] generic-2.6/2.6.24: refresh 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 * $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 }
This page took 0.0722120000000001 seconds and 5 git commands to generate.