/* SPDX-License-Identifier: GPL-2.0-or-later */ /** ****************************************************************************** * @file spi_probe.c * @author StarFive Technology * @version V1.0 * @date 06/25/2020 * @brief ****************************************************************************** * @copy * * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. * * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd. */ #include #include #include #include #include #define IDCODE_CONT_LEN 0 #define IDCODE_PART_LEN 3 #define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN) #define CMD_READ_ID 0x9f #define NOR 0 #define GIGANAND 1 //#define CONFIG_SPI_FLASH_ATMEL //#define CONFIG_SPI_FLASH_GIGADEVICE //#define CONFIG_SPI_FLASH_EON //#define CONFIG_SPI_FLASH_MACRONIX //#define CONFIG_SPI_FLASH_SPANSION //#define CONFIG_SPI_FLASH_STMICRO //#define CONFIG_SPI_FLASH_SST //#define CONFIG_SPI_FLASH_WINBOND //#define TEST_GD25Q64B //#define TEST_GD25Q64C //#define TEST_GD25LB64C static struct spi_flash g_spi_flash[1]; static const struct spi_flash_params spi_flash_table[] = { #ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */ {"AT45DB011D", 0x00221f,8,16,16,4,NOR}, {"AT45DB021D", 0x00231f,8,16,16,8,NOR}, {"AT45DB041D", 0x00241f,8,16,16,8,NOR}, {"AT45DB081D", 0x00251f,8,16,16,16,NOR}, {"AT45DB161D", 0x00261f,8,16,16,32,NOR}, {"AT45DB321D", 0x00271f,8,16,16,64,NOR}, {"AT45DB641D", 0x00281f,8,16,16,128,NOR}, {"AT25DF161", 0x01461f,8,16,16,32,NOR}, {"AT25DF321", 0x01471f,8,16,16,64,NOR}, #endif #ifdef CONFIG_SPI_FLASH_EON /* EON */ {"EN25Q64", 0x17301c,8,16,16,128,NOR}, #endif #ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ #ifdef TEST_GD25Q64B {"GD25Q64B", 0x1740c8,8,16,16,128,NOR}, #endif #ifdef TEST_GD25Q64C {"GD25Q64C", 0x1740c8,8,16,16,128,NOR}, {"GD25Q64C---", 0x1740D8,8,16,16,128,NOR}, #endif {"GD25LQ32", 0x1660c8,8,16,16,64,NOR}, {"GD5F1GQ4UAYIG", 0xc8f1c8,11,64,0,1024,GIGANAND}, #endif #ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ {"MX25L2006E", 0x1220c2,8,16,16,4,NOR}, {"MX25L4005", 0x1320c2,8,16,16,8,NOR}, {"MX25L8005", 0x1420c2,8,16,16,16,NOR}, {"MX25L1605D", 0x1520c2,8,16,16,32,NOR}, {"MX25L3205D", 0x1620c2,8,16,16,64,NOR}, {"MX25L6405D", 0x1720c2,8,16,16,128,NOR}, {"MX25L12805", 0x1820c2,8,16,16,256,NOR}, {"MX25L25635F", 0x1920c2,8,16,16,512,NOR}, {"MX25L51235F", 0x1a20c2,8,16,16,1024,NOR}, {"MX25L12855E", 0x1826c2,8,16,16,256,NOR}, #endif #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ {"S25FL008A", 0x130201, 8,16,16, 16,NOR}, {"S25FL016A", 0x140201, 8,16,16, 32,NOR}, {"S25FL032A", 0x150201, 8,16,16, 64,NOR}, {"S25FL064A", 0x160201, 8,16,16, 128,NOR}, #endif #ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ {"M25P10", 0x112020, 8,16,8, 4,NOR}, {"M25P20", 0x122020, 8,16,16, 4,NOR}, {"M25P40", 0x132020, 8,16,16, 8,NOR}, {"M25P80", 0x142020, 8,16,16, 16,NOR}, {"M25P16", 0x152020, 8,16,16, 32,NOR}, {"M25P32", 0x162020, 8,16,16, 64,NOR}, {"M25P64", 0x172020, 8,16,16, 128,NOR}, {"M25P128", 0x182020, 8,16,32, 64,NOR}, {"N25Q32", 0x16ba20, 8,16,16, 64,NOR}, {"N25Q32A", 0x16bb20, 8,16,16, 64,NOR}, {"N25Q64", 0x17ba20, 8,16,16, 128,NOR}, {"N25Q64A", 0x17bb20, 8,16,16, 128,NOR}, {"N25Q128", 0x18ba20, 8,16,16, 256,NOR}, {"N25Q128A", 0x18bb20, 8,16,16, 256,NOR}, {"N25Q256", 0x19ba20, 8,16,16, 512,NOR}, {"N25Q256A", 0x19bb20, 8,16,16, 512,NOR}, {"N25Q512", 0x20ba20, 8,16,16, 1024,NOR}, {"N25Q512A", 0x20bb20, 8,16,16, 1024,NOR}, {"N25Q1024", 0x21ba20, 8,16,16, 2048,NOR}, {"N25Q1024A", 0x21bb20, 8,16,16,2048,NOR}, #endif #ifdef CONFIG_SPI_FLASH_SST /* SST */ {"SST25VF040B", 0x8d25bf, 8,16,16, 8,NOR}, {"SST25VF080B", 0x8e25bf, 8,16,16, 16,NOR}, {"SST25VF016B", 0x4125bf, 8,16,16, 32,NOR}, {"SST25VF032B", 0x4a25bf, 8,16,16, 64,NOR}, {"SST25VF064C", 0x4b25bf, 8,16,16, 128,NOR}, {"SST25WF512", 0x0125bf, 8,16,16, 1,NOR}, {"SST25WF010", 0x0225bf, 8,16,16, 2,NOR}, {"SST25WF020", 0x0325bf, 8,16,16, 4,NOR}, {"SST25WF040", 0x0425bf, 8,16,16, 8,NOR}, {"SST25WF080", 0x0525bf, 8,16,16, 16,NOR}, #endif #ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ {"W25P80", 0x1420ef, 8,16,16, 16,NOR}, {"W25P16", 0x1520ef, 8,16,16, 32,NOR}, {"W25P32", 0x1620ef, 8,16,16, 64,NOR}, {"W25X40", 0x1330ef, 8,16,16, 8,NOR}, {"W25X16", 0x1530ef, 8,16,16, 32,NOR}, {"W25X32", 0x1630ef, 8,16,16, 64,NOR}, {"W25X64", 0x1730ef, 8,16,16, 128,NOR}, {"W25Q80BL", 0x1440ef, 8,16,16, 16,NOR}, {"W25Q16CL", 0x1540ef, 8,16,16, 32,NOR}, {"W25Q32BV", 0x1640ef, 8,16,16, 64,NOR}, {"W25Q64CV", 0x1740ef, 8,16,16, 128,NOR}, {"W25Q128BV", 0x1840ef, 8,16,16, 256,NOR}, {"W25Q256", 0x1940ef, 8,16,16, 512,NOR}, {"W25Q80BW", 0x1450ef, 8,16,16, 16,NOR}, {"W25Q16DW", 0x1560ef, 8,16,16, 32,NOR}, {"W25Q32DW", 0x1660ef, 8,16,16, 64,NOR}, {"W25Q64DW", 0x1760ef, 8,16,16, 128,NOR}, {"W25Q128FW", 0x1860ef, 8,16,16, 256,NOR}, {"W25Q256FW", 0x1940ef, 8,16,16, 256,NOR}, #endif //{"EN25QH16", 0x15701c, 8,16,16,32, NOR}, //{"IS25WP256D", 0x19609d, 8,16,16,256, NOR}, //{"MT25QL256ABA", 0x19ba20, 8,16,16,256, NOR}, //{"GD25LB64C", 0x1760C8, 8,16,16,256, NOR}, {"S25FL512S_512K", 0x200201, 8,16,64,256, NOR}, {"GD25LQ256D", 0x1960C8, 8,16,16,512, NOR}, {"GD25LQ128", 0x1960C8, 8,16,16,256, NOR} //{"GD25LB64B", 0xC86017, 8,16,16,256, NOR}, //{"NORMAL", 0x000000, 8,16,16, 1, NOR}, }; struct spi_flash *spi_flash_probe_nor(struct spi_slave *spi, u8 *idcode) { struct spi_flash_params *params; struct spi_flash *flash; u32 id = 0; static int i = 0; id = ((idcode[2] << 16) | (idcode[1] << 8) | idcode[0]); if(id == 0x0) { return NULL; } params = spi_flash_table; for (i = 0; spi_flash_table[i].name != NULL; i++) { if ((spi_flash_table[i].id & 0xFFFFFF) == id) { break; } } flash = &g_spi_flash[spi->bus]; if (!flash) { //uart_printf("SF: Failed to allocate memory\r\n"); return NULL; } flash->name = spi_flash_table[i].name; if(spi_flash_table[i].flags == NOR) { /* Assuming power-of-two page size initially. */ flash->write = spi_flash_cmd_write_mode; flash->erase = spi_flash_erase_mode; flash->read = spi_flash_read_mode; flash->page_size = 1 << spi_flash_table[i].l2_page_size; flash->sector_size = flash->page_size * spi_flash_table[i].pages_per_sector; flash->block_size = flash->sector_size * spi_flash_table[i].sectors_per_block; flash->size = flash->page_size * spi_flash_table[i].pages_per_sector * spi_flash_table[i].sectors_per_block * spi_flash_table[i].nr_blocks; } //uart_printf("spi probe complete\r\n"); return flash; } #if 0 static int print_id(u8 *idcode, int len) { int i; uart_printf("idcode:0x"); for (i=len-1; i>=0; i--) print_ubyte_hex(idcode[i]); uart_printf("\r\n"); return 0; } #endif static int spi_read_id(struct spi_slave *spi, unsigned char cmd, void *response, u32 len) { int ret1 = -1,ret2 = -1; unsigned char buf[4] = {0};// = {(u8)cmd, 0x00, 0x00, 0x00}; buf[0] = cmd; buf[1] = 0; buf[2] = 0; buf[3] = 0; ret1 = spi_xfer(spi, 1*8, &buf[0], NULL, SPI_XFER_BEGIN, 8); ret2 = spi_xfer(spi, len*8, NULL, response, SPI_XFER_END, 8); return 0; } static struct spi_flash aic_flash; struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int mode, unsigned int bus_width) { struct spi_slave *spi; struct spi_flash *flash = &aic_flash; int ret = 0; u8 idcode[IDCODE_LEN]; spi = spi_setup_slave(bus, cs, max_hz, mode, bus_width); if (!spi) { //uart_printf("SF: Failed to set up slave\n"); return NULL; } /* Read the ID codes */ ret = spi_read_id(spi, CMD_READ_ID, idcode, sizeof(idcode)); if (ret) { //uart_printf("SF: Failed to read ID : %d\n", ret); goto err_read_id; } //print_id(idcode, sizeof(idcode)); flash = spi_flash_probe_nor(spi,idcode); if (!flash) { goto err_manufacturer_probe; } flash->spi = spi; return flash; err_manufacturer_probe: err_read_id: return NULL; }