diff options
Diffstat (limited to 'qemu/roms/openbios/utils/devbios/programming.c')
-rw-r--r-- | qemu/roms/openbios/utils/devbios/programming.c | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/qemu/roms/openbios/utils/devbios/programming.c b/qemu/roms/openbios/utils/devbios/programming.c new file mode 100644 index 000000000..e1b35a34b --- /dev/null +++ b/qemu/roms/openbios/utils/devbios/programming.c @@ -0,0 +1,539 @@ +/* + * OpenBIOS - free your system! + * ( firmware/flash device driver for Linux ) + * + * programming.c - flash device programming and probing algorithms. + * + * This program is part of a free implementation of the IEEE 1275-1994 + * Standard for Boot (Initialization Configuration) Firmware. + * + * Copyright (C) 1998-2004 Stefan Reinauer, <stepan@openbios.org> + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + * + */ + +// <-- C++ style comments are for experimental comments only. +// They will disappear as soon as I fixed all the stuff. + +/* #define DEBUG_PROBING */ + +#include <linux/config.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) && defined(MODVERSIONS) +#include <linux/modversions.h> +#endif + +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/uaccess.h> + +#include "bios.h" +#include "pcisets.h" +#include "flashchips.h" +#include "programming.h" + +struct flashdevice flashdevices[BIOS_MAXDEV]; +int flashcount; + +/* + * ****************************************** + * + * flashchip handling + * + * ****************************************** + */ + + +void flash_command (unsigned char *addr, unsigned char command) +#if 1 +{ + flash_writeb(addr, 0x5555, 0xaa); + flash_writeb(addr, 0x2AAA, 0x55); + flash_writeb(addr, 0x5555, command); +} +void fwh_flash_command(unsigned char *addr, unsigned char command) +#endif +{ + flash_writeb(addr, 0x75555, 0xaa); + flash_writeb(addr, 0x72aaa, 0x55); + flash_writeb(addr, 0x75555, command); +} + +#define CFLASH flashdevices[flashcount] +int flash_probe_address(void *address) +{ + int flashnum=0, manufnum=0, sectors=0; + unsigned short flash_id, testflash; + unsigned long flags; +#ifdef DEBUG_PROBING + printk( KERN_DEBUG "BIOS: Probing for flash chip @0x%08lx\n", (unsigned long) address); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + save_flags(flags); +#endif + spin_lock_irqsave(&bios_lock, flags); + + testflash= (flash_readb(address, 0))+(flash_readb(address, 1)<<8); + + /* 1st method: Intel, Atmel listen to this.. */ + + flash_command(address, 0x90); + udelay(20); + + flash_id = (flash_readb(address, 0))+(flash_readb(address, 1)<<8); + +#ifdef DEBUG_PROBING + printk (KERN_DEBUG "BIOS: testflash[%04x] flash_id[%04x]\n", + testflash, flash_id); +#endif + + /* 2nd method: Winbond (I think this is Jedec standard) */ + + if (flash_id==testflash) { +#ifdef DEBUG_PROBING + printk (KERN_DEBUG "BIOS: Trying 2nd ID method.\n"); +#endif + flash_command(address, 0xf0); /* Reset */ + udelay(20); + + flash_command(address, 0x80); + flash_command(address, 0x60); + udelay(20); + + flash_id = (flash_readb(address, 0))+(flash_readb(address, 1)<<8); +#ifdef DEBUG_PROBING + printk (KERN_DEBUG "BIOS: testflash[%04x] flash_id[%04x]\n", + testflash, flash_id); +#endif + } + + /* 3rd Method: Some Winbonds seem to want this */ + + if (flash_id==testflash) { +#ifdef DEBUG_PROBING + printk (KERN_DEBUG "BIOS: Trying 3rd ID method.\n"); +#endif + flash_command(address, 0xf0); /* Reset again */ + udelay(20); + + flash_command(address, 0x80); + flash_command(address, 0x20); + udelay(20); + + flash_id = (flash_readb(address, 0))+(flash_readb(address, 1)<<8); +#ifdef DEBUG_PROBING + printk (KERN_DEBUG "BIOS: testflash[%04x] flash_id[%04x]\n", + testflash, flash_id); +#endif + } + + if (flash_id==0x7f7f && flash_readb(address, 0x100)==0x1c) { + /* We have an Eon flashchip. They keep their + * device id at 0x101 instead of 0x1 + */ + printk(KERN_INFO "BIOS: Eon flash device detected\n"); + flash_id=(flash_readb(address, 0x1))+(flash_readb(address, 0x101)<<8); + } + + flash_command(address, 0xf0); + udelay(20); + + spin_unlock_irqrestore(&bios_lock, flags); + + if (flash_id==testflash) return 0; /* Nothing found :-( */ + + while (flashchips[flashnum].id!=0) { + if (flash_id==flashchips[flashnum].id) + break; + flashnum++; + } + + while (manufacturers[manufnum].id!=0) { + if ((flash_id&0xff)==manufacturers[manufnum].id) + break; + manufnum++; + } + + if (flashchips[flashnum].id) { + while (flashchips[flashnum].sectors[sectors]<flashchips[flashnum].size) + sectors++; + } + + if (flashcount >= BIOS_MAXDEV) { + printk(KERN_DEBUG "BIOS: Too many flash devices found.\n"); + return -1; + } + + CFLASH.flashnum = flashnum; + CFLASH.manufnum = manufnum; + CFLASH.id = flash_id; + CFLASH.size = (flashchips[flashnum].size<<10); + CFLASH.sectors = sectors; + CFLASH.open_mode= 0; + CFLASH.open_cnt = 0; + + return 1; +} + +void flash_probe_area(unsigned long romaddr, unsigned long romsize, + int map_always) +{ + unsigned long probeaddr; + unsigned char *mapped; + + mapped=ioremap(romaddr, romsize); + + devices[flashdevices[currflash].idx].activate(); + + probeaddr=(unsigned long)mapped; + + while ( probeaddr < (unsigned long)mapped + romsize - 0x5555 ) { + if ( flash_probe_address ((void *)probeaddr) != 1) { + probeaddr += 4*1024; + continue; + } + + CFLASH.offset = probeaddr-(unsigned long)mapped; + CFLASH.mapped = (unsigned long)mapped; + CFLASH.physical = romaddr+CFLASH.offset; + + printk( KERN_INFO "BIOS: flash device with size " + "%dk (ID 0x%04x) found.\n", + CFLASH.size >> 10, CFLASH.id); + + printk( KERN_INFO "BIOS: physical address " + "0x%08lx (va=0x%08lx+0x%lx).\n", + CFLASH.physical, (unsigned long)CFLASH.mapped, + CFLASH.offset); + + if (flashchips[CFLASH.flashnum].flags&f_fwh_compl) { + unsigned long t_lk; + unsigned int i=7; + printk(KERN_INFO "BIOS: FWH compliant " + "chip detected.\n"); + for (t_lk=0xffb80002; t_lk<=0xffbf0002; t_lk+=0x10000) + { + printk(KERN_INFO "Lock register %d " + "(0x%08lx): 0x%x\n", + i, t_lk, (unsigned int) + (readb(phys_to_virt(t_lk)))); + i--; + } + } + flashcount++; + currflash++; +#ifdef MULTIPLE_FLASH + probeaddr += flashdevices[flashcount-1].size; + flashdevices[flashcount].mapped=flashdevices[flashcount-1].mapped; + flashdevices[flashcount].data=flashdevices[flashcount-1].data; + continue; +#else + break; +#endif + } + + /* We might want to always map the memory + * region in certain cases + */ + + if (map_always) { + CFLASH.flashnum = 0; + CFLASH.manufnum = 0; + CFLASH.id = 0; + CFLASH.size = romsize; + CFLASH.sectors = 0; + CFLASH.open_mode= 0; + CFLASH.open_cnt = 0; + CFLASH.offset = 0; + CFLASH.mapped = (unsigned long)mapped; + CFLASH.physical = romaddr; + printk( KERN_INFO "BIOS: rom device with size " + "%dk registered.\n", CFLASH.size >> 10); + flashcount++; currflash++; + return; + } + + /* We found nothing in this area, so let's unmap it again */ + + if (flashcount && flashdevices[flashcount-1].mapped != (unsigned long)mapped) + iounmap(mapped); + + devices[flashdevices[currflash].idx].deactivate(); +} + +#undef CFLASH + +void flash_program (unsigned char *addr) +{ + flash_command(addr, 0xa0); +} + +void flash_program_atmel (unsigned char *addr) +{ + flash_command(addr, 0x80); + flash_command(addr, 0x20); +} + +int flash_erase (unsigned char *addr, unsigned int flashnum) +{ + flash_command(addr, 0x80); + flash_command(addr, 0x10); + udelay(80); + return flash_ready_toggle(addr, 0); +} + +int flash_erase_sectors (unsigned char *addr, unsigned int flashnum, unsigned int startsec, unsigned int endsec) +{ + unsigned int sector; + + if (!(flashchips[flashnum].flags & f_slow_sector_erase)) { + flash_command(addr, 0x80); + + if (flashchips[flashnum].flags&f_fwh_compl) { + flash_writeb(addr, 0x75555,0xaa); + flash_writeb(addr, 0x72aaa,0x55); + } else { + flash_writeb(addr, 0x5555,0xaa); + flash_writeb(addr, 0x2aaa,0x55); + } + + for (sector=startsec; sector <= endsec; sector++) { + flash_writeb (addr, flashchips[flashnum].sectors[sector]*1024, 0x30); + } + + udelay(150); // 80 max normally, wait 150usec to be sure +#if 0 + if (flashchips[flashnum].flags&f_fwh_compl) +#endif + return flash_ready_toggle(addr, flashchips[flashnum].sectors[sector-1]*1024); +#if 0 + else + return flash_ready_poll(addr, flashchips[flashnum].sectors[sector-1]*1024, 0xff); +#endif + } + + /* sectors must be sent the sector erase command for every sector */ + for (sector=startsec; sector <= endsec; sector++) { + flash_command(addr, 0x80); + if (flashchips[flashnum].flags&f_fwh_compl) { + flash_writeb(addr, 0x75555,0xaa); + flash_writeb(addr, 0x72aaa,0x55); + } else { + flash_writeb(addr, 0x5555,0xaa); + flash_writeb(addr, 0x2aaa,0x55); + } + + flash_writeb(addr, flashchips[flashnum].sectors[sector]*1024, 0x30); + udelay(150); +#if 0 + if (flashchips[flashnum].flags&f_fwh_compl) +#endif + flash_ready_toggle(addr, flashchips[flashnum].sectors[sector] *1024); +#if 0 + else + flash_ready_poll(addr, flashchips[flashnum].sectors[sector]*1024, 0xff); +#endif + } + + return 0; + +} + +/* waiting for the end of programming/erasure by using the toggle method. + * As long as there is a programming procedure going on, bit 6 of the last + * written byte is toggling it's state with each consecutive read. + * The toggling stops as soon as the procedure is completed. + * This function returns 0 if everything is ok, 1 if an error occured + * while programming was in progress. + */ + +int flash_ready_toggle (unsigned char *addr, unsigned int offset) +{ + unsigned long int timeout=0; + unsigned char oldflag, flag; + int loop=1; + + oldflag=flash_readb(addr, offset) & 0x40; + + while (loop && (timeout<0x7fffffff)) { + flag=flash_readb(addr, offset) & 0x40; + + if (flag == oldflag) + loop=0; + + oldflag=flag; + timeout++; + } + + if (loop) { + printk(KERN_DEBUG "BIOS: operation timed out (Toggle)\n"); + return 1; + } + + return 0; +} + +/* This functions is similar to the above one. While a programming + * procedure is going on, bit 7 of the last written data byte is + * inverted. When the procedure is completed, bit 7 contains the + * correct data value + */ + +int flash_ready_poll (unsigned char *addr, unsigned int offset, unsigned char data) +{ + unsigned long int timeout=0; + unsigned char flag; + + flag=flash_readb(addr, offset); + + while ( ( flag & 0x80) != ( data & 0x80)) { + if ( ( flag & 0x80 ) == ( data & 0x80 ) ) { +#ifdef DBGTIMEOUT + printk(KERN_DEBUG "BIOS: Timeout value (EOT Polling) %ld\n",timeout); +#endif + return 0; + } + flag=flash_readb(addr, offset); + if (timeout++>12800) { // 10 times more than usual. + printk(KERN_ERR "BIOS: EOT Polling timed out at 0x%08x." + " Try again or increase max. timeout.\n",offset); + return 1; + } + if ((flag & 0x80) == ( data & 0x80)) { + flag=flash_readb(addr, offset); + } + } +#ifdef DBGTIMEOUT + printk(KERN_DEBUG "BIOS: Timeout value (EOT Polling) %ld\n",timeout); +#endif + + flag=flash_readb(addr, offset); + if ( ( flag & 0x80 ) == ( data & 0x80 ) ) return 0; else return 1; +} + + + +void iflash_program_byte (unsigned char *addr, unsigned int offset, unsigned char data) +{ + unsigned long int timeout=0; + unsigned char flag; + + flash_writeb (addr, offset, 0x40); + flash_writeb (addr, offset, data); + + flash_writeb (addr, offset, 0x70); /* Read Status */ + do { + flag=flash_readb (addr, offset); + if (timeout++>100) { // usually 2 or 3 :-) + printk(KERN_ERR "BIOS: Intel programming timed out at" + "0x%08x. Try again or increase max. timeout.\n",offset); + return; + } + } while ((flag&0x80) != 0x80); + +#ifdef DBGTIMEOUT + printk (KERN_DEBUG"BIOS: Timeout value (Intel byte program) %ld\n",timeout); +#endif + + if (flag&0x18) { + flash_writeb (addr, offset, 0x50); /* Reset Status Register */ + printk (KERN_ERR "BIOS: Error occured, please repeat write operation. (intel)\n"); + } + + flash_writeb (addr, offset, 0xff); +} + + + +int iflash_erase_sectors (unsigned char *addr, unsigned int flashnum, unsigned int startsec, unsigned int endsec) +{ + unsigned long int timeout; + unsigned int sector, offset=0; + unsigned char flag; + + for (sector=startsec; sector<=endsec; sector++) { + offset=(flashchips[flashnum].sectors[sector]*1024); + flash_writeb (addr, offset, 0x20); + flash_writeb (addr, offset, 0xd0); + + flash_writeb (addr, offset, 0x70); /* Read Status */ + timeout=0; + do { + flag=flash_readb (addr, offset); + if (timeout++>1440000) { // usually 144000 + printk(KERN_ERR "BIOS: Intel sector erase timed out at 0x%08x. Try again or increase max. timeout.\n",offset); + return 1; + } + } while ((flag&0x80) != 0x80); + +#ifdef DBGTIMEOUT + printk (KERN_DEBUG "BIOS: Timeout value (Intel sector erase) %ld\n",timeout); +#endif + + if (flag&0x28) { + flash_writeb (addr, offset, 0x50); + flash_writeb (addr, offset, 0xff); + return 1; /* Error! */ + } + } + + flash_writeb (addr, offset, 0xff); + return 0; +} + + + +unsigned char flash_readb(unsigned char *addr, unsigned int offset) +{ +#if defined(__alpha__) + if (flashdevices[currflash].data==(void *)0xfff80000) { + if (offset<0x80000) + outb(0x00,0x800); + else { + outb(0x01, 0x800); + offset-=0x80000; + } + } +#endif + return readb(addr+offset); +} + + + +void flash_writeb(unsigned char *addr, unsigned int offset, unsigned char data) +{ +#if defined(__alpha__) + if (flashdevices[currflash].data==(void *)0xfff80000) { + if (offset<0x80000) + outb(0x00,0x800); + else { + outb(0x01, 0x800); + offset-=0x80000; + } + } +#endif +/* + printk(KERN_DEBUG "BIOS: writing 0x%02x to 0x%lx+0x%x\n", + data,bios,offset); + */ + writeb(data,addr+offset); +} |