convert brcm-2.4 to the new target structure
[openwrt/staging/yousong.git] / target / linux / brcm-2.4 / files / arch / mips / bcm947xx / sflash.c
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
3 *
4 * Copyright 2006, 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: sflash.c,v 1.1.1.13 2006/02/27 03:43:16 honor Exp $
13 */
14
15 #include <osl.h>
16 #include <typedefs.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>
23
24 /* Private global state */
25 static struct sflash sflash;
26
27 /* Issue a serial flash command */
28 static INLINE void
29 sflash_cmd(chipcregs_t *cc, uint opcode)
30 {
31 W_REG(NULL, &cc->flashcontrol, SFLASH_START | opcode);
32 while (R_REG(NULL, &cc->flashcontrol) & SFLASH_BUSY);
33 }
34
35 /* Initialize serial flash access */
36 struct sflash *
37 sflash_init(chipcregs_t *cc)
38 {
39 uint32 id, id2;
40
41 bzero(&sflash, sizeof(sflash));
42
43 sflash.type = R_REG(NULL, &cc->capabilities) & CAP_FLASH_MASK;
44
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(NULL, &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 0x16:
78 /* ST M25P64 64 Mbit Serial Flash */
79 sflash.blocksize = 64 * 1024;
80 sflash.numblocks = 128;
81 break;
82 case 0xbf:
83 W_REG(NULL, &cc->flashaddress, 1);
84 sflash_cmd(cc, SFLASH_ST_RES);
85 id2 = R_REG(NULL, &cc->flashdata);
86 if (id2 == 0x44) {
87 /* SST M25VF80 4 Mbit Serial Flash */
88 sflash.blocksize = 64 * 1024;
89 sflash.numblocks = 8;
90 }
91 break;
92 }
93 break;
94
95 case SFLASH_AT:
96 /* Probe for Atmel chips */
97 sflash_cmd(cc, SFLASH_AT_STATUS);
98 id = R_REG(NULL, &cc->flashdata) & 0x3c;
99 switch (id) {
100 case 0xc:
101 /* Atmel AT45DB011 1Mbit Serial Flash */
102 sflash.blocksize = 256;
103 sflash.numblocks = 512;
104 break;
105 case 0x14:
106 /* Atmel AT45DB021 2Mbit Serial Flash */
107 sflash.blocksize = 256;
108 sflash.numblocks = 1024;
109 break;
110 case 0x1c:
111 /* Atmel AT45DB041 4Mbit Serial Flash */
112 sflash.blocksize = 256;
113 sflash.numblocks = 2048;
114 break;
115 case 0x24:
116 /* Atmel AT45DB081 8Mbit Serial Flash */
117 sflash.blocksize = 256;
118 sflash.numblocks = 4096;
119 break;
120 case 0x2c:
121 /* Atmel AT45DB161 16Mbit Serial Flash */
122 sflash.blocksize = 512;
123 sflash.numblocks = 4096;
124 break;
125 case 0x34:
126 /* Atmel AT45DB321 32Mbit Serial Flash */
127 sflash.blocksize = 512;
128 sflash.numblocks = 8192;
129 break;
130 case 0x3c:
131 /* Atmel AT45DB642 64Mbit Serial Flash */
132 sflash.blocksize = 1024;
133 sflash.numblocks = 8192;
134 break;
135 }
136 break;
137 }
138
139 sflash.size = sflash.blocksize * sflash.numblocks;
140 return sflash.size ? &sflash : NULL;
141 }
142
143 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
144 int
145 sflash_read(chipcregs_t *cc, uint offset, uint len, uchar *buf)
146 {
147 int cnt;
148 uint32 *from, *to;
149
150 if (!len)
151 return 0;
152
153 if ((offset + len) > sflash.size)
154 return -22;
155
156 if ((len >= 4) && (offset & 3))
157 cnt = 4 - (offset & 3);
158 else if ((len >= 4) && ((uint32)buf & 3))
159 cnt = 4 - ((uint32)buf & 3);
160 else
161 cnt = len;
162
163 from = (uint32 *)KSEG1ADDR(SB_FLASH2 + offset);
164 to = (uint32 *)buf;
165
166 if (cnt < 4) {
167 bcopy(from, to, cnt);
168 return cnt;
169 }
170
171 while (cnt >= 4) {
172 *to++ = *from++;
173 cnt -= 4;
174 }
175
176 return (len - cnt);
177 }
178
179 /* Poll for command completion. Returns zero when complete. */
180 int
181 sflash_poll(chipcregs_t *cc, uint offset)
182 {
183 if (offset >= sflash.size)
184 return -22;
185
186 switch (sflash.type) {
187 case SFLASH_ST:
188 /* Check for ST Write In Progress bit */
189 sflash_cmd(cc, SFLASH_ST_RDSR);
190 return R_REG(NULL, &cc->flashdata) & SFLASH_ST_WIP;
191 case SFLASH_AT:
192 /* Check for Atmel Ready bit */
193 sflash_cmd(cc, SFLASH_AT_STATUS);
194 return !(R_REG(NULL, &cc->flashdata) & SFLASH_AT_READY);
195 }
196
197 return 0;
198 }
199
200 /* Write len bytes starting at offset into buf. Returns number of bytes
201 * written. Caller should poll for completion.
202 */
203 int
204 sflash_write(chipcregs_t *cc, uint offset, uint len, const uchar *buf)
205 {
206 struct sflash *sfl;
207 int ret = 0;
208 bool is4712b0;
209 uint32 page, byte, mask;
210
211 if (!len)
212 return 0;
213
214 if ((offset + len) > sflash.size)
215 return -22;
216
217 sfl = &sflash;
218 switch (sfl->type) {
219 case SFLASH_ST:
220 mask = R_REG(NULL, &cc->chipid);
221 is4712b0 = (((mask & CID_ID_MASK) == BCM4712_CHIP_ID) &&
222 ((mask & CID_REV_MASK) == (3 << CID_REV_SHIFT)));
223 /* Enable writes */
224 sflash_cmd(cc, SFLASH_ST_WREN);
225 if (is4712b0) {
226 mask = 1 << 14;
227 W_REG(NULL, &cc->flashaddress, offset);
228 W_REG(NULL, &cc->flashdata, *buf++);
229 /* Set chip select */
230 OR_REG(NULL, &cc->gpioout, mask);
231 /* Issue a page program with the first byte */
232 sflash_cmd(cc, SFLASH_ST_PP);
233 ret = 1;
234 offset++;
235 len--;
236 while (len > 0) {
237 if ((offset & 255) == 0) {
238 /* Page boundary, drop cs and return */
239 AND_REG(NULL, &cc->gpioout, ~mask);
240 if (!sflash_poll(cc, offset)) {
241 /* Flash rejected command */
242 return -11;
243 }
244 return ret;
245 } else {
246 /* Write single byte */
247 sflash_cmd(cc, *buf++);
248 }
249 ret++;
250 offset++;
251 len--;
252 }
253 /* All done, drop cs if needed */
254 if ((offset & 255) != 1) {
255 /* Drop cs */
256 AND_REG(NULL, &cc->gpioout, ~mask);
257 if (!sflash_poll(cc, offset)) {
258 /* Flash rejected command */
259 return -12;
260 }
261 }
262 } else {
263 ret = 1;
264 W_REG(NULL, &cc->flashaddress, offset);
265 W_REG(NULL, &cc->flashdata, *buf);
266 /* Page program */
267 sflash_cmd(cc, SFLASH_ST_PP);
268 }
269 break;
270 case SFLASH_AT:
271 mask = sfl->blocksize - 1;
272 page = (offset & ~mask) << 1;
273 byte = offset & mask;
274 /* Read main memory page into buffer 1 */
275 if (byte || (len < sfl->blocksize)) {
276 W_REG(NULL, &cc->flashaddress, page);
277 sflash_cmd(cc, SFLASH_AT_BUF1_LOAD);
278 /* 250 us for AT45DB321B */
279 SPINWAIT(sflash_poll(cc, offset), 1000);
280 ASSERT(!sflash_poll(cc, offset));
281 }
282 /* Write into buffer 1 */
283 for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
284 W_REG(NULL, &cc->flashaddress, byte++);
285 W_REG(NULL, &cc->flashdata, *buf++);
286 sflash_cmd(cc, SFLASH_AT_BUF1_WRITE);
287 }
288 /* Write buffer 1 into main memory page */
289 W_REG(NULL, &cc->flashaddress, page);
290 sflash_cmd(cc, SFLASH_AT_BUF1_PROGRAM);
291 break;
292 }
293
294 return ret;
295 }
296
297 /* Erase a region. Returns number of bytes scheduled for erasure.
298 * Caller should poll for completion.
299 */
300 int
301 sflash_erase(chipcregs_t *cc, uint offset)
302 {
303 struct sflash *sfl;
304
305 if (offset >= sflash.size)
306 return -22;
307
308 sfl = &sflash;
309 switch (sfl->type) {
310 case SFLASH_ST:
311 sflash_cmd(cc, SFLASH_ST_WREN);
312 W_REG(NULL, &cc->flashaddress, offset);
313 sflash_cmd(cc, SFLASH_ST_SE);
314 return sfl->blocksize;
315 case SFLASH_AT:
316 W_REG(NULL, &cc->flashaddress, offset << 1);
317 sflash_cmd(cc, SFLASH_AT_PAGE_ERASE);
318 return sfl->blocksize;
319 }
320
321 return 0;
322 }
323
324 /*
325 * writes the appropriate range of flash, a NULL buf simply erases
326 * the region of flash
327 */
328 int
329 sflash_commit(chipcregs_t *cc, uint offset, uint len, const uchar *buf)
330 {
331 struct sflash *sfl;
332 uchar *block = NULL, *cur_ptr, *blk_ptr;
333 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
334 uint blk_offset, blk_len, copied;
335 int bytes, ret = 0;
336
337 /* Check address range */
338 if (len <= 0)
339 return 0;
340
341 sfl = &sflash;
342 if ((offset + len) > sfl->size)
343 return -1;
344
345 blocksize = sfl->blocksize;
346 mask = blocksize - 1;
347
348 /* Allocate a block of mem */
349 if (!(block = MALLOC(NULL, blocksize)))
350 return -1;
351
352 while (len) {
353 /* Align offset */
354 cur_offset = offset & ~mask;
355 cur_length = blocksize;
356 cur_ptr = block;
357
358 remainder = blocksize - (offset & mask);
359 if (len < remainder)
360 cur_retlen = len;
361 else
362 cur_retlen = remainder;
363
364 /* buf == NULL means erase only */
365 if (buf) {
366 /* Copy existing data into holding block if necessary */
367 if ((offset & mask) || (len < blocksize)) {
368 blk_offset = cur_offset;
369 blk_len = cur_length;
370 blk_ptr = cur_ptr;
371
372 /* Copy entire block */
373 while (blk_len) {
374 copied = sflash_read(cc, blk_offset, blk_len, blk_ptr);
375 blk_offset += copied;
376 blk_len -= copied;
377 blk_ptr += copied;
378 }
379 }
380
381 /* Copy input data into holding block */
382 memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
383 }
384
385 /* Erase block */
386 if ((ret = sflash_erase(cc, (uint) cur_offset)) < 0)
387 goto done;
388 while (sflash_poll(cc, (uint) cur_offset));
389
390 /* buf == NULL means erase only */
391 if (!buf) {
392 offset += cur_retlen;
393 len -= cur_retlen;
394 continue;
395 }
396
397 /* Write holding block */
398 while (cur_length > 0) {
399 if ((bytes = sflash_write(cc,
400 (uint) cur_offset,
401 (uint) cur_length,
402 (uchar *) cur_ptr)) < 0) {
403 ret = bytes;
404 goto done;
405 }
406 while (sflash_poll(cc, (uint) cur_offset));
407 cur_offset += bytes;
408 cur_length -= bytes;
409 cur_ptr += bytes;
410 }
411
412 offset += cur_retlen;
413 len -= cur_retlen;
414 buf += cur_retlen;
415 }
416
417 ret = len;
418 done:
419 if (block)
420 MFREE(NULL, block, blocksize);
421 return ret;
422 }