upgrade wireless-tools and iproute2
[openwrt.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