📄 pflash.c
字号:
/* $Id: pflash.c,v 1.1.1.2 2003/03/18 22:34:34 anoncvs Exp $ *//* * Copyright (c) 2000 Opsycon AB (www.opsycon.se) * Copyright (c) 2000 Rtmx, Inc (www.rtmx.com) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for Rtmx, Inc by * Opsycon Open System Consulting AB, Sweden. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */#include <stdio.h>#include <string.h>#include <stdlib.h>#include <machine/pio.h>#include <pmon.h>#include "pflash.h"#include "pflash_tgt.h"extern void delay __P((int));/* * FLASH Programming functions. * * Handle flash programing aspects such as different types * organizations and actual HW design organization/interleaving. * Combinations can be endless but an attempt to make something * more MI may be done later when the variations become clear. *//* * Flash devices known by this code. */#define __KB 1024struct fl_device fl_known_dev[] = { { "Am29F040", 0x01, 0xa4, FL_PROTO_AMD, FL_CAP_DE, 512*__KB, 64*__KB }, { "ST29F040", 0x20, 0xe2, FL_PROTO_AMD, FL_CAP_DE, 512*__KB, 64*__KB }, { "i28F016", 0x89, 0xaa, FL_PROTO_INT, 0, 2048*__KB, 64*__KB }, { "i28F016SA",0x89, 0xa0, FL_PROTO_INT, FL_CAP_A7, 2048*__KB, 64*__KB }, { "i28F160", 0xb0, 0xd0, FL_PROTO_INT, FL_CAP_DE, 2048*__KB, 64*__KB }, { "i28F320", 0xb0, 0xd4, FL_PROTO_INT, FL_CAP_DE, 4096*__KB, 64*__KB },/* Wierd... but this is what we get from the 320 on the Palomar-II */ { "i28F320x", 0x89, 0x14, FL_PROTO_INT, FL_CAP_DE, 4096*__KB, 64*__KB }, { 0 },};#undef __KBquad_t unlock_aa = 0xaaaaaaaaaaaaaaaa;quad_t unlock_55 = 0x5555555555555555;quad_t widedata;#define SETWIDE(x) do { \ u_int32_t __a = x; \ __a |= __a << 8; \ __a |= __a << 16; \ widedata = (quad_t)__a << 32 | __a; \ } while(0)/* * Inlineable function to unlock flash. */static __inline void fl_unlock __P((struct fl_map *));static __inline voidfl_unlock(map) struct fl_map *map;{ switch(map->fl_map_bus) { case FL_BUS_8: outb((map->fl_map_base + 0x5555), 0xaa); outb((map->fl_map_base + 0x2aaa), 0x55); break; case FL_BUS_64: break; /* Leave this for now */ case FL_BUS_8_ON_64: SETWIDE(0xaa); movequad((void *)map->fl_map_base + (0x5555 << 3), &widedata); SETWIDE(0x55); movequad((void *)map->fl_map_base + (0x2aaa << 3), &widedata); break; }}/* * Inlineable function to unlock and send command to flash. */static __inline void fl_command_amd __P((struct fl_map *, int, int));static __inline voidfl_command_amd(map, command, offset) struct fl_map *map; int command; int offset;{ fl_unlock(map); switch(map->fl_map_bus) { case FL_BUS_8: outb((map->fl_map_base + offset), command); break; case FL_BUS_64: case FL_BUS_8_ON_64: SETWIDE(command); movequad((void *)map->fl_map_base + (offset << 3), &widedata); break; }}/* * Inlineable function to send INT command to flash. */static __inline void fl_command_int __P((struct fl_map *, int, int));static __inline voidfl_command_int(map, command, offset) struct fl_map *map; int command; int offset;{ delay(5); switch(map->fl_map_bus) { case FL_BUS_8: outb(map->fl_map_base + offset, command); break; case FL_BUS_64: SETWIDE(command); movequad((void *)(map->fl_map_base + offset), &widedata); break; }}/* * Inlineable function to "reset" flash, eg return to read mode. */static __inline void fl_reset __P((struct fl_map *));static __inline voidfl_reset(map) struct fl_map *map;{ switch(map->fl_map_bus) { case FL_BUS_8: outb((map->fl_map_base), FL_RESET); break; case FL_BUS_8_ON_64: SETWIDE(FL_RESET); movequad((void *)map->fl_map_base, &widedata); break; case FL_BUS_64: fl_command_int(map, FL_INT_READ, 0); break; }}/* * Generic function to poll flash BUSY if available. * returns 1 if busy, 0 if OK, -1 if error. */intfl_isbusy(map, dev, what, offset) struct fl_device *dev; struct fl_map *map; int what;{ u_char *p; int d; switch(dev->fl_proto) { case FL_PROTO_AMD: switch(map->fl_map_bus) { case FL_BUS_8_ON_64: offset = offset << 3; /* Fallthrough */ case FL_BUS_8: d = inb(map->fl_map_base + offset); if(((d ^ what) & 0x80) == 0) { d = 0; /* Done */ } else if((d & 0x20) == 0) { d = 1; /* Busy */ } else { d = inb(map->fl_map_base + offset); if(((d ^ what) & 0x80) == 0) { d = 0; /* Done */ } else { d = -1; /* Poll error */ } } break; default: /* Not supported but sorted out much earlier */ } break; case FL_PROTO_INT: switch(map->fl_map_bus) { case FL_BUS_8: d = !(inb(map->fl_map_base) & FL_INT_BUSY); break; case FL_BUS_64: movequad(&widedata, (void *)map->fl_map_base); p = (u_char *)&widedata; d = !((p[1] & p[3] & p[5] & p[7]) & FL_INT_BUSY); break; } break; } return(d);}/* * Find what flash map address nelongs to. */struct fl_map *fl_find_map(void *base){ struct fl_map *map; for(map = tgt_flashmap(); map->fl_map_size != 0; map++) { if(map->fl_map_base > (u_int32_t)base || (map->fl_map_base + map->fl_map_size - 1) < (u_int32_t)base) { continue; /* Not this one */ } else { return(map); } } return((struct fl_map *)NULL);} /* * Try to figure out what kind of flash there is on given address. */struct fl_device *fl_devident(void *base, struct fl_map **m){ struct fl_device *dev; struct fl_map *map; char mfgid, chipid; map = tgt_flashmap(); /* If we can't write to flash, we can't identify */ if(!tgt_flashwrite_enable()) { return((struct fl_device *)NULL); } map = fl_find_map(base); if(map != NULL) { fl_command_amd(map, FL_AUTOSEL, AMD_CMDOFFS); switch(map->fl_map_bus) { case FL_BUS_8: mfgid = inb(map->fl_map_base); chipid = inb(map->fl_map_base+1); if(chipid == mfgid) { /* intel 16 bit flash mem */ chipid = inb(map->fl_map_base+3); } break; case FL_BUS_8_ON_64: mfgid = inb(map->fl_map_base); chipid = inb(map->fl_map_base+8); break; case FL_BUS_64: mfgid = inw(map->fl_map_base); chipid = inw(map->fl_map_base+8); break; } fl_reset(map); /* Lookup device type using manufacturer and device id */ for(dev = &fl_known_dev[0]; dev->fl_name != 0; dev++) { if(dev->fl_mfg == mfgid && dev->fl_id == chipid) { tgt_flashwrite_disable(); if(m) { *m = map; } return(dev); /* GOT IT! */ } }printf("Mfg %2x, Id %2x\n", mfgid, chipid); } tgt_flashwrite_disable(); return((struct fl_device *)NULL);}/* * Erase the flash device(s) addressed. */intfl_erase_device(void *base, int size, int verbose){ struct fl_map *map; struct fl_device *dev; int mask, ok, block; if(tgt_flashwrite_enable() == 0) { return(-2); /* Flash can't be write enabled */ } dev = fl_devident(base, &map); if(dev == NULL) { return(-3); /* No flash device found at address */ } /* * Sanity checks! */ if(size == -1 && (int)base == map->fl_map_base) { size = map->fl_map_size; /* Entire flash */ } mask = ((dev->fl_secsize * map->fl_map_width / map->fl_map_chips) - 1); if((int)base & mask) { size += (int)base & mask; base = (void *)((int)base & ~mask); } else if((size + ((int)base - map->fl_map_base)) > map->fl_map_size) { return(-4); /* End beyound end of flash */ } tgt_flashwrite_enable(); base -= map->fl_map_base; block = (int)base / map->fl_map_chips / dev->fl_secsize; size = (size + mask) & ~mask; /* Round up to catch entire flash */ while(size > 0) { if(size == map->fl_map_size && dev->fl_cap & (FL_CAP_DE|FL_CAP_A7)) { /* * Erase entire devices using the BULK erase feature */ if(verbose) { printf("Erasing all FLASH blocks. "); } switch(dev->fl_proto) { case FL_PROTO_AMD: fl_command_amd(map, FL_ERASE, AMD_CMDOFFS); fl_command_amd(map, FL_CHIP, AMD_CMDOFFS); break; case FL_PROTO_INT: if(dev->fl_cap & FL_CAP_A7) { fl_command_int(map, FL_INT_FCE_A7, 0); } else { fl_command_int(map, FL_INT_FCE, 0); } fl_command_int(map, FL_INT_CNF, 0); break; } size = 0; } else { /* * Not entire flash or no BULK erase feature. We * use sector/block erase. * This code does not handle boot block style devs * like the 28F160B3T yet. */ if(verbose) { printf("Erasing FLASH block %2d ", block); } switch(dev->fl_proto) { case FL_PROTO_AMD: fl_command_amd(map, FL_ERASE, AMD_CMDOFFS); fl_command_amd(map, FL_SECT, (int)base); break; case FL_PROTO_INT: fl_command_int(map, FL_INT_BE, 0); fl_command_int(map, FL_INT_CNF, (int)base); break; } block++; base += dev->fl_secsize * map->fl_map_chips; size -= dev->fl_secsize * map->fl_map_chips; } delay(1000); while((ok = fl_isbusy(map, dev, 0xffffffff, 0)) == 1) { if(verbose) { delay(1000); dotik(256, 0); } } delay(1000); switch(dev->fl_proto) { case FL_PROTO_AMD: fl_reset(map); break; case FL_PROTO_INT: fl_command_int(map, FL_INT_CLR, 0); fl_command_int(map, FL_INT_READ, 0); break; } if(verbose) { printf("\b Done.\n"); } } tgt_flashwrite_disable(); return(ok);}/* * Program a flash device. Assumed that the area is erased already. */intfl_program_device(void *fl_base, void *data_base, int data_size, int verbose){ struct fl_map *map; struct fl_device *dev; int ok; int i, d, off; if(tgt_flashwrite_enable() == 0) { return(-2); /* Flash can't be write enabled */ } dev = fl_devident(fl_base, &map); if(dev == NULL) { tgt_flashwrite_disable(); return(-3); /* No flash device found at address */ } if(data_size == -1 || (int)data_base == -1) { return(-4); /* Bad parameters */ } if((data_size + ((int)fl_base - map->fl_map_base)) > map->fl_map_size) { return(-4); /* Size larger than device array */ } if(verbose) { printf("Programming FLASH. "); } tgt_flashwrite_enable(); for(i = 0; i < data_size; i += map->fl_map_width) { switch(dev->fl_proto) { case FL_PROTO_AMD: off = (int)fl_base - map->fl_map_base; d = *(u_char *)data_base++; fl_command_amd(map, FL_PGM, AMD_CMDOFFS); switch(map->fl_map_bus) { case FL_BUS_8: outb(fl_base, d); fl_base++; break; case FL_BUS_8_ON_64: SETWIDE(d); movequad((void *)map->fl_map_base + (((int)fl_base - map->fl_map_base) << 3), &widedata); fl_base++; break; } break; case FL_PROTO_INT: fl_command_int(map, FL_INT_PGM, 0); switch(map->fl_map_bus) { case FL_BUS_8: *(u_char *)fl_base++ = *(u_char *)data_base++; break; case FL_BUS_64: bcopy(data_base, (void *)&widedata, 8); movequad(fl_base, &widedata); data_base += 8; fl_base += 8; break; } break; } if(verbose) { dotik(32, 0); } while((ok = fl_isbusy(map, dev, d, off)) == 1) { } } switch(dev->fl_proto) { case FL_PROTO_AMD: fl_reset(map); break; case FL_PROTO_INT: fl_command_int(map, FL_INT_CLR, 0); fl_command_int(map, FL_INT_READ, 0); break; } if(verbose) { printf("\b Done.\n"); } tgt_flashwrite_disable(); return(ok);}/* * List the various FLASH devices found. */voidfl_query_info(){ struct fl_map *map; struct fl_device *dev; printf("Available FLASH memory\n"); printf(" Start Size Width Sectorsize Type\n"); /* 0000ffff 0000ffff 4*8 00010000 Am29f040 */ for(map = tgt_flashmap(); map->fl_map_size != 0; map++) { dev = fl_devident((void *)map->fl_map_base, NULL); if(dev == NULL) { continue; /* Empty socket ? */ } printf("%08x %08x %2d*8 %08x %-10s\n", map->fl_map_base, map->fl_map_size, map->fl_map_width, dev->fl_secsize * map->fl_map_chips, dev->fl_name); } printf("\n");}/* * Flash programming functionality. */const Optdesc flash_opts[] = { { "-q", "display info about flash memory" }, { "-e", "erase flash" }, { 0 }};intcmd_flash(ac, av) int ac; char *av[];{extern int optind; int c; int opt_erase = 0; u_int32_t base_addr; u_int32_t offset; u_int32_t flashsize = -1; if(tgt_flashwrite_enable() == 0) { printf("FLASH can't be write enabled by PMON/2000. Please\n"); printf("consult the HW manual on how to enable writing!\n"); return(0); } tgt_flashwrite_disable(); if(ac == 1) { /* No arguments given, display flash info */ fl_query_info(); return(0); } optind = 0; while((c = getopt(ac, av, "eq")) != EOF) { switch(c) { case 'e': /* Erase */ opt_erase = 1; break; case 'q': /* query info */ fl_query_info(); return(0); /* Don't bother with the rest */ } } if(optind >= ac) { return(-1); /* Argument error */ } if(!get_rsa(&base_addr, av[optind++])) { return(-1); } if(optind < ac && !get_rsa (&flashsize, av[optind++])) { return(-1); } if(optind < ac && !get_rsa (&offset, av[optind++])) { return(-1); } if(opt_erase) { c = fl_erase_device((void *)base_addr, flashsize, TRUE); switch(c) { case -4: printf("Bad address and/or size for erase!\n"); break; case -3: printf("No FLASH memory on that address!\n"); break; case -1: printf("Erase status failed!\n"); break; } } else if(optind >= 4) { c = fl_program_device((void *)base_addr, (void *)offset, flashsize, TRUE); switch(c) { case -4: printf("Bad address and/or size!\n"); break; case -3: printf("No FLASH memory on that address!\n"); break; case -1: printf("Programming failed!\n"); break; } } return(0);}/* * Command table registration * ========================== */extern const Optdesc flash_opts[];static const Cmd Cmds[] ={ {"Misc"}, {"flash", "[-qe] addr size data", flash_opts, "program flash memory", cmd_flash, 1, 5, 0}, {0, 0}};static void init_cmd __P((void)) __attribute__ ((constructor));voidinit_cmd(){ cmdlist_expand(Cmds, 1);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -