I broke out the oscope last night and worked on getting writes to work.

New info I've learned or confirmed:

- Base address for talking to EC registers is 0x381.

- I can now de-assert the write protect pin to the flash.

Magic EC address(s) are:
0xFC1A  GPIO5 output control
0xFC2A  GPIO5 value

So you can either set bit 7 in 0xFC1A to disable the output and the
pullup on the WE# pin de-asserts or you can set/clear bit 7 in 0xFC2A
to control the actual value of the IO pin.

I currently chose to control the value of the IO so I'm toggleing bit
7 @ 0xFC2A

- Putting the KBC in reset appears to be mantory.

Just like Rays email said.  If you watch the SPI lines after you boot
you see that there is constant activity on the SPI bus.  If you put
the KBC into reset this activity stops.  KBC reading some sort of
keymap look up table perhaps?

Whats weird is that reads will also work without putting it into
reset.  But I bet its trouble if we try to do the same with writes.

- You need to erase the sector prior to a program.

Spansion data sheet says that page program will take bits from a 1 to
0 and that a erase is necessary first.
The commands that we send to the EC match the same commands on the
spansion datasheet so my current assumption is that the EC is just a
traslation layer and passes these commands directly onto the spansion
part without any additional logic other than formating.


What doesn't work:

- Sector erase.

When I use my sector erase command I don't see anything happen on the
SPI bus.  So somethings still not right.

My plan is later on today to work on it some more and get a handle on
sending commands to the spansion part and perhaps why nothing happens
for sector erase.

Attached is the latest.  Its got some in the write routine  commented
out where I was debugging.  So its just FYI or in case someone else
whats to play with it a bit.

Oh yeah and I fixed a 1Meg per run memory leak.  Every so often my
board would just re-boot and I think this was causing it

--
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); }

enum {
	GPIO5 = 0xfc2a,
	SPIA0 = 0xfea8, 
	SPIA1, 
	SPIA2, 
	SPIDAT,
	SPICMD, 
	SPICFG,
	SPIDATR
};

enum {
	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, 
	SPICMDWE = 8,
	SPIFLASHREADCE = 1 <<6
};

char *chip_to_probe = NULL;

int exclude_start_page, exclude_end_page;
int force=0, verbose=0;

/* this is the default hardware base address. 
  * 0x2d is the MSB, 0x2e is the LSB, 0x2f is the data
  */

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_done(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_done: waiting for 1 second ... bad ... \n");
			trycount = 0;
		}
	}
}	

void
enable_flash_write(void) {
	docmd(WRITEENABLE);
	wait_done();
	printf_debug("enabled flash write\n");
}

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);
}

int
erase_flash(){
	/* no idea how to do this yet on spansion */
	/* 
		we don't ever want to do a full erase as that
		will take out the entire flash and hose the 
		EC code.
	*/
	
	printf("erase_flash:Not bricking your board\n");
}
 
int erase_sector(long addr) {
	setaddr(addr);
	docmd(BLOCKERASEPCM);
	wait_done();
	return 0; // TODO: check status byte?
}

/*
 Erase from sectors 0x10000 to 0xf0000
*/
int erase_linuxbios_area(void) {
	long addr;

	// Just test the top sector for now.
	for (addr = 0xf0000;addr < 0xfffff;addr+=0x10000) {
		erase_sector(addr);
	}
	return 0;
}

unsigned char 
read_flash_byte(unsigned long addr) {
	unsigned char data;
	setaddr(addr);
	docmd(READ);
	wait_done();
	data =  rdata();
	printf_debug("%s: read [EMAIL PROTECTED]", __FUNCTION__, data, addr);
	return data;
}

int
read_flash(unsigned char *buf){
	int i;
	for (i = 0xf0000; i <= 1024*1024; i++) {
		if ((i % 64) == 0)
			printf("\r\r\r\r\r\r\r\r%08x", i);
		buf[i] = read_flash_byte(i);
	}
	return 0;
}

int
write_flash_byte(unsigned long addr, unsigned char data) {
	unsigned char verify;
	setaddr(addr);
	wdata(data);
	docmd(BYTEPROGRAM);
	wait_done();
/*
	verify =  read_flash_byte(addr);
	printf_debug("%s: write [EMAIL PROTECTED]", __FUNCTION__, data, addr);
	if (verify != data) {
		printf("%s: addr 0x%x, want 0x%x, got 0x%x\n", __FUNCTION__, 
				addr, data, verify);
		return -1;
	}
 */
	return 0;
}

int
write_flash(unsigned char  *buf, int start, int end){
	int i;

	enable_flash_write();
	for(i = start; i <= end; i++){
		if ((i % 64) == 0)
			printf("\r\r\r\r\r\r\r\r%08x", i);
		if (write_flash_byte(i, buf[i]))
			return -1;
	}

	return 0;
}


int verify_flash(unsigned char *buf)
{
	int idx;
	int total_size = 1024*1024;

	printf("Verifying flash ");
	
	if(verbose) printf("address: 0x00000000\b\b\b\b\b\b\b\b\b\b");
	
	for (idx = 0; idx < total_size; idx++) {
		if (verbose && ( (idx & 0xfff) == 0xfff ))
			printf("0x%08x", idx);

		if (read_flash_byte(idx) != *(buf + idx)) {
			if (verbose) {
				printf("0x%08x ", idx);
			}
			printf("- FAILED\n");
			return 1;
		}
		
		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("usage: %s [-rwvE] [-V] [-c chipname] [-s exclude_start] [-e exclude_end] [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 flash device\n"
	       "   -V | --verbose: more verbose output\n\n"
	       "   -c | --chip <chipname>: probe only for specified flash chip\n"
	       "   -s | --estart <addr>: exclude start position\n"
	       "   -e | --eend <addr>: exclude end postion\n"
	       "   -m | --mainboard <vendor:part>: override mainboard settings\n"
	       "   -f | --force: force write without checking image\n"
	       "   -l | --layout <file.layout>: read rom layout from file\n"
	       "   -i | --image <name>: only flash image name from flash layout\n"
	       "\n"
	       " If no file is specified, then all that happens\n"
	       " is that flash info is dumped\n\n");
	exit(1);
}

int main(int argc, char *argv[])
{
	unsigned char *buf;
	unsigned long size;
	FILE *image;
	struct flashchip *flash;
	int opt;
	int option_index = 0;
	int read_it = 0, 
	    write_it = 0, 
	    erase_it = 0, 
	    verify_it = 0;
	int ret = 0;
	static char flashimage[1048576];

	static struct option long_options[]= {
		{ "read", 0, 0, 'r' },
		{ "write", 0, 0, 'w' },
		{ "erase", 0, 0, 'E' },
		{ "verify", 0, 0, 'v' },
		{ "estart", 1, 0, 's' },
		{ "eend", 1, 0, 'e' },
		{ "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 's':
			tempstr = strdup(optarg);
			sscanf(tempstr,"%x",&exclude_start_position);
			break;
		case 'i':
			tempstr = strdup(optarg);
			sscanf(tempstr,"%x",&iobase);
			break;
		case 'e':
			tempstr = strdup(optarg);
			sscanf(tempstr,"%x",&exclude_end_position);
			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");

	/* We look at the lbtable first to see if we need a
	 * mainboard specific flash enable sequence.
	 *
	linuxbios_init();
	*/
	enable_flash_cmd();
	enable_flash_write();

	if (!filename && !erase_it) {
		// FIXME: Do we really want this feature implicitly?
		printf("OK, only ENABLING flash write, but NOT FLASHING.\n");
		return 0;
	}

	size = 1024*1024;
	buf = (unsigned char *) calloc(size, sizeof(char));
	
	if (erase_it) {
		erase_flash(flash);
		exit(0);		
	} else if (read_it) {
		if ((image = fopen(filename, "w")) == NULL) {
			perror(filename);
			exit(1);
		}
		printf("Reading Flash...");
		read_flash(buf);

		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");
	} else {
		if ((image = fopen(filename, "r")) == NULL) {
			perror(filename);
			exit(1);
		}
		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, 64*1024, 1024*1024-1);
//		enable_flash_write_protect();
//		restore_kbc_run_mode();
	}

	if (verify_it)
		ret |= verify_flash(buf);

	if (buf) {
		free(buf);
	}
	return ret;
}

_______________________________________________
Devel mailing list
[email protected]
http://mailman.laptop.org/mailman/listinfo/devel

Reply via email to