/* * 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, * * 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 #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) && defined(MODVERSIONS) #include #endif #include #include #include #include #include #include #include #include #include #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]= 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); }