intwidth;intwcycle;/* if 0, the flash is read normally */intbypass;intro;uint8_tcmd;uint8_tstatus;uint16_tident[4];uint8_tcfi_len;uint8_tcfi_table[0x52];QEMUTimer*timer;ram_addr_toff;intfl_mem;void*storage;};staticvoidpflash_timer(void*opaque){pflash_t*pfl=opaque;DPRINTF("%s: command %02x done\n",__func__,pfl->cmd);/* Reset flash */pfl->status^=0x80;if(pfl->bypass){pfl->wcycle=2;}else{cpu_register_physical_memory(pfl->base,pfl->total_len,pfl->off|IO_MEM_ROMD|pfl->fl_mem);pfl->wcycle=0;}pfl->cmd=0;}
ret=-1;offset-=pfl->base;boff=offset&0xFF;if(pfl->width==2)boff=boff>>1;elseif(pfl->width==4)boff=boff>>2;switch(pfl->cmd){default:/* This should never happen : reset state & treat it as a read*/DPRINTF("%s: unknown command state: %x\n",__func__,pfl->cmd);pfl->wcycle=0;pfl->cmd=0;case0x80:/* We accept reads during second unlock sequence... */case0x00:flash_read:/* Flash area read */p=pfl->storage;switch(width){case1:ret=p[offset];//DPRINTF("%s: data offset %08x %02x\n",__func__,offset,ret);break;case2:#ifdefined(TARGET_WORDS_BIGENDIAN)ret=p[offset]<<8;ret|=p[offset+1];#elseret=p[offset];ret|=p[offset+1]<<8;#endif//DPRINTF("%s: data offset %08x %04x\n",__func__,offset,ret);break;case4:#ifdefined(TARGET_WORDS_BIGENDIAN)ret=p[offset]<<24;ret|=p[offset+1]<<16;ret|=p[offset+2]<<8;ret|=p[offset+3];#elseret=p[offset];ret|=p[offset+1]<<8;ret|=p[offset+2]<<16;ret|=p[offset+3]<<24;#endif//DPRINTF("%s: data offset %08x %08x\n",__func__,offset,ret);break;}break;case0x90:/* flash ID read */switch(boff){case0x00:case0x01:ret=pfl->ident[boff&0x01];break;case0x02:ret=0x00;/* Pretend all sectors are unprotected */break;case0x0E:case0x0F:if(pfl->ident[2+(boff&0x01)]==(uint8_t)-1)gotoflash_read;ret=pfl->ident[2+(boff&0x01)];break;default:gotoflash_read;}
break;case0xA0:case0x10:case0x30:/* Status register read */ret=pfl->status;DPRINTF("%s: status %x\n",__func__,ret);/* Toggle bit 6 */pfl->status^=0x40;break;case0x98:/* CFI query mode */if(boff>pfl->cfi_len)ret=0;elseret=pfl->cfi_table[boff];break;}returnret;}/* update flash content on disk */
/* Set the device in I/O access mode */cpu_register_physical_memory(pfl->base,pfl->total_len,pfl->fl_mem);boff=offset&(pfl->sector_len-1);if(pfl->width==2)boff=boff>>1;elseif(pfl->width==4)boff=boff>>2;switch(pfl->wcycle){case0:/* We're in read mode */check_unlock0:if(boff==0x55&&cmd==0x98){enter_CFI_mode:/* Enter CFI query mode */pfl->wcycle=7;pfl->cmd=0x98;return;}if(boff!=0x555||cmd!=0xAA){
__func__,boff,cmd,0x555);gotoreset_flash;}DPRINTF("%s: unlock sequence started\n",__func__);break;case1:/* We started an unlock sequence */check_unlock1:if(boff!=0x2AA||cmd!=0x55){
gotoreset_flash;}switch(cmd){case0x20:pfl->bypass=1;gotodo_bypass;case0x80:case0x90:case0xA0:pfl->cmd=cmd;DPRINTF("%s: starting command %02x\n",__func__,cmd);break;default:DPRINTF("%s: unknown command %02x\n",__func__,cmd);gotoreset_flash;}break;case3:switch(pfl->cmd){case0x80:/* We need another unlock sequence */gotocheck_unlock0;case0xA0:
__func__,offset,value,width);p=pfl->storage;switch(width){case1:p[offset]&=value;pflash_update(pfl,offset,1);break;case2:#ifdefined(TARGET_WORDS_BIGENDIAN)p[offset]&=value>>8;p[offset+1]&=value;#elsep[offset]&=value;p[offset+1]&=value>>8;#endifpflash_update(pfl,offset,2);break;case4:#ifdefined(TARGET_WORDS_BIGENDIAN)p[offset]&=value>>24;p[offset+1]&=value>>16;p[offset+2]&=value>>8;p[offset+3]&=value;#elsep[offset]&=value;p[offset+1]&=value>>8;p[offset+2]&=value>>16;p[offset+3]&=value>>24;#endifpflash_update(pfl,offset,4);break;}pfl->status=0x00|~(value&0x80);/* Let's pretend write is immediate */if(pfl->bypass)gotodo_bypass;gotoreset_flash;case0x90:if(pfl->bypass&&cmd==0x00){/* Unlock bypass reset */gotoreset_flash;}/* We can enter CFI query mode from autoselect mode */if(boff==0x55&&cmd==0x98)gotoenter_CFI_mode;/* No break here */default:DPRINTF("%s: invalid write for command %02x\n",__func__,pfl->cmd);gotoreset_flash;}case4:switch(pfl->cmd){case0xA0:/* Ignore writes while flash data write is occuring *//* As we suppose write is immediate, this should never happen */return;case0x80:gotocheck_unlock1;default:/* Should never happen */DPRINTF("%s: invalid command state %02x (wc 4)\n",__func__,pfl->cmd);gotoreset_flash;}break;case5:switch(cmd){case0x10:if(boff!=0x555){
memset(p+offset,0xFF,pfl->sector_len);pflash_update(pfl,offset,pfl->sector_len);pfl->status=0x00;/* Let's wait 1/2 second before sector erase is done */
qemu_get_clock(vm_clock)+(ticks_per_sec/2));break;default:DPRINTF("%s: invalid command %02x (wc 5)\n",__func__,cmd);gotoreset_flash;}pfl->cmd=cmd;break;case6:switch(pfl->cmd){case0x10:/* Ignore writes during chip erase */return;case0x30:/* Ignore writes during sector erase */return;default:/* Should never happen */DPRINTF("%s: invalid command state %02x (wc 6)\n",__func__,pfl->cmd);gotoreset_flash;}break;case7:/* Special value for CFI queries */DPRINTF("%s: invalid write in CFI query mode\n",__func__);gotoreset_flash;default:/* Should never happen */DPRINTF("%s: invalid write state (wc 7)\n",__func__);gotoreset_flash;}pfl->wcycle++;return;/* Reset flash */reset_flash:
pfl->bypass=0;pfl->wcycle=0;pfl->cmd=0;return;do_bypass:pfl->wcycle=2;pfl->cmd=0;return;}staticuint32_tpflash_readb(void*opaque,target_phys_addr_taddr){returnpflash_read(opaque,addr,1);}staticuint32_tpflash_readw(void*opaque,target_phys_addr_taddr){pflash_t*pfl=opaque;returnpflash_read(pfl,addr,2);}staticuint32_tpflash_readl(void*opaque,target_phys_addr_taddr){pflash_t*pfl=opaque;returnpflash_read(pfl,addr,4);}staticvoidpflash_writeb(void*opaque,target_phys_addr_taddr,uint32_tvalue){pflash_write(opaque,addr,value,1);}staticvoidpflash_writew(void*opaque,target_phys_addr_taddr,uint32_tvalue){pflash_t*pfl=opaque;pflash_write(pfl,addr,value,2);}staticvoidpflash_writel(void*opaque,target_phys_addr_taddr,uint32_tvalue){pflash_t*pfl=opaque;pflash_write(pfl,addr,value,4);}staticCPUWriteMemoryFunc*pflash_write_ops[]={&pflash_writeb,&pflash_writew,&pflash_writel,};staticCPUReadMemoryFunc*pflash_read_ops[]={&pflash_readb,&pflash_readw,&pflash_readl,};/* Count trailing zeroes of a 32 bits quantity */staticintctz32(uint32_tn){intret;ret=0;if(!(n&0xFFFF)){ret+=16;n=n>>16;}if(!(n&0xFF)){ret+=8;n=n>>8;}if(!(n&0xF)){ret+=4;n=n>>4;}if(!(n&0x3)){ret+=2;n=n>>2;}if(!(n&0x1)){ret++;n=n>>1;}#if0/* This is not necessary as n is never 0 */if(!n)ret++;#endifreturnret;}
pfl->off=off;cpu_register_physical_memory(base,total_len,off|pfl->fl_mem|IO_MEM_ROMD);pfl->bs=bs;if(pfl->bs){/* read the initial flash content */bdrv_read(pfl->bs,0,pfl->storage,total_len>>9);}#if0/*XXX:thereshouldbeabittosetupread-only,*thesamewaythehardwaredoes(withWPpin).*/pfl->ro=1;#elsepfl->ro=0;#endifpfl->timer=qemu_new_timer(vm_clock,pflash_timer,pfl);pfl->base=base;pfl->sector_len=sector_len;pfl->total_len=total_len;pfl->width=width;pfl->wcycle=0;pfl->cmd=0;pfl->status=0;pfl->ident[0]=id0;pfl->ident[1]=id1;pfl->ident[2]=id2;pfl->ident[3]=id3;/* Hardcoded CFI table (mostly from SG29 Spansion flash) */pfl->cfi_len=0x52;/* Standard "QRY" string */pfl->cfi_table[0x10]='Q';pfl->cfi_table[0x11]='R';pfl->cfi_table[0x12]='Y';/* Command set (AMD/Fujitsu) */pfl->cfi_table[0x13]=0x02;pfl->cfi_table[0x14]=0x00;/* Primary extended table address (none) */pfl->cfi_table[0x15]=0x00;pfl->cfi_table[0x16]=0x00;/* Alternate command set (none) */pfl->cfi_table[0x17]=0x00;pfl->cfi_table[0x18]=0x00;/* Alternate extended table (none) */pfl->cfi_table[0x19]=0x00;pfl->cfi_table[0x1A]=0x00;/* Vcc min */pfl->cfi_table[0x1B]=0x27;/* Vcc max */pfl->cfi_table[0x1C]=0x36;/* Vpp min (no Vpp pin) */pfl->cfi_table[0x1D]=0x00;/* Vpp max (no Vpp pin) */pfl->cfi_table[0x1E]=0x00;/* Reserved */pfl->cfi_table[0x1F]=0x07;/* Timeout for min size buffer write (16 µs) */pfl->cfi_table[0x20]=0x04;/* Typical timeout for block erase (512 ms) */pfl->cfi_table[0x21]=0x09;/* Typical timeout for full chip erase (4096 ms) */pfl->cfi_table[0x22]=0x0C;/* Reserved */pfl->cfi_table[0x23]=0x01;/* Max timeout for buffer write */pfl->cfi_table[0x24]=0x04;/* Max timeout for block erase */pfl->cfi_table[0x25]=0x0A;/* Max timeout for chip erase */pfl->cfi_table[0x26]=0x0D;/* Device size */pfl->cfi_table[0x27]=ctz32(total_len)+1;/* Flash device interface (8 & 16 bits) */pfl->cfi_table[0x28]=0x02;pfl->cfi_table[0x29]=0x00;/* Max number of bytes in multi-bytes write */
pfl->cfi_table[0x2B]=0x00;/* Number of erase block regions (uniform) */pfl->cfi_table[0x2C]=0x01;/* Erase block region 1 */pfl->cfi_table[0x2D]=nb_blocs-1;pfl->cfi_table[0x2E]=(nb_blocs-1)>>8;pfl->cfi_table[0x2F]=sector_len>>8;pfl->cfi_table[0x30]=sector_len>>16;returnpfl;}