#include #include "typedef.h" #include "amt630h.h" #include "UartPrint.h" #include "timer.h" #include "spi.h" #include "cp15.h" #include "sysinfo.h" #include "crc32.h" #include "gpio.h" #if DEVICE_TYPE_SELECT == SPI_NOR_FLASH || DEVICE_TYPE_SELECT == EMMC_FLASH #define SPI_CS_GPIO 32 #define SPI_RXFIFO_FULL (1<<4) #define SPI_RXFIFO_NOTEMPTY (1<<3) #define SPI_TXFIFO_EMPTY (1<<2) #define SPI_TXFIFO_NOTFULL (1<<1) #define SPIFLASH_BUSY (1<<0) /* SFUD support manufacturer JEDEC ID */ #define SFUD_MF_ID_CYPRESS 0x01 #define SFUD_MF_ID_FUJITSU 0x04 #define SFUD_MF_ID_EON 0x1C #define SFUD_MF_ID_ATMEL 0x1F #define SFUD_MF_ID_MICRON 0x20 #define SFUD_MF_ID_AMIC 0x37 #define SFUD_MF_ID_SANYO 0x62 #define SFUD_MF_ID_INTEL 0x89 #define SFUD_MF_ID_ESMT 0x8C #define SFUD_MF_ID_FUDAN 0xA1 #define SFUD_MF_ID_HYUNDAI 0xAD #define SFUD_MF_ID_SST 0xBF #define SFUD_MF_ID_MICRONIX 0xC2 #define SFUD_MF_ID_GIGADEVICE 0xC8 #define SFUD_MF_ID_ISSI 0xD5 #define SFUD_MF_ID_WINBOND 0xEF static void SpiWriteEnable(void); /* flash chip information */ typedef struct { char *name; /**< flash chip name */ uint8_t mf_id; /**< manufacturer ID */ uint8_t type_id; /**< memory type ID */ uint8_t capacity_id; /**< capacity ID */ uint32_t capacity; /**< flash capacity (bytes) */ } flash_chip; static const flash_chip flash_chip_table[] = { {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L}, {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L}, {"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L}, {"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L}, {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L}, {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L}, {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L}, {"W25H256JV", SFUD_MF_ID_WINBOND, 0x90, 0x19, 32L*1024L*1024L}, {"W25Q512JVFM", SFUD_MF_ID_WINBOND, 0x70, 0x20, 64L*1024L*1024L}, {"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L}, {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L}, {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L}, {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L}, {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L}, {"GD25Q32C", SFUD_MF_ID_GIGADEVICE, 0x40, 0x16, 4L*1024L*1024L}, {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L}, {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L}, {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L}, {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L}, {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L}, {"IS25LP128F", SFUD_MF_ID_ISSI, 0x60, 0x18, 16L*1024L*1024L}, {"MX25L6433F", SFUD_MF_ID_MICRONIX, 0x20, 0x17, 8L*1024L*1024L}, {"MX25L12845G", SFUD_MF_ID_MICRONIX, 0x20, 0x18, 16L*1024L*1024L}, {"MX25L25645G", SFUD_MF_ID_MICRONIX, 0x20, 0x19, 32L*1024L*1024L}, {"W25Q512JVFM", SFUD_MF_ID_WINBOND, 0x70, 0x20, 64L*1024L*1024L}, {"GD25B512ME", SFUD_MF_ID_GIGADEVICE, 0x47, 0x1A, 64L*1024L*1024L}, }; static int addr_in_4_byte = 0; static void SetCSGpioEnable(int enable) { gpio_direction_output(SPI_CS_GPIO, !enable); } static void SetSpiDataMode(unsigned int bitMode) { unsigned int val = 0; (void)val; while((rSPI_SR & SPI_BUSY)); rSPI_SSIENR = 0; val = rSPI_CTLR0; val &=~(0x1f<<16); val |=((bitMode-1)<<16); rSPI_CTLR0 = val; rSPI_SSIENR = 1; } static void SpiWaitIdle(void) { while(rSPI_SR & SPIFLASH_BUSY); udelay(2); } static void SpiEmptyRxFIFO(void) { INT32 data = 0; (void)data; while(rSPI_SR & SPI_RXFIFO_NOTEMPTY) data = rSPI_DR; } /* static void SpiWriteSta2(uint8_t status) { SpiWriteEnable(); SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_WRITE_STATUS2; rSPI_DR = status; SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); } static UINT8 SpiReadSta2(void) { UINT8 status; SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_READ_STATUS2; rSPI_DR = 0; while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); status = rSPI_DR; while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); status = rSPI_DR; PrintVariableValueHex("status s2 :", status); SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); return status; } */ static UINT8 SpiReadSta3(void) { UINT8 status; SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_READ_STATUS3; rSPI_DR = 0; while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); status = rSPI_DR; while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); status = rSPI_DR; PrintVariableValueHex("status s3 :", status); SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); return status; } static UINT8 SpiReadSta(void) { UINT8 status; SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_READ_STATUS; rSPI_DR = 0; while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); status = rSPI_DR; while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); status = rSPI_DR; //PrintVariableValueHex("status:", status); SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); return status; } static void SpiDisable4ByteMode(void) { SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_DISABLE_4BYTE_MODE; while(!(rSPI_SR & SPI_TXFIFO_EMPTY)); SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); } static void SpiEnable4ByteMode(void) { SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_ENABLE_4BYTE_MODE; while(!(rSPI_SR & SPI_TXFIFO_EMPTY)); SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); } static void SpiReadPage(UINT32 pagenum, UINT32 *buf) { UINT32 addr; UINT32 val = 0; INT32 i, j; UINT8 tmpaddr[4]; UINT8 *data = (UINT8*)buf; (void)val; addr = pagenum*BYTESPERPAGE; tmpaddr[0] = addr; tmpaddr[1] = addr>>8; tmpaddr[2] = addr>>16; tmpaddr[3] = addr>>24; SpiEmptyRxFIFO(); SetCSGpioEnable(1); if (addr_in_4_byte) { SetSpiDataMode(8); rSPI_DR = SPI_4BYTEADDR_READ_DATA; rSPI_DR = tmpaddr[3]; rSPI_DR = tmpaddr[2]; rSPI_DR = tmpaddr[1]; rSPI_DR = tmpaddr[0]; for (i = 0; i < 5; i++) { while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); val = rSPI_DR; } } else { rSPI_DR = (tmpaddr[0]<<24) | (tmpaddr[1]<<16) | (tmpaddr[2]<<8) | SPI_READ_DATA; while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); val = rSPI_DR; } if (addr_in_4_byte) { for (i = 0; i < 4; i++) { for (j = 0; j < WORDSPERPAGE; j++) { while(!(rSPI_SR & SPI_TXFIFO_NOTFULL)); rSPI_DR = 0; } for (j = 0; j < WORDSPERPAGE; j++) { while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); *data++ = rSPI_DR; } } } else { for (i = 0; i < WORDSPERPAGE; i++) { while(!(rSPI_SR & SPI_TXFIFO_NOTFULL)); rSPI_DR = 0; } for(i = 0; i < WORDSPERPAGE; i++) { while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); *buf++ = rSPI_DR; } } SpiWaitIdle(); SetCSGpioEnable(0); if (addr_in_4_byte) SetSpiDataMode(32); } void SpiSelectPad() { UINT32 val; val = rSYS_PAD_CTRL02; val &= ~0xfff; val |= (0x1<<10)|(0x1 << 8)|(0x1 << 6)|(0x1 << 4)| (0x1 << 2); rSYS_PAD_CTRL02 = val; val = rSYS_SSP_CLK_CFG; val &= ~((0x1<<31)|(0x1<<30)); val |= (0x1<<31)|(0x1<<30); rSYS_SSP_CLK_CFG = val; //cs inactive first SetCSGpioEnable(0); } void Reset(void) { SetSpiDataMode(8); rSPI_DR = 0x66; while((SpiReadSta() & SPI_BUSY)); // while(!(SpiReadSta() & SPIFLASH_WRITEENABLE)); rSPI_DR = 0x99; while((SpiReadSta() & SPI_BUSY)); // while(!(SpiReadSta() & SPIFLASH_WRITEENABLE)); SetSpiDataMode(32); } #define SPI0_CS0_GPIO 32 #define SPI0_IO0_GPIO 34 static void dwspi_jedec252_reset(void) { int i; int si = 0; UINT32 val; val = rSYS_PAD_CTRL02; val &= ~((3 << 4) | 3); rSYS_PAD_CTRL02 = val; gpio_direction_output(SPI0_CS0_GPIO, 1); gpio_direction_output(SPI0_IO0_GPIO, 1); udelay(300); for (i = 0; i < 4; i++) { gpio_direction_output(SPI0_CS0_GPIO, 0); gpio_direction_output(SPI0_IO0_GPIO, si); si = !si; udelay(300); gpio_direction_output(SPI0_CS0_GPIO, 1); udelay(300); } } static void SpiReadDeviceId(UINT8 *mfid, UINT8 *devid) { UINT8 val[6]; int i; SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_MF_DEVICE_ID; rSPI_DR = 0; rSPI_DR = 0; rSPI_DR = 0; rSPI_DR = 0; rSPI_DR = 0; for (i = 0; i < 6; i++) { while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); val[i] = rSPI_DR; } *mfid = val[4]; *devid = val[5]; SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); } static void SpiReadJedecId(UINT8 *mfid, UINT8 *memid, UINT8 *capid) { UINT8 val[4]; int i; SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_READ_JEDEC_ID; rSPI_DR = 0; rSPI_DR = 0; rSPI_DR = 0; for (i = 0; i < 4; i++) { while(!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); val[i] = rSPI_DR; } *mfid = val[1]; *memid = val[2]; *capid = val[3]; SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); } void SpiReadId(void) { UINT8 mfid,devid,memid,capid; SpiReadDeviceId(&mfid,&devid); SpiReadJedecId(&mfid,&memid,&capid); PrintVariableValueHex("ManufacturerID: ", mfid); PrintVariableValueHex("DeviceID: ", devid); PrintVariableValueHex("Memory Type ID: ", memid); PrintVariableValueHex("Capacity ID: ", capid); } int SpiInit(void) { uint8_t mfid, typeid, capid; unsigned int val; int i; dwspi_jedec252_reset(); SpiSelectPad(); val = rSYS_SOFT_RST; val &= ~(0x1<<8); rSYS_SOFT_RST = val; udelay(10); val |= (0x1<<8); rSYS_SOFT_RST = val; rSPI_SSIENR = 0; rSPI_CTLR0 = 0; rSPI_CTLR0 |=(0<<21)|(0x1f<<16)|(0x0<<12)|(0x0<<8)|(0x0<<4); //rSPI_CTLR1 = 63; rSPI_BAUDR = 4;//42;//16;//2; rSPI_SER = 1; rSPI_IMR = 0; rSPI_SSIENR = 1; // Reset(); SpiReadJedecId(&mfid, &typeid, &capid); for (i = 0; i < sizeof(flash_chip_table) / sizeof(flash_chip); i++) { if ((flash_chip_table[i].mf_id == mfid) && (flash_chip_table[i].type_id == typeid) && (flash_chip_table[i].capacity_id == capid)) { if (flash_chip_table[i].capacity > 0x1000000) { PrintVariableValueHex("i is %x : ", i); addr_in_4_byte = 1; SpiEnable4ByteMode(); } break; } } if (!addr_in_4_byte) SpiDisable4ByteMode(); #ifdef SPI0_QSPI_MODE uint8_t status = SpiReadSta2(); status |= SPI_QE; SpiWriteSta2(status); #endif SpiReadSta3(); udelay(10000); return 0; } #define SPI_READ_MAXLEN BYTESPERPAGE static void SpiLoadStepldr(void (*readfunc)(UINT32, UINT32 *)) { unsigned int i = 0; UINT32 *buf = (UINT32*)STEPLDR_ENTRY; UINT32 offset; UINT32 size; UINT32 nPageCount; UINT32 nPageStart; if (ReadSysInfo()) { SendUartString("read sysinfo fail, try to load stepldr part a.\n"); offset = STEPLDRA_OFFSET; size = STEPLDR_MAX_SIZE; } else { SysInfo *sysinfo = GetSysInfo(); offset = sysinfo->stepldr_offset; size = sysinfo->stepldr_size; } PrintVariableValueHex("stepldr offset: ", offset); nPageCount = (size + BYTESPERPAGE - 1) / BYTESPERPAGE; nPageStart = offset / BYTESPERPAGE; for(i = nPageStart; i < nPageStart + nPageCount; i++) { readfunc(i, buf); buf += BYTESPERPAGE/4; } #ifdef MMU_ENABLE CP15_clean_dcache_for_dma(STEPLDR_ENTRY, STEPLDR_ENTRY + size); #endif } static void SpiWriteEnable(void) { SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_WRITE_ENABLE; SpiWaitIdle(); SetCSGpioEnable(0); while((SpiReadSta() & SPI_BUSY)); while(!(SpiReadSta() & SPIFLASH_WRITEENABLE)); SetSpiDataMode(32); } static void SpiEraseSector(UINT32 sectorNum) { UINT32 addr; UINT8 tmpaddr[4]; addr = BYTESPERSECTOR*sectorNum; tmpaddr[0] = addr; tmpaddr[1] = addr>>8; tmpaddr[2] = addr>>16; tmpaddr[3] = addr>>24; SpiWriteEnable(); SetCSGpioEnable(1); if (addr_in_4_byte) { SetSpiDataMode(8); rSPI_DR = SPI_4BYTEADD_SECTOR_ERASE; rSPI_DR = tmpaddr[3]; rSPI_DR = tmpaddr[2]; rSPI_DR = tmpaddr[1]; rSPI_DR = tmpaddr[0]; } else { rSPI_DR = (tmpaddr[0]<<24) | (tmpaddr[1]<<16) | (tmpaddr[2]<<8) | SPI_SECTOR_ERASE; } SpiWaitIdle(); SetCSGpioEnable(0); while((SpiReadSta() & SPIFLASH_WRITEENABLE)); } static void SpiEraseBlock(UINT32 blockNum) { UINT32 addr; UINT8 tmpaddr[4]; addr = BYTESPERBLOCK*blockNum; tmpaddr[0] = addr; tmpaddr[1] = addr>>8; tmpaddr[2] = addr>>16; tmpaddr[3] = addr>>24; SpiWriteEnable(); SetCSGpioEnable(1); if (addr_in_4_byte) { SetSpiDataMode(8); rSPI_DR = SPI_4BYTEADD_BLOCK_ERASE; rSPI_DR = tmpaddr[3]; rSPI_DR = tmpaddr[2]; rSPI_DR = tmpaddr[1]; rSPI_DR = tmpaddr[0]; } else { rSPI_DR = (tmpaddr[0]<<24) | (tmpaddr[1]<<16) | (tmpaddr[2]<<8) | SPI_BLOCK_ERASE; } SpiWaitIdle(); SetCSGpioEnable(0); while((SpiReadSta() & SPIFLASH_WRITEENABLE)); } static void SpiWritePage(UINT32 pagenum, UINT32 *buf) { UINT32 addr; UINT32 val = 0;; INT32 i; UINT8 tmpaddr[4]; UINT8 *data = (UINT8*)buf; (void)val; addr = pagenum*BYTESPERPAGE; tmpaddr[0] = addr; tmpaddr[1] = addr>>8; tmpaddr[2] = addr>>16; tmpaddr[3] = addr>>24; SpiWriteEnable(); SetCSGpioEnable(1); if (addr_in_4_byte) { SetSpiDataMode(8); rSPI_DR = SPI_4BYTEADD_PAGE_PROGRAM; rSPI_DR = tmpaddr[3]; rSPI_DR = tmpaddr[2]; rSPI_DR = tmpaddr[1]; rSPI_DR = tmpaddr[0]; } else { rSPI_DR = (tmpaddr[0]<<24) | (tmpaddr[1]<<16) | (tmpaddr[2]<<8) | SPI_PAGE_PROGRAM; } if (addr_in_4_byte) { for (i = 0; i < BYTESPERPAGE; i++) { while(!(rSPI_SR & SPI_TXFIFO_NOTFULL)); rSPI_DR = *data++; } } else { for (i = 0; i < WORDSPERPAGE; i++) { while(!(rSPI_SR & SPI_TXFIFO_NOTFULL)); rSPI_DR = *buf++; } } SpiWaitIdle(); SetCSGpioEnable(0); while(SpiReadSta() & SPI_BUSY); } void bootFromSPI(void) { void (*funPtr)(void); SpiLoadStepldr(SpiReadPage); funPtr = (void (*)(void))STEPLDR_ENTRY; funPtr(); } static unsigned int pagecheck[WORDSPERPAGE]; /* offset is at least align to SECOTR_SIZE */ static int SpiNorBurnPage(int pagenum, unsigned int *buf) { int timeout = 3; unsigned int *tmp = (unsigned int *)buf; int i; retry: SpiWritePage(pagenum, buf); SpiReadPage(pagenum, pagecheck); for (i = 0; i < WORDSPERPAGE; i++) { if (tmp[i] != pagecheck[i]) { if (timeout-- > 0) { PrintVariableValueHex("write: ", tmp[i]); PrintVariableValueHex("read: ", pagecheck[i]); SendUartString("burn check data fail, retry...\r\n"); goto retry; } else { SendUartString("burn error!\r\n"); return -1; } } } return 0; } /* offset is at least align to SECOTR_SIZE */ static int SpiNorBurn(void *buf, unsigned int offset, unsigned int size) { int i, j; int secstart, secnum; int blkstart, blknum; int pagestart, pageburned; int remain = size; unsigned int *pbuf = (unsigned int *)buf; pagestart = offset / BYTESPERPAGE; pageburned = 0; while (remain > 0) { unsigned int tmp = offset & (BYTESPERBLOCK - 1); if (tmp) { tmp = (BYTESPERBLOCK - tmp) / BYTESPERSECTOR; secnum = (remain + BYTESPERSECTOR - 1) / BYTESPERSECTOR; secnum = min(tmp, secnum); secstart = offset / BYTESPERSECTOR; for (i = 0; i < secnum; i++) { SpiEraseSector(secstart + i); for (j = 0; j < PAGESPERSECTORS; j++) { if (SpiNorBurnPage(pagestart + j, pbuf)) return -1; pbuf += WORDSPERPAGE; pageburned++; remain -= BYTESPERPAGE; if (remain <= 0) goto finish; } pagestart += PAGESPERSECTORS; } offset += secnum * BYTESPERSECTOR; } else { blkstart = offset / BYTESPERBLOCK; blknum = remain / BYTESPERBLOCK; for (i = 0; i < blknum; i++) { SpiEraseBlock(blkstart + i); for (j = 0; j < PAGESPERSECTORS * SECTORSPERBLOCK; j++) { if (SpiNorBurnPage(pagestart + j, pbuf)) return -1; pbuf += WORDSPERPAGE; pageburned++; remain -= BYTESPERPAGE; if (remain <= 0) goto finish; } pagestart += PAGESPERSECTORS * SECTORSPERBLOCK; } offset += blknum * BYTESPERBLOCK; if (remain > 0) { secstart = offset / BYTESPERSECTOR; secnum = (remain + BYTESPERSECTOR - 1) / BYTESPERSECTOR; for (i = 0; i < secnum; i++) { SpiEraseSector(secstart + i); for (j = 0; j < PAGESPERSECTORS; j++) { if (SpiNorBurnPage(pagestart + j, pbuf)) return -1; pbuf += WORDSPERPAGE; pageburned++; remain -= BYTESPERPAGE; if (remain <= 0) goto finish; } pagestart += PAGESPERSECTORS; } offset += secnum * BYTESPERSECTOR; } } } finish: return 0; } int FlashBurn(void *buf, unsigned int offset, unsigned int size) { return SpiNorBurn(buf, offset, size); } int SpiReadSysInfo(SysInfo *info) { int pagestart; UINT32 checksum; UINT32 calc_checksum; UINT8 data[512]; pagestart = SYSINFOA_OFFSET / BYTESPERPAGE; SpiReadPage(pagestart, (UINT32 *)data); checksum = *(UINT32*)((UINT32)data + sizeof(SysInfo) - 4); calc_checksum = xcrc32((unsigned char*)data, sizeof(SysInfo) - 4, 0xffffffff); if (calc_checksum == checksum) { memcpy(info, data, sizeof(SysInfo)); return 0; } pagestart = SYSINFOB_OFFSET / BYTESPERPAGE; SpiReadPage(pagestart, (UINT32 *)data); checksum = *(UINT32*)((UINT32)data + sizeof(SysInfo) - 4); calc_checksum = xcrc32((unsigned char*)data, sizeof(SysInfo) - 4, 0xffffffff); if (calc_checksum == checksum) { memcpy(info, data, sizeof(SysInfo)); return 0; } return -1; } void SpiWriteSysInfo(SysInfo *info) { SpiNorBurn(info, SYSINFOB_OFFSET, sizeof(SysInfo)); SpiNorBurn(info, SYSINFOA_OFFSET, sizeof(SysInfo)); } #endif