📄 programming.c
字号:
/* * BIOS/Flashrom driver for Linux * * Copyright (C) 1998-2002 Stefan Reinauer <stepan@suse.de> * */// <-- C++ style comments are for experimental comments only.// They will disappear as soon as I fixed all the stuff.#include <linux/config.h>#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"#define OLD_ACTIVATEstruct flashdevice flashdevices[BIOS_MAXDEV];int flashcount;/* * ****************************************** * * flashchip handling * * ****************************************** */void flash_command (unsigned char command)#if 1{ flash_writeb(0x5555, 0xaa); flash_writeb(0x2AAA, 0x55); flash_writeb(0x5555, command);}void fwh_flash_command(unsigned char command)#endif{ flash_writeb(0x75555, 0xaa); flash_writeb(0x72aaa, 0x55); flash_writeb(0x75555, command);}int system_activate(int cmd, void *data){ switch (cmd) { case 0: /* deactivate */#if defined(__i386__) || defined(__ia64__) || defined(__alpha__) pci_functions[chipset].deactivate();#endif break;#if defined(__i386__) || defined(__ia64__) || defined(__alpha__) case 1: /* activate */ pci_functions[chipset].activate();#endif break; } return 0;}int pci_activate(int cmd, void *data){#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,99) struct pci_dev *dev=(struct pci_dev *)data; static u32 status=0; // This function does not allow parallel access // to the flash chips. must use 'atomic' operations. switch (cmd) { case 0: pci_write_config_dword (dev, dev->rom_base_reg, status); break; case 1: pci_read_config_dword(dev, dev->rom_base_reg, &status); if (status) pci_write_config_dword (dev, dev->rom_base_reg, status|PCI_ROM_ADDRESS_ENABLE); else return 1; }#endif return 0;}int flash_probe_address(void *address, int (*activate)(int, void *), void *data){ 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 defined(__alpha__) outb(0x00, 0x800);#endif // currently all flash chip functions rely on bios pointing to the // current flash chip. bios=address; // We should get rid of this! save_flags(flags); spin_lock_irqsave(&bios_lock, flags);#ifdef OLD_ACTIVATE if (activate(1,data)) { spin_unlock_irqrestore(&bios_lock, flags); return 0; }#endif testflash= (flash_readb(0))+(flash_readb(1)<<8); /* 1st method: Intel, Atmel listen to this.. */ flash_command(0x90); udelay(20); flash_id = (flash_readb(0))+(flash_readb(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(0xf0); /* Reset */ udelay(20); flash_command(0x80); flash_command(0x60); udelay(20); flash_id = (flash_readb(0))+(flash_readb(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(0xf0); /* Reset again */ udelay(20); flash_command(0x80); flash_command(0x20); udelay(20); flash_id = (flash_readb(0))+(flash_readb(1)<<8);#ifdef DEBUG_PROBING printk (KERN_DEBUG "BIOS: testflash[%04x] flash_id[%04x]\n", testflash, flash_id); #endif } flash_command(0xf0); udelay(20);#ifdef OLD_ACTIVATE activate(0,data);#endif 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; if (flashchips[flashnum].id) { while (flashchips[flashnum].sectors[sectors]<flashchips[flashnum].size) sectors++; } if (flashcount >= BIOS_MAXDEV) /* Too many flashchips. Wow! */ return -1; flashdevices[flashcount] = (struct flashdevice){ flashnum: flashnum, manufnum: manufnum, id: flash_id, size: (flashchips[flashnum].size<<10), sectors: sectors, open_mode: 0, open_cnt: 0, activate: activate, data: data }; flashcount++; return 1;}void flash_probe(void){#ifdef CONFIG_PCI struct pci_dev *dev;#endif unsigned char *mapped; unsigned long romaddr, romsize; unsigned long probeaddr;#if defined (__i386__) || defined(__ia64__) || defined(__alpha__)#ifndef __alpha__ romaddr=0xffe00000; romsize=2048*1024;#else romaddr=0xfffffffffffc0000; //romaddr=0xfffc0000; romsize=512*1024;#endif /* ioremap the whole bios area. Same on some alphas. */ mapped=ioremap(romaddr,romsize); /* probe system bios area in steps of 4k. * When a flash chip is found, the probing * skips the chip's address space to prevent * multiple detection. * Stop probing after romsize - 0x5555 bytes, as * probing for the chip itself requires at least * a flash chip size of 0x5555 bytes. */ probeaddr=(unsigned long)mapped; do { if ( flash_probe_address ((void *)probeaddr, &(system_activate), NULL) == 1 ) { flashdevices[flashcount-1].offset = (unsigned long)probeaddr-(unsigned long)mapped; flashdevices[flashcount-1].mapped = mapped; flashdevices[flashcount-1].physical = (unsigned char *) romaddr+flashdevices[flashcount-1].offset; printk ( KERN_INFO "BIOS: %dk flashchip (ID 0x%04x) found at " "physical address 0x%08lx (va=0x%08lx+0x%lx).\n", flashdevices[flashcount-1].size>>10, flashdevices[flashcount-1].id, (unsigned long)flashdevices[flashcount-1].physical, (unsigned long)mapped, flashdevices[flashcount-1].offset ); if (flashchips[flashdevices[flashcount-1].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--; } }#if MULTIPLE_SYSTEM_FLASH /* we probably only have one system flash * chip, so we rather bail out here than * doing weird stuff. */ probeaddr += flashdevices[flashcount-1].size;#else break;#endif } else probeaddr += 4*1024; } while ( probeaddr < (unsigned long)mapped + romsize - 0x5555 ); // #elif defined (__alpha__)// Alpha uses normal pc stile mapping right now#if 0 flash_probe_address((void *)0xFFF80000, &(system_activate), NULL);#endif#endif#ifdef CONFIG_PCI#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99) for(dev=pci_devices; dev; dev=dev->next) { if (dev->rom_address) flash_probe_address((void *)(dev->rom_address&(~1)), &(pci_activate), (void *)dev); }#else pci_for_each_dev(dev) { romaddr=dev->resource[PCI_ROM_RESOURCE].start; romsize=dev->resource[PCI_ROM_RESOURCE].end - romaddr + 1; if (romaddr) { printk (KERN_DEBUG "BIOS: Probing PCI device " "with %ldk rom\n", (romsize>>10)); mapped=ioremap(romaddr,romsize); probeaddr=(unsigned long)mapped; do { if ( flash_probe_address ((void *)probeaddr, &(pci_activate), (void *)dev) == 1 ) { flashdevices[flashcount-1].mapped = mapped; flashdevices[flashcount-1].offset = (unsigned long)probeaddr-(unsigned long)mapped; flashdevices[flashcount-1].physical = (unsigned char *)romaddr + flashdevices[flashcount-1].offset;#if MULTIPLE_SYSTEM_FLASH probeaddr += flashdevices[flashcount-1].size;#else break;#endif } else probeaddr += 4*1024; } while (probeaddr < (unsigned long) mapped + romsize - 0x5555); /* If we did not find a single flash chip in the PCI * cards rom area, we unmap again */ if (flashdevices[flashcount-1].mapped!=mapped) iounmap(mapped); } }#endif#endif}void flash_program (void){ flash_command(0xa0);}void flash_program_atmel (void){ flash_command(0x80); flash_command(0x20);}int flash_erase (unsigned int flashnum) {// Should we drop this function? I hope no one would build a flashchip // that has to be erased completely before programming. /* but if data matches size of chip, whole chip erase is faster than sector erase, so might as well make it available */ flash_command(0x80); flash_command(0x10); udelay(80); return flash_ready_poll(0,0xff);}int flash_erase_sectors (unsigned int flashnum, unsigned int startsec, unsigned int endsec) { unsigned int sector; if (!(flashchips[flashnum].flags & f_slow_sector_erase)) { flash_command(0x80); if (!flashchips[flashnum].flags&f_fwh_compl) { flash_writeb(0x5555,0xaa); flash_writeb(0x2aaa,0x55); } else { flash_writeb(0x75555,0xaa); flash_writeb(0x72aaa,0x55); } for (sector=startsec; sector <= endsec; sector++) { flash_writeb (flashchips[flashnum].sectors[sector]*1024, 0x30); } udelay(150); // 80 max normally, wait 150usec to be sure // Why do we use sector-1 here? if (!flashchips[flashnum].flags&f_fwh_compl) return flash_ready_poll(flashchips[flashnum].sectors[sector-1]*1024, 0xff); else return flash_ready_toggle(flashchips[flashnum].sectors[sector]*1024); } /* sectors must be sent the sector erase command for every sector */ for (sector=startsec; sector <= endsec; sector++) { flash_command(0x80); if (!flashchips[flashnum].flags&f_fwh_compl) { flash_writeb(0x5555,0xaa); flash_writeb(0x2aaa,0x55); } else { flash_writeb(0x75555,0xaa); flash_writeb(0x72aaa,0x55); } flash_writeb(flashchips[flashnum].sectors[sector]*1024, 0x30); udelay(150); if (!flashchips[flashnum].flags&f_fwh_compl) flash_ready_poll(flashchips[flashnum].sectors[sector]*1024, 0xff); else flash_ready_toggle(flashchips[flashnum].sectors[sector]*1024); } 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 int offset){ unsigned long int timeout=0; unsigned char oldflag, flag; int loop=1; oldflag=flash_readb(offset) & 0x40; while (loop && (timeout<0x7fffffff)) { flag=flash_readb(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 int offset, unsigned char data){ unsigned long int timeout=0; unsigned char flag; flag=flash_readb(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(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(offset); } }#ifdef DBGTIMEOUT printk(KERN_DEBUG "BIOS: Timeout value (EOT Polling) %ld\n",timeout);#endif flag=flash_readb(offset); if ( ( flag & 0x80 ) == ( data & 0x80 ) ) return 0; else return 1;}void iflash_program_byte (unsigned int offset, unsigned char data){ unsigned long int timeout=0; unsigned char flag; flash_writeb (offset, 0x40); flash_writeb (offset, data); flash_writeb (offset, 0x70); /* Read Status */ do { flag=flash_readb (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 (offset, 0x50); /* Reset Status Register */ printk (KERN_ERR "BIOS: Error occured, please repeat write operation. (intel)\n"); } flash_writeb (offset, 0xff);}int iflash_erase_sectors (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 (offset, 0x20); flash_writeb (offset, 0xd0); flash_writeb (offset, 0x70); /* Read Status */ timeout=0; do { flag=flash_readb (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 (offset, 0x50); flash_writeb (offset, 0xff); return 1; /* Error! */ } } flash_writeb (offset, 0xff); return 0; }unsigned char flash_readb(unsigned int offset){#if defined(__alpha__) if (offset<0x80000) outb(0x00,0x800); else { outb(0x01, 0x800); offset-=0x80000; }#endif return readb(bios+offset);}void flash_writeb(unsigned int offset, unsigned char data) {#if defined(__alpha__) if (offset<0x80000) outb(0x00,0x800); else { outb(0x01, 0x800); offset-=0x80000; }#endif writeb(data,bios+offset);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -