olpcflash now works.
I'm booting my board with Linuxbios from the SPI part.
If you are currently booting from a PLCC then please give this a try.
The rest of you should probably wait a bit and see if I've changed
professions to bricklaying.
One thing I noticed is that SPI boot is slower than the PLCC by just
enough that I thought it had crashed after "Copying LinuxBIOS to RAM."
To compile:
gcc -O olpcflash.c -o olpcflash
or if you test it under the buildrom uClibc env then you will need to
add the -static option
To use:
- Read.
./olpcflash -r filename
- Write (Write does an erase first)
./olpcflash -w filename
- Verify
./olpcflash -v filename
My testing/developement method was to compile it static and copy it to
a USB key.
Then using a serial console I boot under the buildrom env from PLCC,
mount the USB key, and run the program.
A side note is that I build with my USB mass storage 'use_delay' set
zero and that appears to be working fine for my massive sample set of
1 key.
What I claim to have tested and Works For Me:
- read
- write
- verify
- erase
Know Issues:
- THERE ARE NO SAFEGUARDS!!!
nuff said.
- Its really slow.
Currently it takes a little over 11 minutes to program the part. A
large part of this is due to only doing a single byte each program
cycle.
The part can program up to 256 bytes each cycle so that may be the
next feature to add. According to the typical program time in the
datasheet it will only drop to around 6 minutes though so it may not
be worth it.
- Re-entry from KBC reset back into KBC run mode causes the board to reboot.
Need to ask EnE about this. For now putting the KBC back into run
mode after I finish the operation is disabled. This will allow you to
use verify and make sure it worked ok. If you use a PS2 keyboard you
are toast at this point.
--
Richard A. Smith
/*
* flash_rom.c: Flash programming utility
*
* Copyright 2000 Silicon Integrated System Corporation
* Copyright 2004 Tyan Corp
* yhlu [EMAIL PROTECTED] add exclude start and end option
* Copyright 2005-2006 coresystems GmbH
* Stefan Reinauer <[EMAIL PROTECTED]> added rom layout
* support, and checking for suitable rom image, various fixes
* support for flashing the Technologic Systems 5300.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/io.h>
#define printf_debug(x...) { if(verbose) printf(x); }
#define printf_super_debug(x...) { if(verbose > 1) printf(x); }
#define VER_MAJOR 0
#define VER_MINOR 1
#define VER_RELEASE 0
#define LINUXBIOS_START 0x10000
enum {
GPIO5 = 0xfc2a,
SPIA0 = 0xfea8,
SPIA1,
SPIA2,
SPIDAT,
SPICMD,
SPICFG,
SPIDATR
};
enum {
DUMMY,
WRITESTATUS = 1,
BYTEPROGRAM,
READ,
WRITEDISABLE,
READSTATUS,
WRITEENABLE,
HIGHSPEEDREAD = 0xb,
SECTORERASESST = 0x20,
ENABLEWRITESTATUSSST = 0x50,
BLOCKERASESST = 0x52,
CHIPERASESST = 0x60,
CHIPERASEPCM = 0xc7, /* also nexflash */
SECTORERASEPCM = 0xd7,
BLOCKERASEPCM = 0xd8, /* also nexflash, and spansion */
};
enum {
SPIBUSY = 2,
SPIFIRMWAREMODE = 1 << 4,
SPICMDWE = 8,
SPIFLASHREADCE = 1 << 6
};
enum {
WIP = 1 << 0
};
char *chip_to_probe = NULL;
int exclude_start_page, exclude_end_page;
int force=0, verbose=0;
/* this is the defaut index and data IO base address for
* EC register access.
* setup by the EC code.
*/
unsigned short iobase = 0x381;
// count to a billion. Time it. If it's < 1 sec, count to 10B, etc.
unsigned long micro = 1;
void myusec_delay(int time)
{
volatile unsigned long i;
for (i = 0; i < time * micro; i++);
}
void myusec_calibrate_delay()
{
int count = 1000;
unsigned long timeusec;
struct timeval start, end;
int ok = 0;
printf_debug("Setting up microsecond timing loop\n");
while (!ok) {
gettimeofday(&start, 0);
myusec_delay(count);
gettimeofday(&end, 0);
timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
(end.tv_usec - start.tv_usec);
count *= 2;
if (timeusec < 1000000 / 4)
continue;
ok = 1;
}
// compute one microsecond. That will be count / time
micro = count / timeusec;
printf_debug("%ldM loops per second\n", (unsigned long) micro);
}
void
setecindex(unsigned short index){
unsigned char hi = index>>8;
unsigned char lo = index;
outb(hi, iobase);
outb(lo, iobase+1);
printf_super_debug("%s: set 0x%x to 0x%x, 0x%x to 0x%x\n", __FUNCTION__,
iobase, hi, iobase+1, lo);
}
unsigned char
getecdata(void){
unsigned char data;
data = inb(iobase+2);
printf_super_debug("%s: read 0x%x from 0x%x\n", __FUNCTION__, data, iobase+2);
return data;
}
void
putecdata(unsigned char data){
outb(data, iobase+2);
printf_super_debug("%s: wrote 0x%x to 0x%x\n", __FUNCTION__, data, iobase+2);
}
unsigned char
readecdata(unsigned short index){
setecindex(index);
return getecdata();
}
void
writeecdata(unsigned short index, unsigned char data){
setecindex(index);
putecdata(data);
}
void setaddr(unsigned long addr){
unsigned char data;
data = addr;
writeecdata(SPIA0, data);
data = addr >> 8;
writeecdata(SPIA1, data);
data = addr >> 16;
writeecdata(SPIA2, data);
}
unsigned char
rdata(){
unsigned char data;
data = readecdata(SPIDAT);
return data;
}
void
wdata(unsigned char data){
writeecdata(SPIDAT, data);
}
unsigned char
cmd(void){
return readecdata(SPICMD);
}
void
docmd(unsigned char cmd){
writeecdata(SPICMD, cmd);
printf_super_debug("docmd: cmd 0x%x\n", cmd);
}
void
wait_cmd_sent(void){
int trycount = 0;
myusec_delay(10);
// docmd(READSTATUS);
// myusec_delay(1000);
while (readecdata(SPICFG) & SPIBUSY){
trycount++;
myusec_delay(10);
if (trycount > 100000){ /* 1 second! */
printf("wait_sent: Err: waited for > 1 second\n");
trycount = 0;
}
}
}
/*
* The EnE code has lots of small delays inbetween
* many of the actions. Route all this through
* one function so I can play with how long they
* need to be.
*/
void short_delay(void)
{
// EnE code did 4 pci reads of the base address
// which should be around 800nS
// 2 uS should cover it in case I'm wrong
myusec_delay(2);
}
/*
* Firmware mode allows you raw control over the SPI bus
* the spansion part is not supported by the EC in
* "hardware" mode.
* in this mode bytes written to the SPICMD register
* are clocked out the bus.
* This also asserts SPICS#
*/
void start_SPI_firmware_mode_access(void)
{
writeecdata(SPICFG,0x18);
}
void end_SPI_firmware_mode_access(void)
{
writeecdata(SPICFG,0x08);
}
/*
* You must do this prior to _every_ command that
* writes data to the part. The write enable
* latch resets after write commands complete.
*/
void
send_write_enable(void) {
start_SPI_firmware_mode_access();
short_delay();
docmd(WRITEENABLE);
wait_cmd_sent();
end_SPI_firmware_mode_access();
}
void
send_addr(unsigned long addr)
{
unsigned char data;
data = addr >> 16 & 0xff;
docmd(data);
wait_cmd_sent();
data = addr >> 8 & 0xff;
docmd(data);
wait_cmd_sent();
data = addr & 0xff;
docmd(data);
}
void
enable_flash_cmd(void)
{
writeecdata(SPICFG, SPICMDWE|readecdata(SPICFG));
}
void
enable_flash_write_protect(void){
unsigned char val;
val = readecdata(GPIO5);
val &= ~0x80;
writeecdata(GPIO5,val);
}
void
disable_flash_write_protect(void){
unsigned char val;
val = readecdata(GPIO5);
val |= 0x80;
writeecdata(GPIO5,val);
}
/*
* This appears to be necessary. If you watch the lines with
* scope you will see that there is constant activity on the SPI
* bus to the part. Trying to write to the port while all that
* is going on is sure to muck things up.
* Putting this into reset stops all that
* activity.
* Plus Ray Tseng said so.
* It appears to work in read mode without this though.
*/
void put_kbc_in_reset(void){
unsigned char val;
unsigned long timeout = 500000;
outb(0xd8,0x66);
while((inb(0x66) & 0x02) & timeout>0) {
timeout--;
}
val = readecdata(0xff14);
val |= 0x01;
writeecdata(0xff14,val);
}
void restore_kbc_run_mode(void){
unsigned char val;
val = readecdata(0xff14);
val &= ~0x01;
writeecdata(0xff14,val);
}
unsigned char read_status_register(void)
{
unsigned char data=0;
start_SPI_firmware_mode_access();
short_delay();
docmd(READSTATUS);
wait_cmd_sent();
docmd(DUMMY);
wait_cmd_sent();
data = rdata();
end_SPI_firmware_mode_access();
return data;
}
// Staus reg writes; erases and programs all need to
// check this status bit.
int wait_write_done(void)
{
int trycount = 0;
while (read_status_register() & WIP){
trycount++;
myusec_delay(10);
// For the spansion part datasheet claims that
// the only thing that takes longer than 500mS is
// bulk erase and we don't ever want to use that
// command
if (trycount > 100000){ /* 1 second! */
printf("wait_write_done: Err: waited for > 1 second\n");
trycount = 0;
return -1;
}
}
return 0;
}
int erase_sector(unsigned long addr)
{
send_write_enable();
short_delay();
start_SPI_firmware_mode_access();
short_delay();
docmd(BLOCKERASEPCM);
wait_cmd_sent();
send_addr(addr);
wait_cmd_sent();
end_SPI_firmware_mode_access();
return wait_write_done();
}
/*
Erase from sectors 0x10000 to 0xf0000
*/
int erase_linuxbios_area(void) {
unsigned long addr;
for (addr = 0x10000;addr < 0xfffff;addr+=0x10000) {
printf("Erasing Sector: 0x%08x\r\n",addr);
erase_sector(addr);
}
return 0;
}
int erase_flash(void)
{
erase_linuxbios_area();
}
unsigned char
read_flash_byte(unsigned long addr) {
unsigned char data;
setaddr(addr);
docmd(READ);
wait_cmd_sent();
data = rdata();
printf_debug("read [EMAIL PROTECTED]", data, addr);
return data;
}
int
read_flash(unsigned char *buf, unsigned long start, unsigned long stop){
unsigned long i;
for (i = start; i <= stop; i++) {
if ((i % 0x10000) == 0)
printf("Sector 0x%08x\r\n", i);
*buf = read_flash_byte(i);
buf++;
}
return 0;
}
int
write_flash_byte(unsigned long addr, unsigned char data) {
unsigned char verify;
unsigned char addr_byte;
send_write_enable();
short_delay();
start_SPI_firmware_mode_access();
short_delay();
docmd(BYTEPROGRAM);
wait_cmd_sent();
send_addr(addr);
wait_cmd_sent();
docmd(data);
wait_cmd_sent();
end_SPI_firmware_mode_access();
wait_write_done();
/*
verify = read_flash_byte(addr);
if (verify != data) {
printf("addr 0x%x, want 0x%x, got 0x%x\n",
addr, data, verify);
return -1;
}
*/
return 0;
}
int
write_flash(unsigned char *buf, unsigned long start, unsigned long end){
unsigned long i;
printf("Writing...\r\n");
for(i = start; i <= end; i++){
if ((i % 0x10000) == 0)
printf("Sector 0x%08x\r\n", i);
if (write_flash_byte(i, *buf)) {
return -1;
}
buf++;
}
return 0;
}
int verify_flash(unsigned char *buf, unsigned long start, unsigned long end)
{
unsigned long idx;
printf("Verifying flash\n");
if(verbose) printf("address: 0x00000000\b\b\b\b\b\b\b\b\b\b");
for (idx = start; idx <= end; idx++) {
if (verbose && ( (idx & 0xfff) == 0xfff ))
printf("0x%08x", idx);
if ((idx % 0x10000) == 0)
printf("Sector 0x%08x\r\n", idx);
if (read_flash_byte(idx) != *(buf)) {
if (verbose) {
printf("0x%08x ", idx);
}
printf("- FAILED\n");
return 1;
}
buf++;
if (verbose && ( (idx & 0xfff) == 0xfff ))
printf("\b\b\b\b\b\b\b\b\b\b");
}
if (verbose)
printf("\b\b\b\b\b\b\b\b\b\b ");
printf("- VERIFIED \n");
return 0;
}
void usage(const char *name)
{
printf("%s Ver: %d.%d.%d\n",name,VER_MAJOR,VER_MINOR,VER_RELEASE);
printf("usage: %s [-rwv] [-V] [file]\n", name);
printf(" -r | --read: read flash and save into file\n"
" -w | --write: write file into flash (default when file is specified)\n"
" -v | --verify: verify flash against file\n"
" -E | --erase: Erase LinuxBIOS area (0x10000-0xfffff)\n"
" -V | --verbose: more verbose output"
" -h | --help: This message\n\n");
exit(1);
}
int main(int argc, char *argv[])
{
unsigned char *buf;
unsigned long size;
FILE *image;
int opt;
int option_index = 0;
int read_it = 0,
write_it = 0,
erase_it = 0,
verify_it = 0;
int ret = 0;
static struct option long_options[]= {
{ "read", 0, 0, 'r' },
{ "write", 0, 0, 'w' },
{ "erase", 0, 0, 'E' },
{ "verify", 0, 0, 'v' },
{ "iobase", 1, 0, 'i' },
{ "verbose", 0, 0, 'V' },
{ "help", 0, 0, 'h' },
{ 0, 0, 0, 0 }
};
char *filename = NULL;
unsigned int exclude_start_position=0, exclude_end_position=0; // [x,y)
char *tempstr=NULL, *tempstr2=NULL;
if (iopl(3) < 0){
perror("iop(3)");
exit(1);
}
setbuf(stdout, NULL);
while ((opt = getopt_long(argc, argv, "rwvVEfc:s:e:m:l:i:h", long_options,
&option_index)) != EOF) {
switch (opt) {
case 'r':
read_it = 1;
break;
case 'w':
write_it = 1;
break;
case 'v':
verify_it = 1;
break;
case 'V':
verbose++;
break;
case 'E':
erase_it = 1;
break;
case 'i':
tempstr = strdup(optarg);
sscanf(tempstr,"%x",&iobase);
break;
case 'h':
default:
usage(argv[0]);
break;
}
}
if (argc > 1) {
/* Yes, print them. */
int i;
printf_debug ("The arguments are:\n");
for (i = 1; i < argc; ++i)
printf_debug ("%s\n", argv[i]);
}
if (read_it && write_it) {
printf("-r and -w are mutually exclusive\n");
usage(argv[0]);
}
if (optind < argc)
filename = argv[optind++];
printf("Calibrating delay loop... ");
myusec_calibrate_delay();
printf("ok\n");
if (!filename && !erase_it) {
// FIXME: Do we really want this feature implicitly?
printf("Doing nothing\n");
return 0;
}
enable_flash_cmd();
size = (1024*1024) - (64*1024);
buf = (unsigned char *) calloc(size, sizeof(char));
// When I erased the flash and then exited the machine
// crashed.
if (erase_it) {
put_kbc_in_reset();
disable_flash_write_protect();
erase_flash();
enable_flash_write_protect();
// restore_kbc_run_mode();
exit(0);
} else if (read_it) {
if ((image = fopen(filename, "w")) == NULL) {
perror(filename);
exit(1);
}
put_kbc_in_reset();
printf("Reading Flash...\r\n");
read_flash(buf,LINUXBIOS_START,LINUXBIOS_START+size-1);
/*
if (exclude_end_position - exclude_start_position > 0)
memset(buf+exclude_start_position, 0,
exclude_end_position-exclude_start_position);
*/
fwrite(buf, sizeof(char), size, image);
fclose(image);
printf("done\n");
// restore_kbc_run_mode();
} else {
if ((image = fopen(filename, "r")) == NULL) {
perror(filename);
exit(1);
}
printf("Loading 0x%x bytes from %s\r\n",size,filename);
fread(buf, sizeof(char), size, image);
// show_id(buf, size);
fclose(image);
}
if (write_it) {
put_kbc_in_reset();
disable_flash_write_protect();
erase_linuxbios_area();
write_flash(buf,LINUXBIOS_START, LINUXBIOS_START+size-1);
enable_flash_write_protect();
// restore_kbc_run_mode();
}
if (verify_it) {
put_kbc_in_reset();
ret |= verify_flash(buf,LINUXBIOS_START,LINUXBIOS_START+size-1);
// restore_kbc_run_mode();
}
if (buf) {
free(buf);
}
return ret;
}
_______________________________________________
Devel mailing list
[email protected]
http://mailman.laptop.org/mailman/listinfo/devel