/**************************************************************************//** * @file spiflash.c * @version V3.00 * @brief N9H20 series SPI flash driver source file * * SPDX-License-Identifier: Apache-2.0 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/ /* Header files */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "wblib.h" #include "N9H20_SPI.h" UINT8 g_u8Is4ByteMode; int usiCheckBusy() { // check status outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // status command outpw(REG_SPI0_TX0, 0x05); spiTxLen(0, 0, 8); spiActive(0); // get status while(1) { outpw(REG_SPI0_TX0, 0xff); spiTxLen(0, 0, 8); spiActive(0); if (((inpw(REG_SPI0_RX0) & 0xff) & 0x01) != 0x01) break; } outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 return Successful; } INT16 usiReadID() { UINT16 volatile id; outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // command 8 bit outpw(REG_SPI0_TX0, 0x90); spiTxLen(0, 0, 8); spiActive(0); // address 24 bit outpw(REG_SPI0_TX0, 0x000000); spiTxLen(0, 0, 24); spiActive(0); // data 16 bit outpw(REG_SPI0_TX0, 0xffff); spiTxLen(0, 0, 16); spiActive(0); id = inpw(REG_SPI0_RX0) & 0xffff; outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 return id; } int usiWriteEnable() { outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 outpw(REG_SPI0_TX0, 0x06); spiTxLen(0, 0, 8); spiActive(0); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 return Successful; } int usiWriteDisable() { outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 outpw(REG_SPI0_TX0, 0x04); spiTxLen(0, 0, 8); spiActive(0); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 return Successful; } int usiStatusWrite(UINT8 data) { usiWriteEnable(); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // status command outpw(REG_SPI0_TX0, 0x01); spiTxLen(0, 0, 8); spiActive(0); // write status outpw(REG_SPI0_TX0, data); spiTxLen(0, 0, 8); spiActive(0); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 return Successful; } /**************************************************/ INT spiFlashInit(void) { static BOOL bIsSpiFlashOK = 0; int volatile loop; UINT32 u32APBClk; if (!bIsSpiFlashOK) { outpw(REG_APBCLK, inpw(REG_APBCLK) | SPIMS0_CKE); // turn on SPI clk //Reset SPI controller first outpw(REG_APBIPRST, inpw(REG_APBIPRST) | SPI0RST); outpw(REG_APBIPRST, inpw(REG_APBIPRST) & ~SPI0RST); // delay for time // Delay for (loop=0; loop<500; loop++); //configure SPI0 interface, Base Address 0xB800C000 /* apb clock is 48MHz, output clock is 10MHz */ u32APBClk = sysGetAPBClock(); spiIoctl(0, SPI_SET_CLOCK, u32APBClk/1000, 10000); //Startup SPI0 multi-function features, chip select using SS0 outpw(REG_GPDFUN, inpw(REG_GPDFUN) | 0xFF000000); outpw(REG_SPI0_SSR, 0x00); // CS active low outpw(REG_SPI0_CNTRL, 0x04); // Tx: falling edge, Rx: rising edge if ((loop=usiReadID()) == -1) { sysprintf("read id error !! [0x%x]\n", loop&0xffff); return -1; } sysprintf("SPI flash id [0x%x]\n", loop&0xffff); usiStatusWrite(0x00); // clear block protect // check status usiCheckBusy(); bIsSpiFlashOK = 1; } return 0; } INT spiFlashReset(void) { // check status usiCheckBusy(); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // enable reset command outpw(REG_SPI0_TX0, 0x66); spiTxLen(0, 0, 8); spiActive(0); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // reset command outpw(REG_SPI0_TX0, 0x99); spiTxLen(0, 0, 8); spiActive(0); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 sysDelay(5); usiStatusWrite(0x00); // clear block protect return Successful; } INT spiFlashEraseSector(UINT32 addr, UINT32 secCount) { int volatile i; if ((addr % (64*1024)) != 0) return -1; for (i=0; i<secCount; i++) { usiWriteEnable(); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // erase command outpw(REG_SPI0_TX0, 0xd8); spiTxLen(0, 0, 8); spiActive(0); // address if(g_u8Is4ByteMode == TRUE) { outpw(REG_SPI0_TX0, addr+i*64*1024); spiTxLen(0, 0, 32); spiActive(0); } else { outpw(REG_SPI0_TX0, addr+i*64*1024); spiTxLen(0, 0, 24); spiActive(0); } outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 // check status usiCheckBusy(); } return Successful; } INT spiFlashEraseBlock(UINT32 addr, UINT32 blockCount) { int volatile i; if ((addr % (64*1024)) != 0) return -1; for (i=0; i<blockCount; i++) { usiWriteEnable(); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // erase command outpw(REG_SPI0_TX0, 0xd8); spiTxLen(0, 0, 8); spiActive(0); // address if(g_u8Is4ByteMode == TRUE) { outpw(REG_SPI0_TX0, addr+i*64*1024); spiTxLen(0, 0, 32); spiActive(0); } else { outpw(REG_SPI0_TX0, addr+i*64*1024); spiTxLen(0, 0, 24); spiActive(0); } outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 // check status usiCheckBusy(); } return Successful; } INT spiFlashEraseAll(void) { usiWriteEnable(); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 outpw(REG_SPI0_TX0, 0xc7); spiTxLen(0, 0, 8); spiActive(0); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 // check status usiCheckBusy(); return Successful; } INT spiFlashWrite(UINT32 addr, UINT32 len, UINT32 *buf) { int volatile count=0, page, i; UINT32 u32Tmp; count = len / 256; if ((len % 256) != 0) count++; for (i=0; i<count; i++) { // check data len if (len >= 256) { page = 256; len = len - 256; } else page = len; //sysprintf("len %d page %d\n",len , page); usiWriteEnable(); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // write command outpw(REG_SPI0_TX0, 0x02); spiTxLen(0, 0, 8); spiActive(0); // address if(g_u8Is4ByteMode == TRUE) { outpw(REG_SPI0_TX0, addr+i*256); spiTxLen(0, 0, 32); spiActive(0); } else { outpw(REG_SPI0_TX0, addr+i*256); spiTxLen(0, 0, 24); spiActive(0); } // write data while (page > 0) { u32Tmp = ((*buf & 0xFF) << 24) | ((*buf & 0xFF00) << 8) | ((*buf & 0xFF0000) >> 8)| ((*buf & 0xFF000000) >> 24); outpw(REG_SPI0_TX0, u32Tmp); spiTxLen(0, 0, 32); spiActive(0); buf++; page -=4; } outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 // check status usiCheckBusy(); } return Successful; } INT spiFlashRead(UINT32 addr, UINT32 len, UINT32 *buf) { int volatile i; UINT32 u32Tmp; UINT8 *ptr; outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // read command outpw(REG_SPI0_TX0, 03); spiTxLen(0, 0, 8); spiActive(0); // address if(g_u8Is4ByteMode == TRUE) { outpw(REG_SPI0_TX0, addr); spiTxLen(0, 0, 32); spiActive(0); } else { outpw(REG_SPI0_TX0, addr); spiTxLen(0, 0, 24); spiActive(0); } // data for (i=0; i<len/4; i++) { outpw(REG_SPI0_TX0, 0xffffffff); spiTxLen(0, 0, 32); spiActive(0); u32Tmp = inp32(REG_SPI0_RX0); *buf++ = ((u32Tmp & 0xFF) << 24) | ((u32Tmp & 0xFF00) << 8) | ((u32Tmp & 0xFF0000) >> 8)| ((u32Tmp & 0xFF000000) >> 24); } if(len % 4) { ptr = (UINT8 *)buf; for (i=0; i<(len %4); i++) { outpb(REG_SPI0_TX0, 0xff); spiTxLen(0, 0, 8); spiActive(0); *ptr++ = inpb(REG_SPI0_RX0); } } outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 return Successful; } INT spiFlashEnter4ByteMode(void) { outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // enter 4 byte mode command outpw(REG_SPI0_TX0, 0xB7); spiTxLen(0, 0, 8); spiActive(0); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 g_u8Is4ByteMode = TRUE; return Successful; } INT spiFlashExit4ByteMode(void) { outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) | 0x01); // CS0 // exit 4 byte mode command outpw(REG_SPI0_TX0, 0xE9); spiTxLen(0, 0, 8); spiActive(0); outpw(REG_SPI0_SSR, inpw(REG_SPI0_SSR) & 0xfe); // CS0 g_u8Is4ByteMode = FALSE; return Successful; }