Xiang Wang has uploaded this change for review.

View Change

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 */

To view, visit change 33059. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I35c81f7afd4c2c4a52730eeb15ce0b55d8d6af60
Gerrit-Change-Number: 33059
Gerrit-PatchSet: 1
Gerrit-Owner: Xiang Wang <wxjstz@126.com>
Gerrit-MessageType: newchange