Xiang Wang has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/33059
Change subject: drivers/spi: add issi flash support ......................................................................
drivers/spi: add issi flash support
HiFive Unleashed has a spi flash, add code to support
Change-Id: I35c81f7afd4c2c4a52730eeb15ce0b55d8d6af60 Signed-off-by: Xiang Wang wxjstz@126.com --- M src/drivers/spi/Kconfig M src/drivers/spi/Makefile.inc A src/drivers/spi/issi.c M src/drivers/spi/spi_flash_internal.h 4 files changed, 379 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/59/33059/1
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig index b15a502..da46071 100644 --- a/src/drivers/spi/Kconfig +++ b/src/drivers/spi/Kconfig @@ -130,6 +130,13 @@ Select this option if your chipset driver needs to store certain data in the SPI flash and your SPI flash is made by SST.
+config SPI_FLASH_ISSI + bool + default y if SPI_FLASH_INCLUDE_ALL_DRIVERS + help + Select this option if your chipset driver needs to store certain + data in the SPI flash and your SPI flash is made by ISSI. + config SPI_FLASH_STMICRO bool default y if SPI_FLASH_INCLUDE_ALL_DRIVERS diff --git a/src/drivers/spi/Makefile.inc b/src/drivers/spi/Makefile.inc index e55233e..b532398 100644 --- a/src/drivers/spi/Makefile.inc +++ b/src/drivers/spi/Makefile.inc @@ -25,6 +25,7 @@ $(1)-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.c $(1)-$(CONFIG_SPI_FLASH_SPANSION) += spansion.c $(1)-$(CONFIG_SPI_FLASH_SST) += sst.c +$(1)-$(CONFIG_SPI_FLASH_ISSI) += issi.c $(1)-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.c $(1)-$(CONFIG_SPI_FLASH_WINBOND) += winbond.c $(1)-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.c diff --git a/src/drivers/spi/issi.c b/src/drivers/spi/issi.c new file mode 100644 index 0000000..0333a55 --- /dev/null +++ b/src/drivers/spi/issi.c @@ -0,0 +1,369 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 HardenedLinux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <console/console.h> +#include <stdlib.h> +#include <spi_flash.h> +#include <spi-generic.h> +#include <string.h> + +#include "spi_flash_internal.h" + +/* software reset enable */ +#define CMD_ISSI_RSTEN 0x66 +/* software reset */ +#define CMD_ISSI_RST 0x99 +/* read status register */ +#define CMD_ISSI_RDSR 0x05 +/* write status register */ +#define CMD_ISSI_WRSR 0x01 +/* write enable */ +#define CMD_ISSI_WREN 0x06 +/* write disable */ +#define CMD_ISSI_WRDI 0x04 +/* read bank register */ +#define CMD_ISSI_RDBR 0x16 +/* write bank register */ +#define CMD_ISSI_WRBRV 0x17 +/* normal read */ +#define CMD_ISSI_NORD 0x03 +/* normal read with 4Bytes address*/ +#define CMD_ISSI_4NORD 0x13 +/* page program, up to 256bytes */ +#define CMD_ISSI_PP 0x02 +/* page program with 4Bytes address, up to 256bytes */ +#define CMD_ISSI_4PP 0x12 +/* sector erase, section size is 4kbytes */ +#define CMD_ISSI_SEC 0x20 +/* sector erase with 4Bytes address, section size is 4KBytes */ +#define CMD_ISSI_4SEC 0x21 + + +static int issi_flash_cmd_read(const struct spi_flash *flash, + u32 offset, size_t len, void *data); +static int issi_flash_cmd_write(const struct spi_flash *flash, + u32 offset, size_t len, const void *data); +static int issi_flash_cmd_erase(const struct spi_flash *flash, u32 offset, + size_t len); + + +struct issi_spi_flash_params { + u16 model; + u32 size; + u32 sector_size; + u8 status_cmd; + const char *name; + const struct spi_flash_ops *ops; +}; + +static const struct spi_flash_ops spi_flash_ops = { + .read = issi_flash_cmd_read, + .write = issi_flash_cmd_write, + .erase = issi_flash_cmd_erase, + .status = spi_flash_cmd_status, +}; + +static struct issi_spi_flash_params issi_spi_flash_table[] = { + { + .model = 0x6019, + .size = 32 * 1024 * 1024, + .sector_size = 4 * 1024, + .status_cmd = CMD_ISSI_RDSR, + .name = "IS25LP256D", + .ops = &spi_flash_ops + }, + { + .model = 0x7019, + .size = 32 * 1024 * 1024, + .sector_size = 4 * 1024, + .status_cmd = CMD_ISSI_RDSR, + .name = "IS25WP256D", + .ops = &spi_flash_ops + } +}; + +static int issi_do_cmd(const struct spi_flash *flash, + const void *cmd, size_t cmd_len) +{ + int ret = 0; + struct spi_op vectors[] = { + [0] = { .dout = cmd, .bytesout = cmd_len, + .din = NULL, .bytesin = 0, } + }; + do { + ret = spi_claim_bus(&flash->spi); + if (ret) + break; + ret = spi_xfer_vector(&flash->spi, vectors, 1); + if (ret) + break; + spi_release_bus(&flash->spi); + } while (0); + return ret; +} + +static int issi_do_read_cmd(const struct spi_flash *flash, + const void *cmd, size_t cmd_len, + void *data, size_t data_len) +{ + int ret = 0; + struct spi_op vectors[] = { + [0] = { .dout = cmd, .bytesout = cmd_len, + .din = NULL, .bytesin = 0, }, + [1] = { .dout = NULL, .bytesout = 0, + .din = data, .bytesin = data_len }, + }; + size_t count = (data_len == 0 ? 1 : 2); + do { + ret = spi_claim_bus(&flash->spi); + if (ret) + break; + ret = spi_xfer_vector(&flash->spi, vectors, count); + if (ret) + break; + spi_release_bus(&flash->spi); + } while (0); + return ret; +} + +static int issi_do_write_cmd(const struct spi_flash *flash, + const void *cmd, size_t cmd_len, + const void *data, size_t data_len) +{ + int ret = 0; + struct spi_op vectors[] = { + [0] = { .dout = cmd, .bytesout = cmd_len, + .din = NULL, .bytesin = 0, }, + [1] = { .dout = data, .bytesout = data_len, + .din = NULL, .bytesin = 0 }, + }; + size_t count = (data_len == 0 ? 1 : 2); + do { + ret = spi_claim_bus(&flash->spi); + if (ret) + break; + ret = spi_xfer_vector(&flash->spi, vectors, count); + if (ret) + break; + spi_release_bus(&flash->spi); + } while (0); + return ret; +} + +static int issi_software_reset(const struct spi_flash *flash) +{ + int ret = 0; + static const u8 cmd_reset_enable = CMD_ISSI_RSTEN; + static const u8 cmd_reset = CMD_ISSI_RST; + do { + ret = issi_do_cmd(flash, &cmd_reset_enable, 1); + if (ret) + break; + ret = issi_do_cmd(flash, &cmd_reset, 1); + if (ret) + break; + } while (0); + if (ret) + printk(BIOS_WARNING, "SF: Resetting failed\n"); + return ret; +} + +static int issi_enable_writing(const struct spi_flash *flash) +{ + static const u8 cmd = CMD_ISSI_WREN; + int ret = issi_do_cmd(flash, &cmd, 1); + if (ret) + printk(BIOS_WARNING, "SF: Enabling Write failed\n"); + return ret; +} + +static int issi_disable_writing(const struct spi_flash *flash) +{ + static const u8 cmd = CMD_ISSI_WRDI; + int ret = issi_do_cmd(flash, &cmd, 1); + if (ret) + printk(BIOS_WARNING, "SF: Disabling Write failed\n"); + return ret; +} + +static int issi_flash_cmd_read(const struct spi_flash *flash, + u32 offset, size_t len, void *data) +{ + int ret = 0; + if (flash->size <= 32 * 1024 * 1024) { + u8 cmd[] = { + CMD_ISSI_NORD, + (offset >> 16) & 0xff, + (offset >> 8) & 0xff, + (offset >> 0) & 0xff, + }; + ret = issi_do_read_cmd(flash, cmd, sizeof(cmd), data, len); + } else { + u8 cmd[] = { + CMD_ISSI_4NORD, + (offset >> 24) & 0xff, + (offset >> 16) & 0xff, + (offset >> 8) & 0xff, + (offset >> 0) & 0xff, + }; + ret = issi_do_read_cmd(flash, cmd, sizeof(cmd), data, len); + } + if (ret) + printk(BIOS_WARNING, "SF: Failed to send read command " + "(%zu bytes): %d\n", len, ret); + return ret; +} + +static int issi_flash_cmd_write(const struct spi_flash *flash, + u32 offset, size_t len, const void *data) +{ + int ret = 0; + + ret = issi_enable_writing(flash); + if (ret) + return ret; + + do { + size_t wlen = (len > 256 ? 256 : len); + + if (flash->size <= 32 * 1024 * 1024) { + u8 cmd[4] = { + CMD_ISSI_PP, + (offset >> 16) & 0xff, + (offset >> 8) & 0xff, + (offset >> 0) & 0xff + }; + ret = issi_do_write_cmd(flash, cmd, sizeof(cmd), + data, wlen); + } else { + u8 cmd[5] = { + CMD_ISSI_4PP, + (offset >> 24) & 0xff, + (offset >> 16) & 0xff, + (offset >> 8) & 0xff, + (offset >> 0) & 0xff + }; + ret = issi_do_write_cmd(flash, cmd, sizeof(cmd), + data, wlen); + } + if (ret) { + printk(BIOS_WARNING, "SF: Failed to send write command" + " (%zu bytes): %d\n", wlen, ret); + return ret; + } + + len = len - wlen; + offset = offset + wlen; + data = (void *)((uintptr_t)data + wlen); + + ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret) + return ret; + } while (len); + + ret = issi_disable_writing(flash); + + return ret; +} + +static int issi_flash_cmd_erase(const struct spi_flash *flash, u32 offset, + size_t len) +{ + int ret = 0; + + u32 erase_size = flash->sector_size; + if (offset % erase_size || len % erase_size) { + printk(BIOS_WARNING, "SF: Erase offset/length not multiple of" + " erase size\n"); + return -1; + } + if (len == 0) { + printk(BIOS_WARNING, "SF: Erase length cannot be 0\n"); + return -1; + } + + ret = issi_enable_writing(flash); + if (ret) + return ret; + + u32 end = offset + len; + + while (offset < end) { + if (flash->size <= 32 * 1024 * 1024) { + u8 cmd[4] = { + CMD_ISSI_PP, + (offset >> 16) & 0xff, + (offset >> 8) & 0xff, + (offset >> 0) & 0xff + }; + ret = issi_do_cmd(flash, cmd, sizeof(cmd)); + } else { + u8 cmd[5] = { + CMD_ISSI_4PP, + (offset >> 24) & 0xff, + (offset >> 16) & 0xff, + (offset >> 8) & 0xff, + (offset >> 0) & 0xff + }; + ret = issi_do_cmd(flash, cmd, sizeof(cmd)); + } + if (ret) { + printk(BIOS_WARNING, "SF: Failed to send erase command" + " (offset: %#04x): %d\n", offset, ret); + return ret; + } + + offset = offset + erase_size; + + ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret) + return ret; + } + + ret = issi_disable_writing(flash); + + return ret; +} + +int spi_flash_probe_issi(const struct spi_slave *spi, u8 *idcode, + struct spi_flash *flash) +{ + size_t i; + u16 model; + const struct issi_spi_flash_params *params; + + for (i = 0; i < ARRAY_SIZE(issi_spi_flash_table); ++i) { + params = &issi_spi_flash_table[i]; + model = (idcode[1] << 8) | idcode[2]; + if (params->model == model) + break; + } + + if (i == ARRAY_SIZE(issi_spi_flash_table)) { + printk(BIOS_WARNING, "SF: Unsupported ISSI ID %04x\n", model); + return -1; + } + + memcpy(&flash->spi, spi, sizeof(*spi)); + flash->name = params->name; + flash->sector_size = params->sector_size; + flash->size = params->size; + flash->status_cmd = params->status_cmd; + + flash->ops = params->ops; + + issi_software_reset(flash); + + return 0; +} diff --git a/src/drivers/spi/spi_flash_internal.h b/src/drivers/spi/spi_flash_internal.h index a89610a..25e2045 100644 --- a/src/drivers/spi/spi_flash_internal.h +++ b/src/drivers/spi/spi_flash_internal.h @@ -84,5 +84,7 @@ struct spi_flash *flash); int spi_flash_probe_adesto(const struct spi_slave *spi, u8 *idcode, struct spi_flash *flash); +int spi_flash_probe_issi(const struct spi_slave *spi, u8 *idcode, + struct spi_flash *flash);
#endif /* SPI_FLASH_INTERNAL_H */