Root/target/linux/brcm-2.4/files/arch/mips/bcm947xx/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 "include/bcmutils.h"
18#include <sbutils.h>
19#include <sbconfig.h>
20#include <sbchipc.h>
21#include <bcmdevs.h>
22#include <sflash.h>
23
24/* Private global state */
25static struct sflash sflash;
26
27/* Issue a serial flash command */
28static INLINE void
29sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode)
30{
31    W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode);
32    while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY);
33}
34
35/* Initialize serial flash access */
36struct sflash *
37sflash_init(sb_t *sbh, chipcregs_t *cc)
38{
39    uint32 id, id2;
40    osl_t *osh;
41
42    ASSERT(sbh);
43
44    osh = sb_osh(sbh);
45
46    bzero(&sflash, sizeof(sflash));
47
48    sflash.type = sbh->cccaps & CC_CAP_FLASH_MASK;
49
50    switch (sflash.type) {
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        case 0x11:
58            /* ST M25P20 2 Mbit Serial Flash */
59            sflash.blocksize = 64 * 1024;
60            sflash.numblocks = 4;
61            break;
62        case 0x12:
63            /* ST M25P40 4 Mbit Serial Flash */
64            sflash.blocksize = 64 * 1024;
65            sflash.numblocks = 8;
66            break;
67        case 0x13:
68            /* ST M25P80 8 Mbit Serial Flash */
69            sflash.blocksize = 64 * 1024;
70            sflash.numblocks = 16;
71            break;
72        case 0x14:
73            /* ST M25P16 16 Mbit Serial Flash */
74            sflash.blocksize = 64 * 1024;
75            sflash.numblocks = 32;
76            break;
77        case 0x15:
78            /* ST M25P32 32 Mbit Serial Flash */
79            sflash.blocksize = 64 * 1024;
80            sflash.numblocks = 64;
81            break;
82        case 0x16:
83            /* ST M25P64 64 Mbit Serial Flash */
84            sflash.blocksize = 64 * 1024;
85            sflash.numblocks = 128;
86            break;
87        case 0xbf:
88            W_REG(osh, &cc->flashaddress, 1);
89            sflash_cmd(osh, cc, SFLASH_ST_RES);
90            id2 = R_REG(osh, &cc->flashdata);
91            if (id2 == 0x44) {
92                /* SST M25VF80 4 Mbit Serial Flash */
93                sflash.blocksize = 64 * 1024;
94                sflash.numblocks = 8;
95            }
96            break;
97        }
98        break;
99
100    case SFLASH_AT:
101        /* Probe for Atmel chips */
102        sflash_cmd(osh, cc, SFLASH_AT_STATUS);
103        id = R_REG(osh, &cc->flashdata) & 0x3c;
104        switch (id) {
105        case 0xc:
106            /* Atmel AT45DB011 1Mbit Serial Flash */
107            sflash.blocksize = 256;
108            sflash.numblocks = 512;
109            break;
110        case 0x14:
111            /* Atmel AT45DB021 2Mbit Serial Flash */
112            sflash.blocksize = 256;
113            sflash.numblocks = 1024;
114            break;
115        case 0x1c:
116            /* Atmel AT45DB041 4Mbit Serial Flash */
117            sflash.blocksize = 256;
118            sflash.numblocks = 2048;
119            break;
120        case 0x24:
121            /* Atmel AT45DB081 8Mbit Serial Flash */
122            sflash.blocksize = 256;
123            sflash.numblocks = 4096;
124            break;
125        case 0x2c:
126            /* Atmel AT45DB161 16Mbit Serial Flash */
127            sflash.blocksize = 512;
128            sflash.numblocks = 4096;
129            break;
130        case 0x34:
131            /* Atmel AT45DB321 32Mbit Serial Flash */
132            sflash.blocksize = 512;
133            sflash.numblocks = 8192;
134            break;
135        case 0x3c:
136            /* Atmel AT45DB642 64Mbit Serial Flash */
137            sflash.blocksize = 1024;
138            sflash.numblocks = 8192;
139            break;
140        }
141        break;
142    }
143
144    sflash.size = sflash.blocksize * sflash.numblocks;
145    return sflash.size ? &sflash : NULL;
146}
147
148/* Read len bytes starting at offset into buf. Returns number of bytes read. */
149int
150sflash_read(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, uchar *buf)
151{
152    uint8 *from, *to;
153    int cnt, i;
154    osl_t *osh;
155
156    ASSERT(sbh);
157
158    if (!len)
159        return 0;
160
161    if ((offset + len) > sflash.size)
162        return -22;
163
164    if ((len >= 4) && (offset & 3))
165        cnt = 4 - (offset & 3);
166    else if ((len >= 4) && ((uintptr)buf & 3))
167        cnt = 4 - ((uintptr)buf & 3);
168    else
169        cnt = len;
170
171    osh = sb_osh(sbh);
172
173    from = (uint8 *)(uintptr)OSL_UNCACHED(SB_FLASH2 + offset);
174    to = (uint8 *)buf;
175
176    if (cnt < 4) {
177        for (i = 0; i < cnt; i ++) {
178            *to = R_REG(osh, from);
179            from ++;
180            to ++;
181        }
182        return cnt;
183    }
184
185    while (cnt >= 4) {
186        *(uint32 *)to = R_REG(osh, (uint32 *)from);
187        from += 4;
188        to += 4;
189        cnt -= 4;
190    }
191
192    return (len - cnt);
193}
194
195/* Poll for command completion. Returns zero when complete. */
196int
197sflash_poll(sb_t *sbh, chipcregs_t *cc, uint offset)
198{
199    osl_t *osh;
200
201    ASSERT(sbh);
202
203    osh = sb_osh(sbh);
204
205    if (offset >= sflash.size)
206        return -22;
207
208    switch (sflash.type) {
209    case SFLASH_ST:
210        /* Check for ST Write In Progress bit */
211        sflash_cmd(osh, cc, SFLASH_ST_RDSR);
212        return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP;
213    case SFLASH_AT:
214        /* Check for Atmel Ready bit */
215        sflash_cmd(osh, cc, SFLASH_AT_STATUS);
216        return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY);
217    }
218
219    return 0;
220}
221
222/* Write len bytes starting at offset into buf. Returns number of bytes
223 * written. Caller should poll for completion.
224 */
225int
226sflash_write(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
227{
228    struct sflash *sfl;
229    int ret = 0;
230    bool is4712b0;
231    uint32 page, byte, mask;
232    osl_t *osh;
233
234    ASSERT(sbh);
235
236    osh = sb_osh(sbh);
237
238    if (!len)
239        return 0;
240
241    if ((offset + len) > sflash.size)
242        return -22;
243
244    sfl = &sflash;
245    switch (sfl->type) {
246    case SFLASH_ST:
247        is4712b0 = (sbh->chip == BCM4712_CHIP_ID) && (sbh->chiprev == 3);
248        /* Enable writes */
249        sflash_cmd(osh, cc, SFLASH_ST_WREN);
250        if (is4712b0) {
251            mask = 1 << 14;
252            W_REG(osh, &cc->flashaddress, offset);
253            W_REG(osh, &cc->flashdata, *buf++);
254            /* Set chip select */
255            OR_REG(osh, &cc->gpioout, mask);
256            /* Issue a page program with the first byte */
257            sflash_cmd(osh, cc, SFLASH_ST_PP);
258            ret = 1;
259            offset++;
260            len--;
261            while (len > 0) {
262                if ((offset & 255) == 0) {
263                    /* Page boundary, drop cs and return */
264                    AND_REG(osh, &cc->gpioout, ~mask);
265                    if (!sflash_poll(sbh, cc, offset)) {
266                        /* Flash rejected command */
267                        return -11;
268                    }
269                    return ret;
270                } else {
271                    /* Write single byte */
272                    sflash_cmd(osh, cc, *buf++);
273                }
274                ret++;
275                offset++;
276                len--;
277            }
278            /* All done, drop cs if needed */
279            if ((offset & 255) != 1) {
280                /* Drop cs */
281                AND_REG(osh, &cc->gpioout, ~mask);
282                if (!sflash_poll(sbh, cc, offset)) {
283                    /* Flash rejected command */
284                    return -12;
285                }
286            }
287        } else if ( (sbh->ccrev >= 20) && (len != 1) ) {
288        //} else if ( sbh->ccrev >= 20 ) { /* foxconn modified by EricHuang, 05/24/2007 */
289            W_REG(NULL, &cc->flashaddress, offset);
290            W_REG(NULL, &cc->flashdata, *buf++);
291            /* Issue a page program with CSA bit set */
292            sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP);
293            ret = 1;
294            offset++;
295            len--;
296            while (len > 0) {
297                if ((offset & 255) == 0) {
298                    /* Page boundary, poll droping cs and return */
299                    W_REG(NULL, &cc->flashcontrol, 0);
300                    /* wklin added start, 06/08/2007 */
301                    W_REG(NULL, &cc->flashcontrol, 0);
302                    OSL_DELAY(1);
303                    /* wklin added end, 06/08/2007 */
304                    /* wklin rmeoved start, 06/08/2007 */
305#if 0
306                    if (!sflash_poll(sbh, cc, offset)) {
307                        /* Flash rejected command */
308                        return -11;
309                    }
310#endif
311                                        /* wklin removed end, 06/08/2007 */
312                    return ret;
313                } else {
314                    /* Write single byte */
315                    sflash_cmd(osh, cc, SFLASH_ST_CSA | *buf++);
316                }
317                ret++;
318                offset++;
319                len--;
320            }
321            /* All done, drop cs if needed */
322            if ((offset & 255) != 1) {
323                /* Drop cs, poll */
324                W_REG(NULL, &cc->flashcontrol, 0);
325                /* wklin added start, 06/08/2007 */
326                W_REG(NULL, &cc->flashcontrol, 0);
327                OSL_DELAY(1);
328                /* wklin added end, 06/08/2007 */
329                /* wklin removed start, 06/08/2007 */
330#if 0
331                if (!sflash_poll(sbh, cc, offset)) {
332                    /* Flash rejected command */
333                    return -12;
334                }
335#endif
336                                /* wklin removed end, 06/08/2007 */
337            }
338        } else {
339            ret = 1;
340            W_REG(osh, &cc->flashaddress, offset);
341            W_REG(osh, &cc->flashdata, *buf);
342            /* Page program */
343            sflash_cmd(osh, cc, SFLASH_ST_PP);
344        }
345        break;
346    case SFLASH_AT:
347        mask = sfl->blocksize - 1;
348        page = (offset & ~mask) << 1;
349        byte = offset & mask;
350        /* Read main memory page into buffer 1 */
351        if (byte || (len < sfl->blocksize)) {
352            W_REG(osh, &cc->flashaddress, page);
353            sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD);
354            /* 250 us for AT45DB321B */
355            SPINWAIT(sflash_poll(sbh, cc, offset), 1000);
356            ASSERT(!sflash_poll(sbh, cc, offset));
357        }
358        /* Write into buffer 1 */
359        for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
360            W_REG(osh, &cc->flashaddress, byte++);
361            W_REG(osh, &cc->flashdata, *buf++);
362            sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE);
363        }
364        /* Write buffer 1 into main memory page */
365        W_REG(osh, &cc->flashaddress, page);
366        sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM);
367        break;
368    }
369
370    return ret;
371}
372
373/* Erase a region. Returns number of bytes scheduled for erasure.
374 * Caller should poll for completion.
375 */
376int
377sflash_erase(sb_t *sbh, chipcregs_t *cc, uint offset)
378{
379    struct sflash *sfl;
380    osl_t *osh;
381
382    ASSERT(sbh);
383
384    osh = sb_osh(sbh);
385
386    if (offset >= sflash.size)
387        return -22;
388
389    sfl = &sflash;
390    switch (sfl->type) {
391    case SFLASH_ST:
392        sflash_cmd(osh, cc, SFLASH_ST_WREN);
393        W_REG(osh, &cc->flashaddress, offset);
394        sflash_cmd(osh, cc, SFLASH_ST_SE);
395        return sfl->blocksize;
396    case SFLASH_AT:
397        W_REG(osh, &cc->flashaddress, offset << 1);
398        sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE);
399        return sfl->blocksize;
400    }
401
402    return 0;
403}
404
405/*
406 * writes the appropriate range of flash, a NULL buf simply erases
407 * the region of flash
408 */
409int
410sflash_commit(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
411{
412    struct sflash *sfl;
413    uchar *block = NULL, *cur_ptr, *blk_ptr;
414    uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
415    uint blk_offset, blk_len, copied;
416    int bytes, ret = 0;
417    osl_t *osh;
418
419    ASSERT(sbh);
420
421    osh = sb_osh(sbh);
422
423    /* Check address range */
424    if (len <= 0)
425        return 0;
426
427    sfl = &sflash;
428    if ((offset + len) > sfl->size)
429        return -1;
430
431    blocksize = sfl->blocksize;
432    mask = blocksize - 1;
433
434    /* Allocate a block of mem */
435    if (!(block = MALLOC(osh, blocksize)))
436        return -1;
437
438    while (len) {
439        /* Align offset */
440        cur_offset = offset & ~mask;
441        cur_length = blocksize;
442        cur_ptr = block;
443
444        remainder = blocksize - (offset & mask);
445        if (len < remainder)
446            cur_retlen = len;
447        else
448            cur_retlen = remainder;
449
450        /* buf == NULL means erase only */
451        if (buf) {
452            /* Copy existing data into holding block if necessary */
453            if ((offset & mask) || (len < blocksize)) {
454                blk_offset = cur_offset;
455                blk_len = cur_length;
456                blk_ptr = cur_ptr;
457
458                /* Copy entire block */
459                while (blk_len) {
460                    copied = sflash_read(sbh, cc, blk_offset, blk_len, blk_ptr);
461                    blk_offset += copied;
462                    blk_len -= copied;
463                    blk_ptr += copied;
464                }
465            }
466
467            /* Copy input data into holding block */
468            memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
469        }
470
471        /* Erase block */
472        if ((ret = sflash_erase(sbh, cc, (uint) cur_offset)) < 0)
473            goto done;
474        while (sflash_poll(sbh, cc, (uint) cur_offset));
475
476        /* buf == NULL means erase only */
477        if (!buf) {
478            offset += cur_retlen;
479            len -= cur_retlen;
480            continue;
481        }
482
483        /* Write holding block */
484        while (cur_length > 0) {
485            if ((bytes = sflash_write(sbh, cc,
486                                      (uint) cur_offset,
487                                      (uint) cur_length,
488                                      (uchar *) cur_ptr)) < 0) {
489                ret = bytes;
490                goto done;
491            }
492            while (sflash_poll(sbh, cc, (uint) cur_offset));
493            cur_offset += bytes;
494            cur_length -= bytes;
495            cur_ptr += bytes;
496        }
497
498        offset += cur_retlen;
499        len -= cur_retlen;
500        buf += cur_retlen;
501    }
502
503    ret = len;
504done:
505    if (block)
506        MFREE(osh, block, blocksize);
507    return ret;
508}
509

Archive Download this file



interactive