ND 1.24 updates
[tomato.git] / release / src / shared / sflash.c
blobd2b4c1039ef61b38c6b25fb213663813288a4417
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright 2005, 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.
12 * $Id: sflash.c,v 1.1.1.11 2005/03/07 07:31:12 kanki Exp $
15 #include <typedefs.h>
16 #include <osl.h>
17 #include <sbconfig.h>
18 #include <sbchipc.h>
19 #include <mipsinc.h>
20 #include <bcmutils.h>
21 #include <bcmdevs.h>
22 #include <sflash.h>
24 /* Private global state */
25 static struct sflash sflash;
27 /* Issue a serial flash command */
28 static INLINE void
29 sflash_cmd(chipcregs_t *cc, uint opcode)
31 W_REG(&cc->flashcontrol, SFLASH_START | opcode);
32 while (R_REG(&cc->flashcontrol) & SFLASH_BUSY);
35 /* Initialize serial flash access */
36 struct sflash *
37 sflash_init(chipcregs_t *cc)
39 uint32 id, id2;
41 bzero(&sflash, sizeof(sflash));
43 sflash.type = R_REG(&cc->capabilities) & CAP_FLASH_MASK;
45 switch (sflash.type) {
46 case SFLASH_ST:
47 /* Probe for ST chips */
48 sflash_cmd(cc, SFLASH_ST_DP);
49 sflash_cmd(cc, SFLASH_ST_RES);
50 id = R_REG(&cc->flashdata);
51 switch (id) {
52 case 0x11:
53 /* ST M25P20 2 Mbit Serial Flash */
54 sflash.blocksize = 64 * 1024;
55 sflash.numblocks = 4;
56 break;
57 case 0x12:
58 /* ST M25P40 4 Mbit Serial Flash */
59 sflash.blocksize = 64 * 1024;
60 sflash.numblocks = 8;
61 break;
62 case 0x13:
63 /* ST M25P80 8 Mbit Serial Flash */
64 sflash.blocksize = 64 * 1024;
65 sflash.numblocks = 16;
66 break;
67 case 0x14:
68 /* ST M25P16 16 Mbit Serial Flash */
69 sflash.blocksize = 64 * 1024;
70 sflash.numblocks = 32;
71 break;
72 case 0x15:
73 /* ST M25P32 32 Mbit Serial Flash */
74 sflash.blocksize = 64 * 1024;
75 sflash.numblocks = 64;
76 break;
77 case 0xbf:
78 W_REG(&cc->flashaddress, 1);
79 sflash_cmd(cc, SFLASH_ST_RES);
80 id2 = R_REG(&cc->flashdata);
81 if (id2 == 0x44) {
82 /* SST M25VF80 4 Mbit Serial Flash */
83 sflash.blocksize = 64 * 1024;
84 sflash.numblocks = 8;
86 break;
88 break;
90 case SFLASH_AT:
91 /* Probe for Atmel chips */
92 sflash_cmd(cc, SFLASH_AT_STATUS);
93 id = R_REG(&cc->flashdata) & 0x3c;
94 switch (id) {
95 case 0xc:
96 /* Atmel AT45DB011 1Mbit Serial Flash */
97 sflash.blocksize = 256;
98 sflash.numblocks = 512;
99 break;
100 case 0x14:
101 /* Atmel AT45DB021 2Mbit Serial Flash */
102 sflash.blocksize = 256;
103 sflash.numblocks = 1024;
104 break;
105 case 0x1c:
106 /* Atmel AT45DB041 4Mbit Serial Flash */
107 sflash.blocksize = 256;
108 sflash.numblocks = 2048;
109 break;
110 case 0x24:
111 /* Atmel AT45DB081 8Mbit Serial Flash */
112 sflash.blocksize = 256;
113 sflash.numblocks = 4096;
114 break;
115 case 0x2c:
116 /* Atmel AT45DB161 16Mbit Serial Flash */
117 sflash.blocksize = 512;
118 sflash.numblocks = 4096;
119 break;
120 case 0x34:
121 /* Atmel AT45DB321 32Mbit Serial Flash */
122 sflash.blocksize = 512;
123 sflash.numblocks = 8192;
124 break;
125 case 0x3c:
126 /* Atmel AT45DB642 64Mbit Serial Flash */
127 sflash.blocksize = 1024;
128 sflash.numblocks = 8192;
129 break;
131 break;
134 sflash.size = sflash.blocksize * sflash.numblocks;
135 return sflash.size ? &sflash : NULL;
138 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
140 sflash_read(chipcregs_t *cc, uint offset, uint len, uchar *buf)
142 int cnt;
143 uint32 *from, *to;
145 if (!len)
146 return 0;
148 if ((offset + len) > sflash.size)
149 return -22;
151 if ((len >= 4) && (offset & 3))
152 cnt = 4 - (offset & 3);
153 else if ((len >= 4) && ((uint32)buf & 3))
154 cnt = 4 - ((uint32)buf & 3);
155 else
156 cnt = len;
158 from = (uint32 *)KSEG1ADDR(SB_FLASH2 + offset);
159 to = (uint32 *)buf;
161 if (cnt < 4) {
162 bcopy(from, to, cnt);
163 return cnt;
166 while (cnt >= 4) {
167 *to++ = *from++;
168 cnt -= 4;
171 return (len - cnt);
174 /* Poll for command completion. Returns zero when complete. */
176 sflash_poll(chipcregs_t *cc, uint offset)
178 if (offset >= sflash.size)
179 return -22;
181 switch (sflash.type) {
182 case SFLASH_ST:
183 /* Check for ST Write In Progress bit */
184 sflash_cmd(cc, SFLASH_ST_RDSR);
185 return R_REG(&cc->flashdata) & SFLASH_ST_WIP;
186 case SFLASH_AT:
187 /* Check for Atmel Ready bit */
188 sflash_cmd(cc, SFLASH_AT_STATUS);
189 return !(R_REG(&cc->flashdata) & SFLASH_AT_READY);
192 return 0;
195 /* Write len bytes starting at offset into buf. Returns number of bytes
196 * written. Caller should poll for completion.
199 sflash_write(chipcregs_t *cc, uint offset, uint len, const uchar *buf)
201 struct sflash *sfl;
202 int ret = 0;
203 bool is4712b0;
204 uint32 page, byte, mask;
206 if (!len)
207 return 0;
209 if ((offset + len) > sflash.size)
210 return -22;
212 sfl = &sflash;
213 switch (sfl->type) {
214 case SFLASH_ST:
215 mask = R_REG(&cc->chipid);
216 is4712b0 = (((mask & CID_ID_MASK) == BCM4712_DEVICE_ID) &&
217 ((mask & CID_REV_MASK) == (3 << CID_REV_SHIFT)));
218 /* Enable writes */
219 sflash_cmd(cc, SFLASH_ST_WREN);
220 if (is4712b0) {
221 mask = 1 << 14;
222 W_REG(&cc->flashaddress, offset);
223 W_REG(&cc->flashdata, *buf++);
224 /* Set chip select */
225 OR_REG(&cc->gpioout, mask);
226 /* Issue a page program with the first byte */
227 sflash_cmd(cc, SFLASH_ST_PP);
228 ret = 1;
229 offset++;
230 len--;
231 while (len > 0) {
232 if ((offset & 255) == 0) {
233 /* Page boundary, drop cs and return */
234 AND_REG(&cc->gpioout, ~mask);
235 if (!sflash_poll(cc, offset)) {
236 /* Flash rejected command */
237 return -11;
239 return ret;
240 } else {
241 /* Write single byte */
242 sflash_cmd(cc, *buf++);
244 ret++;
245 offset++;
246 len--;
248 /* All done, drop cs if needed */
249 if ((offset & 255) != 1) {
250 /* Drop cs */
251 AND_REG(&cc->gpioout, ~mask);
252 if (!sflash_poll(cc, offset)) {
253 /* Flash rejected command */
254 return -12;
257 } else {
258 ret = 1;
259 W_REG(&cc->flashaddress, offset);
260 W_REG(&cc->flashdata, *buf);
261 /* Page program */
262 sflash_cmd(cc, SFLASH_ST_PP);
264 break;
265 case SFLASH_AT:
266 mask = sfl->blocksize - 1;
267 page = (offset & ~mask) << 1;
268 byte = offset & mask;
269 /* Read main memory page into buffer 1 */
270 if (byte || len < sfl->blocksize) {
271 W_REG(&cc->flashaddress, page);
272 sflash_cmd(cc, SFLASH_AT_BUF1_LOAD);
273 /* 250 us for AT45DB321B */
274 SPINWAIT(sflash_poll(cc, offset), 1000);
275 ASSERT(!sflash_poll(cc, offset));
277 /* Write into buffer 1 */
278 for (ret = 0; ret < len && byte < sfl->blocksize; ret++) {
279 W_REG(&cc->flashaddress, byte++);
280 W_REG(&cc->flashdata, *buf++);
281 sflash_cmd(cc, SFLASH_AT_BUF1_WRITE);
283 /* Write buffer 1 into main memory page */
284 W_REG(&cc->flashaddress, page);
285 sflash_cmd(cc, SFLASH_AT_BUF1_PROGRAM);
286 break;
289 return ret;
292 /* Erase a region. Returns number of bytes scheduled for erasure.
293 * Caller should poll for completion.
296 sflash_erase(chipcregs_t *cc, uint offset)
298 struct sflash *sfl;
300 if (offset >= sflash.size)
301 return -22;
303 sfl = &sflash;
304 switch (sfl->type) {
305 case SFLASH_ST:
306 sflash_cmd(cc, SFLASH_ST_WREN);
307 W_REG(&cc->flashaddress, offset);
308 sflash_cmd(cc, SFLASH_ST_SE);
309 return sfl->blocksize;
310 case SFLASH_AT:
311 W_REG(&cc->flashaddress, offset << 1);
312 sflash_cmd(cc, SFLASH_AT_PAGE_ERASE);
313 return sfl->blocksize;
316 return 0;
320 * writes the appropriate range of flash, a NULL buf simply erases
321 * the region of flash
324 sflash_commit(chipcregs_t *cc, uint offset, uint len, const uchar *buf)
326 struct sflash *sfl;
327 uchar *block = NULL, *cur_ptr, *blk_ptr;
328 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
329 uint blk_offset, blk_len, copied;
330 int bytes, ret = 0;
331 void *osh;
333 /* Check address range */
334 if (len <= 0)
335 return 0;
337 sfl = &sflash;
338 if ((offset + len) > sfl->size)
339 return -1;
341 blocksize = sfl->blocksize;
342 mask = blocksize - 1;
344 /* get kernel osl handler */
345 osh = osl_attach(NULL);
347 /* Allocate a block of mem */
348 if (!(block = MALLOC(osh, blocksize)))
349 return -1;
351 while (len) {
352 /* Align offset */
353 cur_offset = offset & ~mask;
354 cur_length = blocksize;
355 cur_ptr = block;
357 remainder = blocksize - (offset & mask);
358 if (len < remainder)
359 cur_retlen = len;
360 else
361 cur_retlen = remainder;
363 /* buf == NULL means erase only */
364 if (buf) {
365 /* Copy existing data into holding block if necessary */
366 if ((offset & mask) || (len < blocksize)) {
367 blk_offset = cur_offset;
368 blk_len = cur_length;
369 blk_ptr = cur_ptr;
371 /* Copy entire block */
372 while(blk_len) {
373 copied = sflash_read(cc, blk_offset, blk_len, blk_ptr);
374 blk_offset += copied;
375 blk_len -= copied;
376 blk_ptr += copied;
380 /* Copy input data into holding block */
381 memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
384 /* Erase block */
385 if ((ret = sflash_erase(cc, (uint) cur_offset)) < 0)
386 goto done;
387 while (sflash_poll(cc, (uint) cur_offset));
389 /* buf == NULL means erase only */
390 if (!buf) {
391 offset += cur_retlen;
392 len -= cur_retlen;
393 continue;
396 /* Write holding block */
397 while (cur_length > 0) {
398 if ((bytes = sflash_write(cc,
399 (uint) cur_offset,
400 (uint) cur_length,
401 (uchar *) cur_ptr)) < 0) {
402 ret = bytes;
403 goto done;
405 while (sflash_poll(cc, (uint) cur_offset));
406 cur_offset += bytes;
407 cur_length -= bytes;
408 cur_ptr += bytes;
411 offset += cur_retlen;
412 len -= cur_retlen;
413 buf += cur_retlen;
416 ret = len;
417 done:
418 if (block)
419 MFREE(osh, block, blocksize);
420 return ret;