upgrade wireless-tools and iproute2
[openwrt/svn-archive/archive.git] / openwrt / package / linux / kernel-source / arch / mips / brcm-boards / bcm947xx / sflash.c
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
3 *
4 * Copyright 2004, 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 <bcmutils.h>
17 #include <osl.h>
18 #include <sbchipc.h>
19 #include <sflash.h>
20
21 /* Private global state */
22 static struct sflash sflash;
23
24 /* Issue a serial flash command */
25 static INLINE void
26 sflash_cmd(chipcregs_t *cc, uint opcode)
27 {
28 W_REG(&cc->flashcontrol, SFLASH_START | opcode);
29 while (R_REG(&cc->flashcontrol) & SFLASH_BUSY);
30 }
31
32 /* Initialize serial flash access */
33 struct sflash *
34 sflash_init(chipcregs_t *cc)
35 {
36 uint32 id, id2;
37
38 bzero(&sflash, sizeof(sflash));
39
40 sflash.type = R_REG(&cc->capabilities) & CAP_FLASH_MASK;
41
42 switch (sflash.type) {
43 case SFLASH_ST:
44 /* Probe for ST chips */
45 sflash_cmd(cc, SFLASH_ST_DP);
46 sflash_cmd(cc, SFLASH_ST_RES);
47 id = R_REG(&cc->flashdata);
48 switch (id) {
49 case 0x11:
50 /* ST M25P20 2 Mbit Serial Flash */
51 sflash.blocksize = 64 * 1024;
52 sflash.numblocks = 4;
53 break;
54 case 0x12:
55 /* ST M25P40 4 Mbit Serial Flash */
56 sflash.blocksize = 64 * 1024;
57 sflash.numblocks = 8;
58 break;
59 case 0x13:
60 /* ST M25P80 8 Mbit Serial Flash */
61 sflash.blocksize = 64 * 1024;
62 sflash.numblocks = 16;
63 break;
64 case 0x14:
65 /* ST M25P16 16 Mbit Serial Flash */
66 sflash.blocksize = 64 * 1024;
67 sflash.numblocks = 32;
68 break;
69 case 0xbf:
70 W_REG(&cc->flashaddress, 1);
71 sflash_cmd(cc, SFLASH_ST_RES);
72 id2 = R_REG(&cc->flashdata);
73 if (id2 == 0x44) {
74 /* SST M25VF80 4 Mbit Serial Flash */
75 sflash.blocksize = 64 * 1024;
76 sflash.numblocks = 8;
77 }
78 break;
79 }
80 break;
81
82 case SFLASH_AT:
83 /* Probe for Atmel chips */
84 sflash_cmd(cc, SFLASH_AT_STATUS);
85 id = R_REG(&cc->flashdata) & 0x3c;
86 switch (id) {
87 case 0x2c:
88 /* Atmel AT45DB161 16Mbit Serial Flash */
89 sflash.blocksize = 512;
90 sflash.numblocks = 4096;
91 break;
92 case 0x34:
93 /* Atmel AT45DB321 32Mbit Serial Flash */
94 sflash.blocksize = 512;
95 sflash.numblocks = 8192;
96 break;
97 case 0x3c:
98 /* Atmel AT45DB642 64Mbit Serial Flash */
99 sflash.blocksize = 1024;
100 sflash.numblocks = 8192;
101 break;
102 }
103 break;
104 }
105
106 sflash.size = sflash.blocksize * sflash.numblocks;
107 return sflash.size ? &sflash : NULL;
108 }
109
110 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
111 int
112 sflash_read(chipcregs_t *cc, uint offset, uint len, uchar *buf)
113 {
114 int cnt;
115 uint32 *from, *to;
116
117 if (!len)
118 return 0;
119
120 if ((offset + len) > sflash.size)
121 return -22;
122
123 if ((len >= 4) && (offset & 3))
124 cnt = 4 - (offset & 3);
125 else if ((len >= 4) && ((uint32)buf & 3))
126 cnt = 4 - ((uint32)buf & 3);
127 else
128 cnt = len;
129
130 from = (uint32 *)(CC_FLASH_BASE + offset);
131 to = (uint32 *)buf;
132
133 if (cnt < 4) {
134 bcopy(from, to, cnt);
135 return cnt;
136 }
137
138 while (cnt >= 4) {
139 *to++ = *from++;
140 cnt -= 4;
141 }
142
143 return (len - cnt);
144 }
145
146 /* Poll for command completion. Returns zero when complete. */
147 int
148 sflash_poll(chipcregs_t *cc, uint offset)
149 {
150 if (offset >= sflash.size)
151 return -22;
152
153 switch (sflash.type) {
154 case SFLASH_ST:
155 /* Check for ST Write In Progress bit */
156 sflash_cmd(cc, SFLASH_ST_RDSR);
157 return R_REG(&cc->flashdata) & SFLASH_ST_WIP;
158 case SFLASH_AT:
159 /* Check for Atmel Ready bit */
160 sflash_cmd(cc, SFLASH_AT_STATUS);
161 return !(R_REG(&cc->flashdata) & SFLASH_AT_READY);
162 }
163
164 return 0;
165 }
166
167 /* Write len bytes starting at offset into buf. Returns number of bytes
168 * written. Caller should poll for completion.
169 */
170 int
171 sflash_write(chipcregs_t *cc, uint offset, uint len, const uchar *buf)
172 {
173 struct sflash *sfl;
174 int ret = 0;
175 uint32 page, byte, mask;
176
177 if (!len)
178 return 0;
179
180 if ((offset + len) > sflash.size)
181 return -22;
182
183 sfl = &sflash;
184 switch (sfl->type) {
185 case SFLASH_ST:
186 ret = 1;
187 /* Enable writes */
188 sflash_cmd(cc, SFLASH_ST_WREN);
189 W_REG(&cc->flashaddress, offset);
190 W_REG(&cc->flashdata, *buf);
191 /* Page program */
192 sflash_cmd(cc, SFLASH_ST_PP);
193 break;
194 case SFLASH_AT:
195 mask = sfl->blocksize - 1;
196 page = (offset & ~mask) << 1;
197 byte = offset & mask;
198 /* Read main memory page into buffer 1 */
199 if (byte || len < sfl->blocksize) {
200 W_REG(&cc->flashaddress, page);
201 sflash_cmd(cc, SFLASH_AT_BUF1_LOAD);
202 /* 250 us for AT45DB321B */
203 SPINWAIT(sflash_poll(cc, offset), 1000);
204 ASSERT(!sflash_poll(cc, offset));
205 }
206 /* Write into buffer 1 */
207 for (ret = 0; ret < len && byte < sfl->blocksize; ret++) {
208 W_REG(&cc->flashaddress, byte++);
209 W_REG(&cc->flashdata, *buf++);
210 sflash_cmd(cc, SFLASH_AT_BUF1_WRITE);
211 }
212 /* Write buffer 1 into main memory page */
213 W_REG(&cc->flashaddress, page);
214 sflash_cmd(cc, SFLASH_AT_BUF1_PROGRAM);
215 break;
216 }
217
218 return ret;
219 }
220
221 /* Erase a region. Returns number of bytes scheduled for erasure.
222 * Caller should poll for completion.
223 */
224 int
225 sflash_erase(chipcregs_t *cc, uint offset)
226 {
227 struct sflash *sfl;
228
229 if (offset >= sflash.size)
230 return -22;
231
232 sfl = &sflash;
233 switch (sfl->type) {
234 case SFLASH_ST:
235 sflash_cmd(cc, SFLASH_ST_WREN);
236 W_REG(&cc->flashaddress, offset);
237 sflash_cmd(cc, SFLASH_ST_SE);
238 return sfl->blocksize;
239 case SFLASH_AT:
240 W_REG(&cc->flashaddress, offset << 1);
241 sflash_cmd(cc, SFLASH_AT_PAGE_ERASE);
242 return sfl->blocksize;
243 }
244
245 return 0;
246 }
247
248 /*
249 * writes the appropriate range of flash, a NULL buf simply erases
250 * the region of flash
251 */
252 int
253 sflash_commit(chipcregs_t *cc, uint offset, uint len, const uchar *buf)
254 {
255 struct sflash *sfl;
256 uchar *block = NULL, *cur_ptr, *blk_ptr;
257 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
258 uint blk_offset, blk_len, copied;
259 int bytes, ret = 0;
260
261 /* Check address range */
262 if (len <= 0)
263 return 0;
264
265 sfl = &sflash;
266 if ((offset + len) > sfl->size)
267 return -1;
268
269 blocksize = sfl->blocksize;
270 mask = blocksize - 1;
271
272 /* Allocate a block of mem */
273 if (!(block = MALLOC(blocksize)))
274 return -1;
275
276 while (len) {
277 /* Align offset */
278 cur_offset = offset & ~mask;
279 cur_length = blocksize;
280 cur_ptr = block;
281
282 remainder = blocksize - (offset & mask);
283 if (len < remainder)
284 cur_retlen = len;
285 else
286 cur_retlen = remainder;
287
288 /* buf == NULL means erase only */
289 if (buf) {
290 /* Copy existing data into holding block if necessary */
291 if ((offset & mask) || (len < blocksize)) {
292 blk_offset = cur_offset;
293 blk_len = cur_length;
294 blk_ptr = cur_ptr;
295
296 /* Copy entire block */
297 while(blk_len) {
298 copied = sflash_read(cc, blk_offset, blk_len, blk_ptr);
299 blk_offset += copied;
300 blk_len -= copied;
301 blk_ptr += copied;
302 }
303 }
304
305 /* Copy input data into holding block */
306 memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
307 }
308
309 /* Erase block */
310 if ((ret = sflash_erase(cc, (uint) cur_offset)) < 0)
311 goto done;
312 while (sflash_poll(cc, (uint) cur_offset));
313
314 /* buf == NULL means erase only */
315 if (!buf) {
316 offset += cur_retlen;
317 len -= cur_retlen;
318 continue;
319 }
320
321 /* Write holding block */
322 while (cur_length > 0) {
323 if ((bytes = sflash_write(cc,
324 (uint) cur_offset,
325 (uint) cur_length,
326 (uchar *) cur_ptr)) < 0) {
327 ret = bytes;
328 goto done;
329 }
330 while (sflash_poll(cc, (uint) cur_offset));
331 cur_offset += bytes;
332 cur_length -= bytes;
333 cur_ptr += bytes;
334 }
335
336 offset += cur_retlen;
337 len -= cur_retlen;
338 buf += cur_retlen;
339 }
340
341 done:
342 if (block)
343 MFREE(block, blocksize);
344 return ret;
345 }
346