ChrisEric1 CECL has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/72057 )
Change subject: Add support for VIA VL805 USB 3 XHCI flashing ......................................................................
Add support for VIA VL805 USB 3 XHCI flashing
It works fine for me on my Raspberry Pi 4 Model B.
Was able read read, erase, write, verify.
Only thing is the WP with the W25X10 gives a untested warning.
And I changed W25X10 to RDID4 since it wouldn't work otherwise.
Change-Id: I71435afcacdf97e14d627e35bce3d29de9657f38 Signed-off-by: Christopher Lentocha christopherericlentocha@gmail.com --- M Makefile M flashchips.c M flashrom.8.tmpl M include/programmer.h M programmer_table.c A vl805.c 6 files changed, 262 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/57/72057/1
diff --git a/Makefile b/Makefile index 425b58c..3148cf8 100644 --- a/Makefile +++ b/Makefile @@ -152,6 +152,7 @@ CONFIG_OGP_SPI \ CONFIG_SATAMV \ CONFIG_SATASII \ + CONFIG_VL805 \
DEPENDS_ON_LIBUSB1 := \ CONFIG_CH341A_SPI \ @@ -516,6 +517,9 @@ # Winchiphead CH341A CONFIG_CH341A_SPI ?= yes
+# Enable VIA VL805 programmer for now. +CONFIG_VL805 ?= yes + # Digilent Development board JTAG CONFIG_DIGILENT_SPI ?= yes
@@ -767,6 +771,11 @@ PROGRAMMER_OBJS += ch341a_spi.o endif
+ifeq ($(CONFIG_VL805), yes) +FEATURE_FLAGS += -D'CONFIG_VL805=1' +PROGRAMMER_OBJS += vl805.o +endif + ifeq ($(CONFIG_DIGILENT_SPI), yes) FEATURE_FLAGS += -D'CONFIG_DIGILENT_SPI=1' PROGRAMMER_OBJS += digilent_spi.o diff --git a/flashchips.c b/flashchips.c index 625fe62..db72ee4 100644 --- a/flashchips.c +++ b/flashchips.c @@ -7783,6 +7783,46 @@
{ .vendor = "ISSI", + .name = "IS25LP080D", + .bustype = BUS_SPI, + .manufacture_id = 0x9D, + .model_id = 0x6014, + .total_size = 1024, + .page_size = 256, + /* supports SFDP */ + /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ + .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .tested = TEST_OK_PREW, + .probe = PROBE_SPI_RDID4, + .probe_timing = TIMING_ZERO, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 256} }, + .block_erase = SPI_BLOCK_ERASE_20, + }, { + .eraseblocks = { {32 * 1024, 32} }, + .block_erase = SPI_BLOCK_ERASE_52, + }, { + .eraseblocks = { {64 * 1024, 16} }, + .block_erase = SPI_BLOCK_ERASE_D8, + }, { + .eraseblocks = { {1024 * 1024, 1} }, + .block_erase = SPI_BLOCK_ERASE_60, + }, { + .eraseblocks = { {1024 * 1024, 1} }, + .block_erase = SPI_BLOCK_ERASE_C7, + } + }, + .printlock = SPI_PRETTYPRINT_STATUS_REGISTER_PLAIN, /* TODO: improve */ + .unlock = SPI_DISABLE_BLOCKPROTECT, + .write = SPI_CHIP_WRITE256, + .read = SPI_CHIP_READ, + .voltage = {2700, 3600}, + }, + + { + .vendor = "ISSI", .name = "IS29GL064B", .bustype = BUS_PARALLEL, .manufacture_id = ISSI_ID, @@ -19123,7 +19163,7 @@ .page_size = 256, .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, - .probe = PROBE_SPI_RDID, + .probe = PROBE_SPI_RDID4, .probe_timing = TIMING_ZERO, .block_erasers = { diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index 0eabee5..c1436fc 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -417,6 +417,8 @@ .sp .BR "* ch341a_spi" " (for SPI flash ROMs attached to WCH CH341A)" .sp +.BR "* vl805" " (for SPI flash ROMs attached to the VIA VL805 PCIe USB 3.0 Host controller)" +.sp .BR "* digilent_spi" " (for SPI flash ROMs attached to iCEblink40 development boards)" .sp .BR "* jlink_spi" " (for SPI flash ROMs attached to SEGGER J-Link and compatible devices)" @@ -1365,6 +1367,10 @@ .BR "ch341a_spi " programmer The WCH CH341A programmer does not support any parameters currently. SPI frequency is fixed at 2 MHz, and CS0 is used as per the device. +.BR "vl805 " programmer +This module supports SPI flash programming through the VL805/VL806 PCIe-USB 3.0 +Host controller. +.SS .SS .BR "ni845x_spi " programmer .IP @@ -1696,6 +1702,9 @@ .B ogp needs PCI configuration space read access and raw memory access. .sp +.B vl805 +needs PCI configuration space read access and raw memory access. +.sp .BR realtek_mst_i2c_spi " and " parade_lspcon need userspace access to the selected I2C bus. .sp diff --git a/include/programmer.h b/include/programmer.h index 9e706d5..30c1dc2 100644 --- a/include/programmer.h +++ b/include/programmer.h @@ -60,6 +60,7 @@ extern const struct programmer_entry programmer_atavia; extern const struct programmer_entry programmer_buspirate_spi; extern const struct programmer_entry programmer_ch341a_spi; +extern const struct programmer_entry programmer_vl805; extern const struct programmer_entry programmer_dediprog; extern const struct programmer_entry programmer_developerbox; extern const struct programmer_entry programmer_digilent_spi; diff --git a/programmer_table.c b/programmer_table.c index d58a155..4fb705a 100644 --- a/programmer_table.c +++ b/programmer_table.c @@ -152,6 +152,10 @@ &programmer_ch341a_spi, #endif
+#if CONFIG_VL805 == 1 + &programmer_vl805, +#endif + #if CONFIG_DIGILENT_SPI == 1 &programmer_digilent_spi, #endif diff --git a/vl805.c b/vl805.c new file mode 100644 index 0000000..8374063 --- /dev/null +++ b/vl805.c @@ -0,0 +1,180 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2019, 2020 Carl-Daniel Hailfinger + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Driver for the VIA VL805 programmer hardware by VIA. + * See http://www.via.com/ for more info. + */ + +#include "programmer.h" +#include "spi.h" +#include "flash.h" +//#include "hwaccess.h" +#include "hwaccess_x86_io.h" +#include "platform/pci.h" + +extern const struct dev_entry devs_vl805[]; + +const struct dev_entry devs_vl805[] = { + {0x1106, 0x3483, OK, "VIA", "VL805"}, + {0}, +}; + +static struct pci_dev *dev = NULL; + +static void vl805_setregval(int reg, uint32_t val) +{ + pci_write_long(dev, 0x78, reg); + pci_write_long(dev, 0x7c, val); +} + +static uint32_t vl805_getregval(int reg) +{ + pci_write_long(dev, 0x78, reg); + + return pci_read_long(dev, 0x7c); +} + +/* Some of the registers have unknown purpose and are just used inside the init sequence replay. */ +#define VL805_REG_0x30004 0x00030004 +#define VL805_REG_STOP_POLLING 0x0004000c +#define VL805_REG_WB_EN 0x00040020 +#define VL805_REG_SPI_OUTDATA 0x000400d0 +#define VL805_REG_SPI_INDATA 0x000400e0 +#define VL805_REG_SPI_TRANSACTION 0x000400f0 +#define VL805_REG_CLK_DIV 0x000400f8 +#define VL805_REG_SPI_CHIP_ENABLE_LEVEL 0x000400fc + +/* Send a SPI command to the flash chip. */ +static int vl805_spi_send_command(const struct flashctx *flash, + unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + unsigned int i, j; + uint32_t outdata; + uint32_t indata = 0; + unsigned int curwritecnt = 0; + unsigned int curreadcnt = 0; + + vl805_setregval(VL805_REG_SPI_CHIP_ENABLE_LEVEL, 0x00000000); + + for (j = 0; j < writecnt; j += 4) { + curwritecnt = min(4, writecnt - j); + outdata = 0; + for (i = 0; i < curwritecnt; i++) { + outdata <<= 8; + outdata |= writearr[j + i]; + } + vl805_setregval(VL805_REG_SPI_OUTDATA, outdata); + vl805_setregval(VL805_REG_SPI_TRANSACTION, 0x00000580 | (curwritecnt << 3)); + } + + /* Superfluous, the original driver doesn't do that, but we want to have a quiet bus during read. */ + vl805_setregval(VL805_REG_SPI_OUTDATA, 0); + + for (j = 0; j < readcnt; j += 4) { + curreadcnt = min(4, readcnt - j); + vl805_setregval(VL805_REG_SPI_TRANSACTION, 0x00000580 | (curreadcnt << 3)); + indata = vl805_getregval(VL805_REG_SPI_INDATA); + for (i = 0; i < curreadcnt; i++) { + unsigned pos = curreadcnt - (i + 1); + readarr[j + i] = (indata >> (8 * pos)) & 0xff; + } + } + + vl805_setregval(VL805_REG_SPI_CHIP_ENABLE_LEVEL, 0x00000001); + + return 0; +} + +static const struct spi_master spi_master_vl805 = { + .max_data_read = 64 * 1024, /* Maximum data read size in one go (excluding opcode+address). */ + .max_data_write = 256, /* Maximum data write size in one go (excluding opcode+address). */ + .command = vl805_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = default_spi_write_256, + .write_aai = default_spi_write_aai, + .probe_opcode = default_spi_probe_opcode, +}; + +static void vl805_programmer_active(uint8_t val) +{ + pci_write_byte(dev, 0x43, val); +} + +static int vl805_shutdown(void *data) +{ + /* Shutdown stuff. */ + vl805_programmer_active(0x0); + + return 0; +} + +int vl805_init(const struct programmer_cfg *cfg); + +int vl805_init(const struct programmer_cfg *cfg) +{ + uint32_t val; + + #if defined(__i386__) || defined(__x86_64__) + if (rget_io_perms()) + return 1; + #endif + + dev = pcidev_init(cfg, devs_vl805, PCI_BASE_ADDRESS_0); /* Actually no BAR setup needed at all. */ + if (!dev) + return 1; + + vl805_programmer_active(0x1); + val = pci_read_long(dev, 0x50); + msg_pdbg("VL805 firmware version 0x%08x\n", val); + vl805_programmer_active(0x0); + + /* Some sort of init sequence, just copied from the logs. */ + vl805_programmer_active(0x1); + + vl805_setregval(VL805_REG_SPI_CHIP_ENABLE_LEVEL, 0x00000001); + vl805_setregval(VL805_REG_0x30004, 0x00000200); + vl805_setregval(VL805_REG_WB_EN, 0xffffff01); + vl805_setregval(VL805_REG_STOP_POLLING, 0x00000001); + + /* We send 4 uninitialized(?) bytes to the flash chip here. */ + vl805_setregval(VL805_REG_SPI_TRANSACTION, 0x000005a0); + vl805_setregval(VL805_REG_CLK_DIV, 0x0000000a); + + /* Some sort of cleanup sequence, just copied from the logs. */ + vl805_setregval(VL805_REG_SPI_TRANSACTION, 0x00000000); + vl805_programmer_active(0x0); + + register_shutdown(vl805_shutdown, NULL); + vl805_programmer_active(0x1); + + register_spi_master(&spi_master_vl805, NULL); + + return 0; +} + +const struct programmer_entry programmer_vl805 = { + .name = "vl805", + .type = PCI, + .devs.dev = devs_vl805, + .init = vl805_init, +};