diff options
Diffstat (limited to 'bsps/powerpc/shared/flash/spansionFlash.c')
-rw-r--r-- | bsps/powerpc/shared/flash/spansionFlash.c | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/bsps/powerpc/shared/flash/spansionFlash.c b/bsps/powerpc/shared/flash/spansionFlash.c new file mode 100644 index 0000000000..41d9e3cce2 --- /dev/null +++ b/bsps/powerpc/shared/flash/spansionFlash.c @@ -0,0 +1,477 @@ +/* + * Trivial driver for spansion flash present on the + * MVME3100 board. + * + * For recognized devices, look for 'spansionDevs'. + * + * This driver has only been tested with stride=4 + * and in 16-bit mode (width=2). + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include <rtems.h> +#include <stdio.h> +#include <inttypes.h> + +#include <bsp/flashPgmPvt.h> + +#define DEBUG 5 +#undef DEBUG + +#ifdef DEBUG +#define STATIC +#else +#define STATIC static +#endif + +/* Manual says max erase time is 3.5 s */ +#define ERASE_TIMEOUT 4 /* seconds */ +#define WRITE_TIMEOUT 1000 /* us; manual says: 240us typ. */ + +/* Assume flash-endianness == CPU endianness */ + +#ifdef __PPC__ +#define IOSYNC(mem) do { __asm__ volatile("eieio"); } while (0) +#else +#define IOSYNC(mem) do { } while (0) +#endif + +/********* Forward Declarations ****************/ + +STATIC int +flash_get_id_s160(struct bankdesc *, uint32_t , uint32_t *, uint32_t *); + +STATIC void +flash_unlock_block_s160(struct bankdesc *, uint32_t); + +STATIC void +flash_lock_block_s160(struct bankdesc *, uint32_t); + +STATIC int +flash_erase_block_s160(struct bankdesc *, uint32_t); + +STATIC uint32_t +flash_check_ready_s160(struct bankdesc *, uint32_t); + +STATIC void +flash_print_stat_s160(struct bankdesc *, uint32_t, int); + +STATIC void +flash_array_mode_s160(struct bankdesc *, uint32_t); + +STATIC uint32_t +flash_write_line_s160(struct bankdesc *, uint32_t, const char *, uint32_t); + +/********* Global Variables ********************/ + +static struct flash_bank_ops spansionOps = { + get_id : flash_get_id_s160, + unlock_block: flash_unlock_block_s160, + lock_block : flash_lock_block_s160, + erase_block : flash_erase_block_s160, + check_ready : flash_check_ready_s160, + print_stat : flash_print_stat_s160, + array_mode : flash_array_mode_s160, + write_line : flash_write_line_s160, +}; + +static struct devdesc spansionDevs[] = { + { 0x007e2101, "S29GL128N", 0x01000000, 32, 0x20000 }, /* 16MB */ + { 0x007e2201, "S29GL256N", 0x02000000, 32, 0x20000 }, /* 32MB */ + { 0x007e2301, "S29GL512N", 0x04000000, 32, 0x20000 }, /* 64MB */ + { 0, 0, 0, 0} +}; + +struct vendesc BSP_flash_vendor_spansion[] = { + { 0x01, "Spansion/AMD", spansionDevs, &spansionOps }, + { 0, 0} +}; + +/********* Register Definitions ****************/ + +#define UNLK1_ADDR_16 0x555 +#define UNLK1_DATA 0xaa +#define UNLK2_ADDR_16 0x2aa +#define UNLK2_ADDR_8 0x555 +#define UNLK2_DATA 0x55 +#define ASEL_DATA 0x90 +#define VEND_ID_ADDR_16 0x000 +#define SPROT_ADDR_16 0x002 +#define DEV1_ID_ADDR_16 0x001 +#define DEV2_ID_ADDR_16 0x00e +#define DEV3_ID_ADDR_16 0x00f +#define ERASE_DATA 0x80 +#define SECT_ERASE_DATA 0x30 +#define DQ7_DATA 0x80 +#define RESET_DATA 0xf0 +#define WRBUF_DATA 0x25 +#define PGBUF_DATA 0x29 + +#define DQ7_POLL_ALL (-1) + +/********* Helper Types ************************/ + +union bconv { + uint32_t u; + uint16_t s[2]; + char c[4]; +}; + +/********* Register Access Primitives **********/ + +/* All of these currently assume stride == 4, i.e. + * two 16-bit devices or 4 8-bit devices in parallel. + * + * FIXME: + * 8-bit mode and strides 1,2 untested. + */ + +#define ADDR32(b, a, o) ((a) + ((o)*FLASH_STRIDE(b))) + +static inline uint32_t +fl_rd32(struct bankdesc *b, uint32_t a, uint32_t off) +{ +volatile union bconv *p; +uint32_t rval; + + if ( 1 == b->width ) + off <<= 1; + + a = ADDR32(b, a, off); + + p = (volatile union bconv *)a; + if ( 4 == FLASH_STRIDE(b) ) { + rval = p->u; + IOSYNC(p->u); + } else if ( 2 == FLASH_STRIDE(b) ) { + rval = p->s[0]; + IOSYNC(p->s[0]); + } else { + rval = p->c[0]; + IOSYNC(p->c[0]); + } + return rval; +} + +static inline void +fl_wr32(struct bankdesc *b, uint32_t a, uint32_t v) +{ +volatile union bconv *p = (volatile union bconv*)a; + if ( 4 == FLASH_STRIDE(b) ) { + p->u = v; + IOSYNC(p->u); + } else if ( 2 == FLASH_STRIDE(b) ) { + p->s[0] = v; + IOSYNC(p->s[0]); + } else { + p->c[0] = v; + IOSYNC(p->c[0]); + } +} + +static inline uint32_t +fl_splat32(struct bankdesc *b, uint32_t x) +{ + if ( 4 == FLASH_STRIDE(b) ) { + if ( 1 == b->width ) { + x = (x << 8) | x; + } + x = (x<<16) | x; + } else if ( 2 == FLASH_STRIDE(b) ) { + if ( 1 == b->width ) + x = (x << 8) | x; + } + return x; +} + +static inline uint32_t +fl_x32(struct bankdesc *b, union bconv *pv) +{ + if ( 4 == FLASH_STRIDE(b) ) + return pv->u; + else if ( 2 == FLASH_STRIDE(b) ) + return pv->s[0]; + else + return pv->c[0]; +} + +static inline void +fl_wr32_cmd(struct bankdesc *b, uint32_t a, uint32_t off, uint32_t cmd) +{ + if ( 1 == b->width ) { + if ( off == UNLK2_ADDR_16 ) + off = UNLK2_ADDR_8; + else + /* all others are simply left shifted */ + off <<= 1; + } + cmd = fl_splat32(b, cmd); + a = ADDR32(b, a, off); + fl_wr32(b, a, cmd); +} + +/* Send unlock sequence */ +static inline void unlk(struct bankdesc *b, uint32_t a) +{ + a &= ~ ( ADDR32(b, 0,0x1000) - 1 ); + fl_wr32_cmd(b, a, UNLK1_ADDR_16, UNLK1_DATA); + fl_wr32_cmd(b, a, UNLK2_ADDR_16, UNLK2_DATA); +} + +/********* Helper Routines *********************/ + +STATIC int +sector_is_protected(struct bankdesc *b, uint32_t addr) +{ +int rval; + unlk(b, addr); + fl_wr32_cmd(b, addr, UNLK1_ADDR_16, ASEL_DATA); + rval = fl_rd32(b, addr, SPROT_ADDR_16); + flash_array_mode_s160(b, addr); + return rval; +} + +STATIC int fl_dq7_poll(struct bankdesc *b, uint32_t addr, uint32_t d7_val) +{ + d7_val &= fl_splat32(b, DQ7_DATA); + return ( (fl_rd32(b, addr, 0) & fl_splat32(b, DQ7_DATA)) == d7_val ); +} + +/* Do DQ7 polling until DQ7 reads the value passed in d7_val + * or timeout + */ +STATIC int +flash_pend(struct bankdesc *b, uint32_t addr, uint32_t timeout_us, uint32_t d7_val) +{ +uint32_t then = BSP_flashBspOps.read_us_timer(); +uint32_t now = then; + + do { + if ( fl_dq7_poll(b, addr, d7_val) ) { +#if (DEBUG > 4) + printf("Write buffer succeded after %"PRIi32"us\n", (now-then)*8/333); +#endif + return 0; + } + now = BSP_flashBspOps.read_us_timer(); + } while ( now - then < timeout_us ); + + return -1; +} + + +/********* Access Methods **********************/ + +STATIC void +flash_array_mode_s160(struct bankdesc *b, uint32_t addr) +{ + fl_wr32_cmd(b, addr, 0, RESET_DATA); +} + +STATIC int +flash_get_id_s160(struct bankdesc *b, uint32_t addr, uint32_t *pVendorId, uint32_t *pDeviceId) +{ +uint32_t dev_id[3], x, i; + + if ( 4 != FLASH_STRIDE(b) ) + fprintf(stderr,"Warning: strides other than 4 untested\n(%s at %d)\n", + __FILE__,__LINE__); + + if ( 2 != b->width ) + fprintf(stderr,"Warning: device width other than 2 untested\n(%s at %d)\n", + __FILE__,__LINE__); + + addr &= ~ (ADDR32(b, 0, 0x1000) - 1); + unlk(b, addr); + fl_wr32_cmd(b, addr, UNLK1_ADDR_16, ASEL_DATA); + *pVendorId = fl_rd32(b, addr, VEND_ID_ADDR_16) & 0xff; + dev_id [0] = fl_rd32(b, addr, DEV1_ID_ADDR_16); + dev_id [1] = fl_rd32(b, addr, DEV2_ID_ADDR_16); + dev_id [2] = fl_rd32(b, addr, DEV3_ID_ADDR_16); + +#ifdef DEBUG + printf("Vendor Id 0x%08"PRIx32", Dev Ids: 0x%08"PRIx32", 0x%08"PRIx32", 0x%08"PRIx32"\n", + *pVendorId, dev_id[0], dev_id[1], dev_id[2]); +#endif + + flash_array_mode_s160(b, addr); + + for ( x=0, i=0; i<3; i++ ) { + x = (x<<8) | (dev_id[i] & 0xff); + } + + *pDeviceId = x; + + return 0; +} + + +STATIC void +flash_lock_block_s160(struct bankdesc *b, uint32_t addr) +{ +} + +STATIC void +flash_unlock_block_s160(struct bankdesc *b, uint32_t addr) +{ +} + +STATIC uint32_t +flash_check_ready_s160(struct bankdesc *b, uint32_t addr) +{ + flash_array_mode_s160(b, addr); + return 0; +} + +/* Erase single block holding 'addr'ess + * + * RETURNS: zero on error, device status on failure. + * + * NOTES: - device switched back to array mode on exit. + * - 'addr' must be 32-bit aligned. + */ +STATIC int +flash_erase_block_s160(struct bankdesc *b, uint32_t addr) +{ +rtems_interval p,i; + + addr &= ~ (b->fblksz-1); + + if ( sector_is_protected(b, addr) ) { + fprintf(stderr,"Sector at 0x%08"PRIx32" is protected\n", addr); + return -10; + } + + unlk(b, addr); + fl_wr32_cmd(b, addr, UNLK1_ADDR_16, ERASE_DATA); + unlk(b, addr); + fl_wr32_cmd(b, addr, 0, SECT_ERASE_DATA); + + p = rtems_clock_get_ticks_per_second(); + p *= ERASE_TIMEOUT; + + for ( i=p; i; i-- ) { + rtems_task_wake_after(1); + if ( fl_dq7_poll(b, addr, DQ7_POLL_ALL) ) { + break; + } + } +#ifdef DEBUG + printf("ERASE polled for %"PRIi32" ticks\n", p-i); +#endif + flash_array_mode_s160(b, addr); + + if ( i ) { + /* write successful; verify */ + for ( i = 0; i < b->fblksz; i++ ) { + if ( 0xff != ((char*)addr)[i] ) { + fprintf(stderr,"ERROR: Erase verification failed at %p\n", + ((char*)addr) + i); + return -1; + } + } + return 0; + } + return -1; +} + +STATIC void +flash_print_stat_s160(struct bankdesc *b, uint32_t sta, int verbose) +{ + fprintf(stderr,"Flash Spansion 160 error %"PRIi32"\n", sta); +} + +STATIC uint32_t +flash_write_line_s160(struct bankdesc *b, uint32_t a, const char *s, uint32_t N) +{ +uint32_t sta, nxt, j, v; +union bconv buf; + + if ( 0 == N ) + return -11; + + if ( N & (FLASH_STRIDE(b) - 1) ) { + fprintf(stderr,"flash_write_line_s160: invalid byte count (not multiple of stride\n"); + return -10; + } + + unlk(b, a); + + /* address block */ + fl_wr32_cmd(b, a, 0, WRBUF_DATA); + + /* (16-bit) word count per device */ + N /= FLASH_STRIDE(b); + + fl_wr32_cmd(b, a, 0, N-1); + + /* silence compiler warning about uninitialized var (N > 0 at this point) */ + v = 0; + + /* fill buffer */ + for (nxt = a; N>0; N--) { +#if (DEBUG > 4) + printf("Writing DAT *0x%08"PRIx32" = 0x%08"PRIx32"\n", nxt, *(uint32_t*)s); +#endif + /* deal with misaligned sources */ + for ( j=0; j<FLASH_STRIDE(b); j++ ) { + buf.c[j] = *s++; + } + v = fl_x32(b, &buf); + fl_wr32(b, nxt, v); + nxt += FLASH_STRIDE(b); + } + + /* burn buffer */ + fl_wr32_cmd(b, a, 0, PGBUF_DATA); + + /* pend */ + + sta = flash_pend(b, nxt - FLASH_STRIDE(b), WRITE_TIMEOUT, v); + + return sta; +} |