Attention is currently required from: Carl-Daniel Hailfinger. Hello Carl-Daniel Hailfinger,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/flashrom/+/50264
to review the following change.
Change subject: VIA VL805 SPI Flash Bus support ......................................................................
VIA VL805 SPI Flash Bus support
The VIA VL805 (and VL806 2-port variant) is a 4 ports PCIe-Super Speed USB 3.0 bridge.
Vendor website: https://www.via-labs.com/product_show.php?id=48
The bridge requires an SPI NOR Flash in order to load the firmware, and offers registers used to access this SPI bus in order to program the SPI NOR Flash.
Reverse engineered based on PCI traces created by cleverca22 on a Raspberry Pi 4 Model B.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net [narmstrong: Tested, fixed validated on an Amlogic ARM64 based board with the VL806 variant] Signed-off-by: Neil Armstrong narmstrong@baylibre.com Change-Id: I7439d31cdf5a2174dd27b9dbe5de24ad64963334 --- M Makefile M flashrom.8.tmpl M flashrom.c M meson.build M meson_options.txt M programmer.h A vl805.c 7 files changed, 210 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/64/50264/1
diff --git a/Makefile b/Makefile index 6d37d55..f41bf14 100644 --- a/Makefile +++ b/Makefile @@ -794,6 +794,9 @@ # Disable J-Link for now. CONFIG_JLINK_SPI ?= no
+# Enable VIA VL805 programmer for now. +CONFIG_VL805 ?= yes + # Disable wiki printing by default. It is only useful if you have wiki access. CONFIG_PRINT_WIKI ?= no
@@ -1126,6 +1129,12 @@ PROGRAMMER_OBJS += mstarddc_spi.o endif
+ifeq ($(CONFIG_VL805), yes) +FEATURE_CFLAGS += -D'CONFIG_VL805=1' +PROGRAMMER_OBJS += vl805.o +NEED_PCI := yes +endif + ifeq ($(CONFIG_CH341A_SPI), yes) FEATURE_CFLAGS += -D'CONFIG_CH341A_SPI=1' PROGRAMMER_OBJS += ch341a_spi.o diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index 02f60dc..b289676 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -345,6 +345,8 @@ .sp .BR "* stlinkv3_spi" " (for SPI flash ROMs attached to STMicroelectronics STLINK V3 devices)" .sp +.BR "* vl805" " (for SPI flash ROMs attached to the VIA VL805 PCIe USB 3.0 Host controller)" +.sp Some programmers have optional or mandatory parameters which are described in detail in the .B PROGRAMMER-SPECIFIC INFORMATION @@ -1316,6 +1318,10 @@ If the passed frequency is not supported by the adapter the nearest lower supported frequency will be used. .SS +.BR "vl805 " programmer +This module supports SPI flash programming through the VL805/VL806 PCIe-USB 3.0 +Host controller. +.SS
.SH EXAMPLES To back up and update your BIOS, run @@ -1394,6 +1400,10 @@ .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 +."PLACEHOLDER_NEWPROG_MAN_REQUIREMENTS On OpenBSD, you can obtain raw access permission by setting .B "securelevel=-1" in diff --git a/flashrom.c b/flashrom.c index c89abad..716d904 100644 --- a/flashrom.c +++ b/flashrom.c @@ -533,6 +533,17 @@ }, #endif
+#if CONFIG_VL805 == 1 + { + .name = "vl805", + .type = PCI, + .devs.dev = devs_vl805, + .init = vl805_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif {0}, /* This entry corresponds to PROGRAMMER_INVALID. */ };
diff --git a/meson.build b/meson.build index 73df722..c9b0577 100644 --- a/meson.build +++ b/meson.build @@ -68,6 +68,7 @@ config_stlinkv3_spi = get_option('config_stlinkv3_spi') config_lspcon_i2c_spi = get_option('config_lspcon_i2c_spi') config_realtek_mst_i2c_spi = get_option('config_realtek_mst_i2c_spi') +config_vl805 = get_option('config_vl805')
cargs = [] deps = [] @@ -124,6 +125,7 @@ config_rayer_spi = false config_satamv = false config_satasii = false + config_vl805 = false endif
# set defines for configured programmers @@ -333,6 +335,10 @@ srcs += 'custom_baud.c' srcs += 'serial.c' endif +if config_vl805 + srcs += 'vl805.c' + cargs += '-DCONFIG_VL805=1' +endif
prefix = get_option('prefix') sbindir = join_paths(prefix, get_option('sbindir')) diff --git a/meson_options.txt b/meson_options.txt index b599d66..3a68c70 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -40,3 +40,4 @@ option('config_stlinkv3_spi', type : 'boolean', value : true, description : 'STMicroelectronics STLINK-V3') option('config_lspcon_i2c_spi', type : 'boolean', value : false, description : 'Parade lspcon USB-C to HDMI protocol translator') option('config_realtek_mst_i2c_spi', type : 'boolean', value : true, description : 'Realtek MultiStream Transport MST') +option('config_vl805', type : 'boolean', value : true, description : 'VIA Labs VL805 SPI Flash Bus') diff --git a/programmer.h b/programmer.h index 29a100b..990a21d 100644 --- a/programmer.h +++ b/programmer.h @@ -142,6 +142,9 @@ #if CONFIG_REALTEK_MST_I2C_SPI == 1 PROGRAMMER_REALTEK_MST_I2C_SPI, #endif +#if CONFIG_VL805 == 1 + PROGRAMMER_VL805, + #endif PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -599,6 +602,12 @@ int ni845x_spi_init(void); #endif
+/* vl805.c */ +#if CONFIG_VL805 == 1 +int vl805_init(void); +extern const struct dev_entry devs_vl805[]; +#endif + /* flashrom.c */ struct decode_sizes { uint32_t parallel; diff --git a/vl805.c b/vl805.c new file mode 100644 index 0000000..e20130b --- /dev/null +++ b/vl805.c @@ -0,0 +1,164 @@ +/* + * 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" + +const struct dev_entry devs_vl805[] = { + {0x1106, 0x3483, NT, "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(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, +}; + +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(void) +{ + uint32_t val; + + if (rget_io_perms()) + return 1; + + dev = pcidev_init(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); + + return 0; +}