/* * Copyright (c) 2022, Erich Styger * * SPDX-License-Identifier: BSD-3-Clause */ #include "McuW25Q128.h" #if MCUW28Q128_CONFIG_ENABLED #include "McuUtility.h" #include "McuGPIO.h" #include "McuWait.h" #include "McuShell.h" #include "McuLog.h" #include "McuSPI.h" #define McuW25_CMD_PAGE_PROGRAM 0x02 #define McuW25_CMD_DATA_READ 0x03 #define McuW25_CMD_READ_STATUS1 0x05 #define McuW25_CMD_WRITE_ENABLE 0x06 #define McuW25_CMD_WRITE_DISABLE 0x04 #define McuW25_CMD_GET_ID 0x9F #define McuW25_ID0_WINBOND 0xEF /* first byte of ID for Winbond */ #define McuW25_ID1_WINBOND_xQ 0x40 /* second byte for W25Q128JV-IQ-JQ */ #define McuW25_ID2_WINBOND_xQ 0x18 /* third byte for W25Q128JV-IQ-JQ */ #define McuW25_ID1_WINBOND_xM 0x70 /* second byte for W25Q128JV-IM-JM */ #define McuW25_ID2_WINBOND_xM 0x18 /* third byte for W25Q128JV-IM-JM */ #define McuW25_CMD_GET_SERIAL 0x4B #define McuW25_CMD_SECTOR_ERASE_4K 0x20 #define McuW25_CMD_BLOCK_ERASE_32K 0x52 #define McuW25_CMD_BLOCK_ERASE_64K 0xD8 #define McuW25_CMD_CHIP_ERASE 0xC7 #define SPI_WRITE(write) \ { \ while(McuSPI_SendByte(write)!=0) {} \ } #define SPI_WRITE_READ(write, readP) \ { \ while(McuSPI_SendReceiveByte(write, readP)!=0) {} \ } #define SPI_WRITE_READ_BLOCK(write, read, size) \ { \ while(McuSPI_SendReceiveBlock(write, read, size)!=0) {} \ } uint8_t McuW25_ReadStatus1(uint8_t *status) { McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_READ_STATUS1); SPI_WRITE_READ(0, status); McuW25_CONFIG_CS_DISABLE(); return ERR_OK; } bool McuW25_isBusy(void) { uint8_t status; McuW25_ReadStatus1(&status); return (status&1); } void McuW25_WaitIfBusy(void) { while(McuW25_isBusy()) { McuWait_Waitms(1); } } uint8_t McuW25_Read(uint32_t address, uint8_t *buf, size_t bufSize) { McuW25_WaitIfBusy(); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_DATA_READ); SPI_WRITE(address>>16); SPI_WRITE(address>>8); SPI_WRITE(address); SPI_WRITE_READ_BLOCK(NULL, buf, bufSize); McuW25_CONFIG_CS_DISABLE(); return ERR_OK; } uint8_t McuW25_EraseAll(void) { McuW25_WaitIfBusy(); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_WRITE_ENABLE); McuW25_CONFIG_CS_DISABLE(); McuWait_Waitus(1); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_CHIP_ERASE); McuW25_CONFIG_CS_DISABLE(); return ERR_OK; } uint8_t McuW25_EraseSector4K(uint32_t address) { McuW25_WaitIfBusy(); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_WRITE_ENABLE); McuW25_CONFIG_CS_DISABLE(); McuWait_Waitus(1); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_SECTOR_ERASE_4K); SPI_WRITE(address>>16); SPI_WRITE(address>>8); SPI_WRITE(address); McuW25_CONFIG_CS_DISABLE(); return ERR_OK; } uint8_t McuW25_EraseBlock32K(uint32_t address) { McuW25_WaitIfBusy(); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_WRITE_ENABLE); McuW25_CONFIG_CS_DISABLE(); McuWait_Waitus(1); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_BLOCK_ERASE_32K); SPI_WRITE(address>>16); SPI_WRITE(address>>8); SPI_WRITE(address); McuW25_CONFIG_CS_DISABLE(); return ERR_OK; } uint8_t McuW25_EraseBlock64K(uint32_t address) { McuW25_WaitIfBusy(); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_WRITE_ENABLE); McuW25_CONFIG_CS_DISABLE(); McuWait_Waitus(1); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_BLOCK_ERASE_64K); SPI_WRITE(address>>16); SPI_WRITE(address>>8); SPI_WRITE(address); McuW25_CONFIG_CS_DISABLE(); return ERR_OK; } /*! * Program a page with data * \param address, should be aligned on page (256 bytes) if programming 256 bytes * \param data pointer to data * \param dataSize size of data in bytes, max 256 * \return error code, ERR_OK for no error */ uint8_t McuW25_ProgramPage(uint32_t address, const uint8_t *data, size_t dataSize) { McuW25_WaitIfBusy(); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_WRITE_ENABLE); McuW25_CONFIG_CS_DISABLE(); McuWait_Waitus(1); McuW25_CONFIG_CS_ENABLE(); SPI_WRITE(McuW25_CMD_PAGE_PROGRAM); SPI_WRITE(address>>16); SPI_WRITE(address>>8); SPI_WRITE(address); #if 0 while(dataSize>0) { SPI_WRITE(*data); dataSize--; data++; } #else SPI_WRITE_READ_BLOCK(data, NULL, dataSize); #endif McuW25_CONFIG_CS_DISABLE(); return ERR_OK; } uint8_t McuW25_GetCapacity(uint32_t *capacity) { uint32_t n = 0x100000; /* unknown chips, default to 1 MByte */ uint8_t id[McuW25_ID_BUF_SIZE] = {0,0,0}; uint8_t res; res = McuW25_ReadID(id, sizeof(id)); if (res!=ERR_OK) { return ERR_FAILED; } if (id[2] >= 16 && id[2] <= 31) { n = 1ul << id[2]; } else if (id[2] >= 32 && id[2] <= 37) { n = 1ul << (id[2] - 6); } else if ((id[0]==0 && id[1]==0 && id[2]==0) || (id[0]==255 && id[1]==255 && id[2]==255)) { *capacity = 0; return ERR_FAILED; } *capacity = n; return ERR_OK; } uint8_t McuW25_ReadSerialNumber(uint8_t *buf, size_t bufSize) { int i; if (bufSizestdOut); res = McuW25_ReadID(id, sizeof(id)); buf[0] = '\0'; if (res==ERR_OK) { for(int i=0; istdOut); res = McuW25_GetCapacity(&capacity); if (res==ERR_OK) { buf[0] = '\0'; McuUtility_strcatNum32u(buf, sizeof(buf), capacity); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" bytes\r\n"); } else { McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n"); } McuShell_SendStatusStr((const unsigned char*)" Capacity", buf, io->stdOut); res = McuW25_ReadSerialNumber(serial, sizeof(serial)); /* check serial number */ if(res==ERR_OK) { buf[0] = '\0'; for(i=0; istdOut); res = McuW25_ReadStatus1(&status); if(res==ERR_OK) { McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x"); McuUtility_strcatNum8Hex(buf, sizeof(buf), status); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" SEC:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<6)?(unsigned char*)"1": (unsigned char*)"0"); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" TB:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<5)?(unsigned char*)"1": (unsigned char*)"0"); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" BP2:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<4)?(unsigned char*)"1": (unsigned char*)"0"); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" BP1:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<3)?(unsigned char*)"1": (unsigned char*)"0"); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" BP0:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<2)?(unsigned char*)"1": (unsigned char*)"0"); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" WEL:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<1)?(unsigned char*)"1": (unsigned char*)"0"); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" BUSY:"); McuUtility_strcat(buf, sizeof(buf), status&(1<<0)?(unsigned char*)"1": (unsigned char*)"0"); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); } else { McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n"); } McuShell_SendStatusStr((const unsigned char*)" Status", buf, io->stdOut); return ERR_OK; } static uint8_t ReadBytes(void *hndl, uint32_t address, uint8_t *buf, size_t bufSize) { (void)hndl; /* not used */ return McuW25_Read(address, buf, bufSize); } uint8_t McuW25_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { const unsigned char *p; int32_t val, end; uint32_t res; uint8_t data[32]; int i; if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "McuW25 help")==0) { McuShell_SendHelpStr((unsigned char*)"McuW25", (const unsigned char*)"Group of Winbond W25Q128 Flash commands\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" help|status", (const unsigned char*)"Print help or status information\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" read ", (const unsigned char*)"Read memory from to address\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" erase all", (const unsigned char*)"Erase all data on device\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" erase 4k ", (const unsigned char*)"Erase a 4K sector\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" erase 32k ", (const unsigned char*)"Erase a 32K block\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" erase 64k ", (const unsigned char*)"Erase a 64K block\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" write ", (const unsigned char*)"Write to page (max 32 bytes data, separated by spaces)\r\n", io->stdOut); *handled = TRUE; return ERR_OK; } else if (McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, "McuW25 status")==0) { *handled = TRUE; return McuW25_PrintStatus(io); } else if (McuUtility_strncmp((char*)cmd, "McuW25 read ", sizeof("McuW25 read ")-1)==0) { *handled = TRUE; p = cmd+sizeof("McuW25 read ")-1; res = McuUtility_xatoi(&p, &val); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"wrong start address\r\n", io->stdErr); return ERR_FAILED; } else { res = McuUtility_xatoi(&p, &end); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"wrong end address\r\n", io->stdErr); return ERR_FAILED; } res = McuShell_PrintMemory(NULL, val, end, 3, 16, ReadBytes, io); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"memory read failed\r\n", io->stdErr); return ERR_FAILED; } return ERR_OK; } } else if (McuUtility_strncmp((char*)cmd, "McuW25 write ", sizeof("McuW25 write ")-1)==0) { *handled = TRUE; p = cmd+sizeof("McuW25 write ")-1; res = McuUtility_xatoi(&p, &val); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"wrong address\r\n", io->stdErr); return ERR_FAILED; } else { for(i=0; i0) { res = McuW25_ProgramPage(val, data, i); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"failed programming page\r\n", io->stdErr); return ERR_FAILED; } } } return ERR_OK; } else if (McuUtility_strcmp((char*)cmd, "McuW25 erase all")==0) { *handled = TRUE; res = McuW25_EraseAll(); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"failed erasing all memory\r\n", io->stdErr); return ERR_FAILED; } return ERR_OK; } else if (McuUtility_strncmp((char*)cmd, "McuW25 erase 4k ", sizeof("McuW25 erase 4k ")-1)==0) { *handled = TRUE; p = cmd+sizeof("McuW25 erase 4k ")-1; res = McuUtility_xatoi(&p, &val); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"wrong address\r\n", io->stdErr); return ERR_FAILED; } else { res = McuW25_EraseSector4K(val); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"failed erasing 4k sector\r\n", io->stdErr); return ERR_FAILED; } } return ERR_OK; } else if (McuUtility_strncmp((char*)cmd, "McuW25 erase 32k ", sizeof("McuW25 erase 32k ")-1)==0) { *handled = TRUE; p = cmd+sizeof("McuW25 erase 32k ")-1; res = McuUtility_xatoi(&p, &val); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"wrong address\r\n", io->stdErr); return ERR_FAILED; } else { res = McuW25_EraseBlock32K(val); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"failed erasing 32k block\r\n", io->stdErr); return ERR_FAILED; } } return ERR_OK; } else if (McuUtility_strncmp((char*)cmd, "McuW25 erase 64k ", sizeof("McuW25 erase 64k ")-1)==0) { *handled = TRUE; p = cmd+sizeof("McuW25 erase 64k ")-1; res = McuUtility_xatoi(&p, &val); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"wrong address\r\n", io->stdErr); return ERR_FAILED; } else { res = McuW25_EraseBlock64K(val); if (res!=ERR_OK) { McuShell_SendStr((unsigned char*)"failed erasing 32k block\r\n", io->stdErr); return ERR_FAILED; } } return ERR_OK; } return ERR_OK; } void McuW25_Deinit(void) { McuW25_CONFIG_CS_DISABLE(); /* disable chip select by default */ } void McuW25_Init(void) { McuW25_CONFIG_CS_DISABLE(); /* disable chip select by default */ } #endif /* MCUW28Q128_CONFIG_ENABLED */