VIA VL805 support, first draft. No idea if it actually works. It is highly likely that SPI accesses with a readcnt not being a multiple of 4 will return incorrect data due to the unknown encoding of the register containing SPI responses of the chip. That will be obvious from any verbose log, though, and once I have such data, adjusting the code is trivial.
Reverse engineered based on PCI traces created by cleverca22 on a Raspberry Pi 4 Model B.
RFC, with some unrelated (automatic programmer driver writer) and some related changes (buggy PCI patch reverted).
In case someone is feeling less brave, I also have a version which aborts early after the init sequence.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
diff -r 7bf17529e516 Makefile --- a/Makefile Tue Dec 31 18:22:02 2019 +0100 +++ b/Makefile Wed Jan 15 13:20:27 2020 +0100 @@ -696,6 +696,11 @@ # Disable J-Link for now. CONFIG_JLINK_SPI ?= no
+# Enable VIA VL805 programmer for now. +CONFIG_VL805 ?= yes + +#PLACEHOLDER_NEWPROG_DEFAULTCONFIG + # Disable wiki printing by default. It is only useful if you have wiki access. CONFIG_PRINT_WIKI ?= no
@@ -759,7 +764,9 @@ ifeq ($(CONFIG_OGP_SPI), yes) override CONFIG_BITBANG_SPI = yes else +#PLACEHOLDER_NEWPROG_BITBANGSPICONFIG1 CONFIG_BITBANG_SPI ?= no +#PLACEHOLDER_NEWPROG_BITBANGSPICONFIG2 endif endif endif @@ -996,6 +1003,14 @@ PROGRAMMER_OBJS += mstarddc_spi.o endif
+ifeq ($(CONFIG_VL805), yes) +FEATURE_CFLAGS += -D'CONFIG_VL805=1' +PROGRAMMER_OBJS += vl805.o +NEED_PCI := yes +endif + +#PLACEHOLDER_NEWPROG_COMPILERULE + ifeq ($(CONFIG_CH341A_SPI), yes) FEATURE_CFLAGS += -D'CONFIG_CH341A_SPI=1' PROGRAMMER_OBJS += ch341a_spi.o diff -r 7bf17529e516 atavia.c --- a/atavia.c Tue Dec 31 18:22:02 2019 +0100 +++ b/atavia.c Wed Jan 15 13:20:27 2020 +0100 @@ -142,7 +142,7 @@ if (rget_io_perms()) return 1;
- dev = pcidev_init(ata_via, PCI_ROM_ADDRESS); /* Acutally no BAR setup needed at all. */ + dev = pcidev_init(ata_via, PCI_ROM_ADDRESS); /* Actually no BAR setup needed at all. */ if (!dev) return 1;
diff -r 7bf17529e516 build_new_driver.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build_new_driver.sh Wed Jan 15 13:20:27 2020 +0100 @@ -0,0 +1,419 @@ +#!/bin/bash +# flashrom programmer driver skeleton builder. +# Copyright 2012,2020 Carl-Daniel Hailfinger +# Licensed under the GNU GPL v2 +# The license of the generated programmer driver is unrelated to the licsense +# of this script and can be specified below. + +# Fill in all info in the block below, and don't touch anything else. +# The data provided here is just an example. +# Name of the programmer. Needs to be an all-lowercase valid C identifier. +PROGRAMMERNAME=vl805 +# Short description of the programmer. Please do not use / inside the name, it will break the sed expressions. +PROGRAMMERDESCR="VIA VL805 programmer" +# Name of the programmer manufacturer. +PROGRAMMERMANUF="VIA" +# Website for the programmer. +PROGRAMMERURL="http://www.via.com/" +# Fill in your name here. +AUTHORNAME="Carl-Daniel Hailfinger" +# License version of the new programmer driver: 2 or 2+ (for 2+later) +LICENSE_GPL=2 +# Does the programmer need a map/unmap function? +HAVE_MAP=no +# Does the programmer have its own delay function? +HAVE_DELAY=no +# Does the programmer need some sort of direct hardware access? +NEED_PCI=yes +# Does the programmer need some sort of serial port access? +NEED_SERIAL=no +# Is the programmer a PCI device, USB device, or something else? +# You have to specify exactly one of PCI, USB, OTHER +DEVICETYPE=PCI +# Note: Usually a programmer only has one of NEED_PARLPCFWH, NEED_SPI or NEED_SPI_BITBANG set to yes. +# Does the programmer use Parallel/LPC/FWH functionality? +NEED_PARLPCFWH=no +# Which of PARALLEL/LPC/FWH buses does the programer use? FIXME: Explain how to handle multiple buses. +BUS_PARLPCFWH=none +# Does the programmer use SPI functionality without bitbanging? FIXME: Check if a SPI bitbanging driver with NEED_SPI=no generates useful code. +NEED_SPI=yes +# Does the programmer use the bitbanging SPI infrastructure? +NEED_SPI_BITBANG=no + +# No user serviceable parts below. +unset LANG +unset LANGUAGE +unset LC_COLLATE +if test $LICENSE_GPL = 2; then + GPLV3EITHER= + GPLV3OR= +elif test $LICENSE_GPL = 2+; then + GPLV3EITHER="either" + GPLV3OR="\n * (at your option) any later version" +else + echo "Specified license can not be handled automatically" + exit 1 +fi +if test $HAVE_MAP = yes; then MAPNAME=$PROGRAMMERNAME; else MAPNAME=fallback; fi +if test $HAVE_DELAY = yes; then DELAYNAME=$PROGRAMMERNAME; else DELAYNAME=internal; fi +PROGRAMMERNAMECAPS=$(echo -n $PROGRAMMERNAME|tr "[[:lower:]]" "[[:upper:]]") +CONFIGNAME=CONFIG_$PROGRAMMERNAMECAPS +ENUMNAME=PROGRAMMER_$PROGRAMMERNAMECAPS +if test $NEED_PCI = yes; then NEEDS="NEED_PCI := yes\n"; fi +if test $NEED_SERIAL = yes; then NEEDS+="NEED_SERIAL := yes\n"; fi + +sed "s-^//PLACEHOLDER_NEWPROG_PROGRAMMER_ARRAY-\ +#if ${CONFIGNAME} == 1\n\ + {\n\ + .name = "${PROGRAMMERNAME}",\n\ +\0-" flashrom.c >flashrom.c.mine +if test $DEVICETYPE = OTHER; then +sed "s-^//PLACEHOLDER_NEWPROG_PROGRAMMER_ARRAY-\ + .type = OTHER,\n\ + .devs.note = "Textual list of usable devices\\n",\n\ +\0-" flashrom.c.mine >flashrom.c.mine1 +mv flashrom.c.mine1 flashrom.c.mine +else +sed "s-^//PLACEHOLDER_NEWPROG_PROGRAMMER_ARRAY-\ + .type = ${DEVICETYPE},\n\ + .devs.dev = devs_${PROGRAMMERNAME},\n\ +\0-" flashrom.c.mine >flashrom.c.mine1 +mv flashrom.c.mine1 flashrom.c.mine +fi +sed "s-^//PLACEHOLDER_NEWPROG_PROGRAMMER_ARRAY-\ + .init = ${PROGRAMMERNAME}_init,\n\ + .map_flash_region = ${MAPNAME}_map,\n\ + .unmap_flash_region = ${MAPNAME}_unmap,\n\ + .delay = ${DELAYNAME}_delay,\n\ + },\n\ +#endif\n\ +\n\0-" flashrom.c.mine >flashrom.c.mine1 +mv flashrom.c.mine1 flashrom.c.mine + +sed -e "s/^#PLACEHOLDER_NEWPROG_DEFAULTCONFIG/\ +# Enable ${PROGRAMMERDESCR} for now.\n\ +${CONFIGNAME} ?= yes\n\ +\n\0/" \ +-e "s/^#PLACEHOLDER_NEWPROG_COMPILERULE/\ +ifeq ($(${CONFIGNAME}), yes)\n\ +FEATURE_CFLAGS += -D'${CONFIGNAME}=1'\n\ +PROGRAMMER_OBJS += ${PROGRAMMERNAME}.o\n\ +${NEEDS}\ +endif\n\ +\n\0/" Makefile >Makefile.mine + +if test $NEED_SPI_BITBANG = yes; then +sed -e "s/^#PLACEHOLDER_NEWPROG_BITBANGSPICONFIG1/\ +ifeq ($(${CONFIGNAME}), yes)\n\ +override CONFIG_BITBANG_SPI = yes\n\ +else\n\ +\0/" \ +-e "s/^#PLACEHOLDER_NEWPROG_BITBANGSPICONFIG2/\ +\0\n\ +endif/;" Makefile.mine >Makefile.mine1 +mv Makefile.mine1 Makefile.mine +fi + +sed -e "s-^//PLACEHOLDER_NEWPROG_PROGRAMMER_ENUM-\ +#if ${CONFIGNAME} == 1\n\ + ${ENUMNAME},\n\ +#endif\n\ +\0-" \ +-e "s-^//PLACEHOLDER_NEWPROG_PUBLICFUNCTIONS-\ +/* ${PROGRAMMERNAME}.c */\n\ +#if ${CONFIGNAME} == 1\n\ +int ${PROGRAMMERNAME}_init(void);\n\ +\0-" programmer.h >programmer.h.mine + +if test $DEVICETYPE = PCI -o $DEVICETYPE = USB; then +sed -e "s-^//PLACEHOLDER_NEWPROG_PUBLICFUNCTIONS-\ +extern const struct dev_entry devs_${PROGRAMMERNAME}[];\n\ +\n\0-" programmer.h.mine >programmer.h.mine1 +mv programmer.h.mine1 programmer.h.mine +fi + +sed -e "s-^//PLACEHOLDER_NEWPROG_PUBLICFUNCTIONS-\ +#endif\n\ +\n\0-" programmer.h.mine >programmer.h.mine1 +mv programmer.h.mine1 programmer.h.mine + +if test $NEED_SPI_BITBANG = yes; then +sed -e "s-//PLACEHOLDER_NEWPROG_SELECT_SPI_BITBANG$-\ +|| ${CONFIGNAME} == 1 \0-" programmer.h.mine >programmer.h.mine1 +mv programmer.h.mine1 programmer.h.mine +fi + +# No idea if roff supports hidden comments. Hook up to hopefully unchanged sequences. +sed -e "s/.*PLACEHOLDER_NEWPROG_MAN_SHORTDESCRIPTION/\ +.BR "* ${PROGRAMMERNAME}" " (${PROGRAMMERDESCR})"\n\ +.sp\n\ +\0/" \ +-e "s/.*PLACEHOLDER_NEWPROG_MAN_LONGDESCRIPTION/\ +.SS\n\ +.BR "${PROGRAMMERNAME} " programmer\n\ +Please describe the programmer parameters here.\n\ +\0/" \ +-e "s/.*PLACEHOLDER_NEWPROG_MAN_REQUIREMENTS/\ +.B ${PROGRAMMERNAME}\n\ +Please describe the programmer requirements here.\n\ +.sp\n\ +\0/" flashrom.8.tmpl > flashrom.8.tmpl.mine + +cat >$PROGRAMMERNAME.c.mine <<EOF +/* + * This file is part of the flashrom project. + * + * Copyright (C) $(date +%Y) ${AUTHORNAME} + * + * 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; ${GPLV3EITHER}version 2 of the License${GPLV3OR}. + * + * 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 ${PROGRAMMERDESCR} hardware by ${PROGRAMMERMANUF}. + * See ${PROGRAMMERURL} for more info. + */ + +#include "flash.h" +#include "programmer.h" + +EOF + +if test $DEVICETYPE = PCI -o $DEVICETYPE = USB; then +cat >>$PROGRAMMERNAME.c.mine <<EOF +const struct dev_entry devs_${PROGRAMMERNAME}[] = { + {0xdead, 0xbeef, NT, "Vendor name", "Device name"}, + + {0}, +}; + +EOF +fi + +if test $NEED_PARLPCFWH = yes; then +cat >>$PROGRAMMERNAME.c.mine <<EOF +static void ${PROGRAMMERNAME}_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr) +{ + /* Write a byte to the flash chip. */ +} + +static uint8_t ${PROGRAMMERNAME}_chip_readb(const struct flashctx *flash, const chipaddr addr) +{ + /* Read a byte from the flash chip and return it. */ + /* Set it to 0xff to get the template to compile. */ + uint8_t val = 0xff; + + return val; +} + +static const struct par_programmer par_programmer_${PROGRAMMERNAME} = { + .chip_readb = ${PROGRAMMERNAME}_chip_readb, + /* If your programmer supports word/long accesses, change the lines below. */ + .chip_readw = fallback_chip_readw, + .chip_readl = fallback_chip_readl, + .chip_readn = fallback_chip_readn, + .chip_writeb = ${PROGRAMMERNAME}_chip_writeb, + .chip_writew = fallback_chip_writew, + .chip_writel = fallback_chip_writel, + .chip_writen = fallback_chip_writen, +}; + +EOF +fi + +if test $NEED_SPI_BITBANG = yes; then +cat >>$PROGRAMMERNAME.c.mine <<EOF +static void ${PROGRAMMERNAME}_bitbang_set_cs(int val) +{ + /* Set/clear the CS# line. */ +} + +static void ${PROGRAMMERNAME}_bitbang_set_sck(int val) +{ + /* Set/clear the SCLK line. */ +} + +static void ${PROGRAMMERNAME}_bitbang_set_mosi(int val) +{ + /* Set/clear the MOSI line. */ +} + +static int ${PROGRAMMERNAME}_bitbang_get_miso(void) +{ + /* Get the state of the MISO line and return it. */ + /* Set it to 1 to get the template to compile. */ + int misoval = 1; + + return misoval; +} + +/* If this programmer does not support requesting/releasing the SPI bus, remove + * the functions ${PROGRAMMERNAME}_request_spibus and ${PROGRAMMERNAME}_release_spibus + * and set bitbang_spi_master_${PROGRAMMERNAME} members .request_bus and .release_bus + * to NULL. + */ +static void ${PROGRAMMERNAME}_request_spibus(void) +{ +} + +static void ${PROGRAMMERNAME}_release_spibus(void) +{ +} + +static const struct bitbang_spi_master bitbang_spi_master_${PROGRAMMERNAME} = { + .set_cs = ${PROGRAMMERNAME}_bitbang_set_cs, + .set_sck = ${PROGRAMMERNAME}_bitbang_set_sck, + .set_mosi = ${PROGRAMMERNAME}_bitbang_set_mosi, + .get_miso = ${PROGRAMMERNAME}_bitbang_get_miso, + .request_bus = ${PROGRAMMERNAME}_request_spibus, + .release_bus = ${PROGRAMMERNAME}_release_spibus, + .half_period = 1, /* Delay in microseconds before each SCLK level change. */ +}; + +EOF +fi + +if test $NEED_SPI = yes; then +cat >>$PROGRAMMERNAME.c.mine <<EOF +/* Include string.h for memset to get the template to compile. Remove this. */ +#include <string.h> +static int ${PROGRAMMERNAME}_spi_send_command(struct flashctx *flash, + unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + /* Send a SPI command to the flash chip. */ + /* Set readarr to 0xff to get the template to compile and run without segfaults. */ + memset(readarr, 0xff, readcnt); + + return 0; +} + +static const struct spi_master spi_master_${PROGRAMMERNAME} = { + .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 = ${PROGRAMMERNAME}_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = default_spi_write_256, +}; + +EOF +fi + +cat >>$PROGRAMMERNAME.c.mine <<EOF +static int ${PROGRAMMERNAME}_shutdown(void *data) +{ + /* Shutdown stuff. */ + return 0; +} + +int ${PROGRAMMERNAME}_init(void) +{ + /* Init stuff (i.e. parameter parsing) here which does not need to be + * undone. + */ + + /* If your shutdown function takes a parameter, replace NULL with it. */ + register_shutdown(${PROGRAMMERNAME}_shutdown, NULL); + + /* Init stuff which needs to be undone on shutdown. */ + +EOF + +if test $NEED_SPI_BITBANG = yes; then +cat >>$PROGRAMMERNAME.c.mine <<EOF + /* 1 usec halfperiod delay, change as needed. */ + if (bitbang_spi_init(&bitbang_spi_master_${PROGRAMMERNAME})) + return 1; + +EOF +fi + +if test $NEED_SPI = yes; then +cat >>$PROGRAMMERNAME.c.mine <<EOF + register_spi_master(&spi_master_${PROGRAMMERNAME}); + +EOF +fi + +if test $NEED_PARLPCFWH = yes; then +cat >>$PROGRAMMERNAME.c.mine <<EOF + register_par_programmer(&par_programmer_${PROGRAMMERNAME}, BUS_${BUS_PARLPCFWH}); + +EOF +fi + +cat >>$PROGRAMMERNAME.c.mine <<EOF + return 0; +} +EOF + +csplit -f .newmeson_options meson_options.txt "/#PLACEHOLDER_NEWPROG_MESON_OPTION_START/+1" "/#PLACEHOLDER_NEWPROG_MESON_OPTION_END/" +echo "option('config_${PROGRAMMERNAME}', type : 'boolean', value : true, description : '${PROGRAMMERDESCR}')" >>.newmeson_options01 +sort .newmeson_options01 >.newmeson_options03 +cat .newmeson_options00 .newmeson_options03 .newmeson_options02 >meson_options.txt.mine +rm .newmeson_options00 .newmeson_options01 .newmeson_options02 .newmeson_options03 + +csplit -f .newmeson meson.build "/#PLACEHOLDER_NEWPROG_MESON_CONFIGFETCH_START/+1" "/#PLACEHOLDER_NEWPROG_MESON_CONFIGFETCH_END/" +echo "config_${PROGRAMMERNAME} = get_option('config_${PROGRAMMERNAME}')" >>.newmeson01 +sort .newmeson01 >.newmeson03 +cat .newmeson00 .newmeson03 .newmeson02 >.newmeson.build.mine +rm .newmeson00 .newmeson01 .newmeson02 .newmeson03 + +if test $DEVICETYPE = PCI ; then + csplit -f .newmeson .newmeson.build.mine "/#PLACEHOLDER_NEWPROG_MESON_PCI_REQUIREMENT_MISSING_START/+1" "/#PLACEHOLDER_NEWPROG_MESON_PCI_REQUIREMENT_MISSING_END/" + echo " config_${PROGRAMMERNAME} = false" >>.newmeson01 + sort .newmeson01 >.newmeson03 + cat .newmeson00 .newmeson03 .newmeson02 >.newmeson.build.mine + rm .newmeson00 .newmeson01 .newmeson02 .newmeson03 +fi + +if test $DEVICETYPE = USB ; then + csplit -f .newmeson .newmeson.build.mine "/#PLACEHOLDER_NEWPROG_MESON_USB_REQUIREMENT_MISSING_START/+1" "/#PLACEHOLDER_NEWPROG_MESON_USB_REQUIREMENT_MISSING_END/" + echo " config_${PROGRAMMERNAME} = false" >>.newmeson01 + sort .newmeson01 >.newmeson03 + cat .newmeson00 .newmeson03 .newmeson02 >.newmeson.build.mine + rm .newmeson00 .newmeson01 .newmeson02 .newmeson03 +fi + +csplit -f .newmeson .newmeson.build.mine "/#PLACEHOLDER_NEWPROG_MESON_FILES_DEFINES_NEEDS_START/+1" "/#PLACEHOLDER_NEWPROG_MESON_FILES_DEFINES_NEEDS_END/" +# FIXME: The current meson.build always builds the PCI intrastructure unless explicitly disabled. +cat >>.newmeson01 <<EOF +if config_${PROGRAMMERNAME} + srcs += '${PROGRAMMERNAME}.c' + cargs += '-D${CONFIGNAME}=1' +EOF +if $NEED_SERIAL = yes; then + cat >>.newmeson01 <<EOF + need_serial = true +EOF +cat >>.newmeson01 <<EOF +endif +EOF +# FIXME: Sorting is a bit more complicated here. Skip it for now. +cat .newmeson00 .newmeson01 .newmeson02 >.newmeson.build.mine +rm .newmeson00 .newmeson01 .newmeson02 + +mv .newmeson.build.mine meson.build.mine + +echo "The driver skeleton has been created in $PROGRAMMERNAME.c.mine" +echo "Modified versions of existing files have been created with extension .mine" +echo "You can replace the original files with the modified versions by running" +echo "for a in *; do test -f $a.mine && mv $a.mine $a; done" +echo "If you want to use the newly generated skeleton $PROGRAMMERNAME.c.mine , run" +echo "mv $PROGRAMMERNAME.c.mine $PROGRAMMERNAME.c" +echo +echo "WARNING: Please note that rerunning build_new_driver.sh will overwrite" +echo "all *.mine files, but it won't touch $PROGRAMMERNAME.c ." +echo "If something goes wrong, you can revert all files which look odd and" +echo "run this script again." diff -r 7bf17529e516 flashchips.h --- a/flashchips.h Tue Dec 31 18:22:02 2019 +0100 +++ b/flashchips.h Wed Jan 15 13:20:27 2020 +0100 @@ -602,7 +602,7 @@ #define PMC_PM49FL004 0x6E
/* - * The Sanyo chip found so far uses SPI, first byte is manufacture code, + * The Sanyo chip found so far uses SPI, first byte is manufacturer code, * second byte is the device code, * third byte is a dummy byte. */ diff -r 7bf17529e516 flashrom.8.tmpl --- a/flashrom.8.tmpl Tue Dec 31 18:22:02 2019 +0100 +++ b/flashrom.8.tmpl Wed Jan 15 13:20:27 2020 +0100 @@ -345,6 +345,9 @@ .sp .BR "* stlinkv3_spi" " (for SPI flash ROMs attached to STMicroelectronics STLINK V3 devices)" .sp +.BR "* vl805" " (VIA VL805 programmer)" +.sp +."PLACEHOLDER_NEWPROG_MAN_SHORTDESCRIPTION Some programmers have optional or mandatory parameters which are described in detail in the .B PROGRAMMER-SPECIFIC INFORMATION @@ -1287,7 +1290,10 @@ If the passed frequency is not supported by the adapter the nearest lower supported frequency will be used. .SS - +.BR "vl805 " programmer +Please describe the programmer parameters here. +.SS +."PLACEHOLDER_NEWPROG_MAN_LONGDESCRIPTION .SH EXAMPLES To back up and update your BIOS, run .sp @@ -1365,6 +1371,10 @@ .B ogp needs PCI configuration space read access and raw memory access. .sp +.B vl805 +Please describe the programmer requirements here. +.sp +."PLACEHOLDER_NEWPROG_MAN_REQUIREMENTS On OpenBSD, you can obtain raw access permission by setting .B "securelevel=-1" in diff -r 7bf17529e516 flashrom.c --- a/flashrom.c Tue Dec 31 18:22:02 2019 +0100 +++ b/flashrom.c Wed Jan 15 13:20:27 2020 +0100 @@ -473,6 +473,19 @@ }, #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 + +//PLACEHOLDER_NEWPROG_PROGRAMMER_ARRAY {0}, /* This entry corresponds to PROGRAMMER_INVALID. */ };
diff -r 7bf17529e516 meson.build --- a/meson.build Tue Dec 31 18:22:02 2019 +0100 +++ b/meson.build Wed Jan 15 13:20:27 2020 +0100 @@ -30,6 +30,7 @@ add_project_arguments('-DFLASHROM_VERSION="' + meson.project_version() + '"', language : 'c')
# get defaults from configure +# PLACEHOLDER_NEWPROG_MESON_CONFIGFETCH_START config_atahpt = get_option('config_atahpt') config_atapromise = get_option('config_atapromise') config_atavia = get_option('config_atavia') @@ -62,6 +63,7 @@ config_serprog = get_option('config_serprog') config_usbblaster_spi = get_option('config_usbblaster_spi') config_stlinkv3_spi = get_option('config_stlinkv3_spi') +# PLACEHOLDER_NEWPROG_MESON_CONFIGFETCH_END
cargs = [] deps = [] @@ -86,11 +88,13 @@ srcs += 'usbdev.c' deps += dependency('libusb-1.0') else +# PLACEHOLDER_NEWPROG_MESON_USB_REQUIREMENT_MISSING_START config_ch341a_spi = false config_dediprog = false config_digilent_spi = false config_developerbox_spi = false config_pickit2_spi = false +# PLACEHOLDER_NEWPROG_MESON_USB_REQUIREMENT_MISSING_END endif
# some programmers require libpci @@ -99,6 +103,7 @@ deps += dependency('libpci') cargs += '-DNEED_PCI=1' else +# PLACEHOLDER_NEWPROG_MESON_PCI_REQUIREMENT_MISSING_START config_atahpt = false config_atapromise = false config_atavia = false @@ -116,9 +121,11 @@ config_rayer_spi = false config_satamv = false config_satasii = false +# PLACEHOLDER_NEWPROG_MESON_PCI_REQUIREMENT_MISSING_END endif
# set defines for configured programmers +# PLACEHOLDER_NEWPROG_MESON_FILES_DEFINES_NEEDS_START if config_atahpt srcs += 'atahpt.c' cargs += '-DCONFIG_ATAHPT=1' @@ -276,6 +283,7 @@ srcs += 'stlinkv3_spi.c' cargs += '-DCONFIG_STLINKV3_SPI=1' endif +# PLACEHOLDER_NEWPROG_MESON_FILES_DEFINES_NEEDS_END
# bitbanging SPI infrastructure if config_bitbang_spi diff -r 7bf17529e516 meson_options.txt --- a/meson_options.txt Tue Dec 31 18:22:02 2019 +0100 +++ b/meson_options.txt Wed Jan 15 13:20:27 2020 +0100 @@ -1,6 +1,7 @@ option('pciutils', type : 'boolean', value : true, description : 'use pciutils') option('usb', type : 'boolean', value : true, description : 'use libusb1')
+#PLACEHOLDER_NEWPROG_MESON_OPTION_START option('config_atahpt', type : 'boolean', value : false, description : 'Highpoint (HPT) ATA/RAID controllers') option('config_atapromise', type : 'boolean', value : false, description : 'Promise ATA controller') option('config_atavia', type : 'boolean', value : true, description : 'VIA VT6421A LPC memory') @@ -33,3 +34,4 @@ option('config_satasii', type : 'boolean', value : true, description : 'SiI SATA controllers') option('config_serprog', type : 'boolean', value : true, description : 'serprog') option('config_usbblaster_spi', type : 'boolean', value : true, description : 'Altera USB-Blaster dongles') +#PLACEHOLDER_NEWPROG_MESON_OPTION_END diff -r 7bf17529e516 pcidev.c --- a/pcidev.c Tue Dec 31 18:22:02 2019 +0100 +++ b/pcidev.c Wed Jan 15 13:20:27 2020 +0100 @@ -148,33 +148,6 @@ return (uintptr_t)addr; }
-static uintptr_t pcidev_validate(struct pci_dev *dev, int bar, const struct dev_entry *devs) -{ - unsigned i; - - /* Check against list of supported devices. */ - for (i = 0; devs[i].device_name != NULL; i++) { - if (dev->device_id != devs[i].device_id) - continue; - - msg_pinfo("Found "%s %s" (%04x:%04x, BDF %02x:%02x.%x).\n", - devs[i].vendor_name, devs[i].device_name, - dev->vendor_id, dev->device_id, dev->bus, dev->dev, - dev->func); - - if (devs[i].status == NT) - msg_pinfo("===\nThis PCI device is UNTESTED. Please report the 'flashrom -p " - "xxxx' output\n" - "to flashrom@flashrom.org if it works for you. Please add the name " - "of your\n" - "PCI device to the subject. Thank you for your help!\n===\n"); - - - return pcidev_readbar(dev, bar); - } - return 0; -} - static int pcidev_shutdown(void *data) { if (pacc == NULL) { @@ -210,10 +183,12 @@ struct pci_dev *pcidev_init(const struct dev_entry *devs, int bar) { struct pci_dev *dev; + struct pci_dev *found_dev = NULL; struct pci_filter filter; char *pcidev_bdf; char *msg = NULL; int found = 0; + int i; uintptr_t addr = 0;
if (pci_init_common() != 0) @@ -232,10 +207,30 @@
for (dev = pacc->devices; dev; dev = dev->next) { if (pci_filter_match(&filter, dev)) { + /* Check against list of supported devices. */ + for (i = 0; devs[i].device_name != NULL; i++) + if ((dev->vendor_id == devs[i].vendor_id) && + (dev->device_id == devs[i].device_id)) + break; + /* Not supported, try the next one. */ + if (devs[i].device_name == NULL) + continue; + + msg_pdbg("Found "%s %s" (%04x:%04x, BDF %02x:%02x.%x).\n", devs[i].vendor_name, + devs[i].device_name, dev->vendor_id, dev->device_id, dev->bus, dev->dev, + dev->func); + if (devs[i].status == NT) + msg_pinfo("===\nThis PCI device is UNTESTED. Please report the 'flashrom -p " + "xxxx' output\n" + "to flashrom@flashrom.org if it works for you. Please add the name " + "of your\n" + "PCI device to the subject. Thank you for your help!\n===\n"); + /* FIXME: We should count all matching devices, not * just those with a valid BAR. */ - if ((addr = pcidev_validate(dev, bar, devs)) != 0) { + if ((addr = pcidev_readbar(dev, bar)) != 0) { + found_dev = dev; found++; } } @@ -251,7 +246,7 @@ return NULL; }
- return dev; + return found_dev; }
enum pci_write_type { diff -r 7bf17529e516 programmer.h --- a/programmer.h Tue Dec 31 18:22:02 2019 +0100 +++ b/programmer.h Wed Jan 15 13:20:27 2020 +0100 @@ -127,6 +127,10 @@ #if CONFIG_STLINKV3_SPI == 1 PROGRAMMER_STLINKV3_SPI, #endif +#if CONFIG_VL805 == 1 + PROGRAMMER_VL805, +#endif +//PLACEHOLDER_NEWPROG_PROGRAMMER_ENUM PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -573,6 +577,15 @@ int ni845x_spi_init(void); #endif
+/* vl805.c */ +#if CONFIG_VL805 == 1 +int vl805_init(void); +extern const struct dev_entry devs_vl805[]; + +#endif + +//PLACEHOLDER_NEWPROG_PUBLICFUNCTIONS + /* flashrom.c */ struct decode_sizes { uint32_t parallel; diff -r 7bf17529e516 vl805.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vl805.c Wed Jan 15 13:20:27 2020 +0100 @@ -0,0 +1,171 @@ +/* + * 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 "flash.h" +#include "programmer.h" +#include "hwaccess.h" +#include "spi.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++) { + readarr[j + i] = indata & 0xff; + indata >>= 8; + } + } + + 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, +}; + +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) +{ + 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); + uint32_t 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); + val = vl805_getregval(VL805_REG_0x30004); + if (val != 0x00000200) { + msg_perr("VL805_REG_0x30004 returned unexpected value 0x%08x\n", val); + return 1; + } + vl805_setregval(VL805_REG_0x30004, 0x00000200); + val = vl805_getregval(VL805_REG_WB_EN); + if (val != 0xffffffff) { + msg_perr("VL805_REG_WB_EN returned unexpected value 0x%08x\n", val); + return 1; + } + vl805_setregval(VL805_REG_WB_EN, 0xffffff01); + val = vl805_getregval(VL805_REG_STOP_POLLING); + if (val != 0x00000001) { + msg_perr("VL805_REG_STOP_POLLING returned unexpected value 0x%08x\n", val); + return 1; + } + 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; +}
On 15.01.20 13:29, Carl-Daniel Hailfinger wrote:
VIA VL805 support, first draft. No idea if it actually works. It is highly likely that SPI accesses with a readcnt not being a multiple of 4 will return incorrect data due to the unknown encoding of the register containing SPI responses of the chip. That will be obvious from any verbose log, though, and once I have such data, adjusting the code is trivial.
Reverse engineered based on PCI traces created by cleverca22 on a Raspberry Pi 4 Model B.
RFC, with some unrelated (automatic programmer driver writer) and some related changes (buggy PCI patch reverted).
In case someone is feeling less brave, I also have a version which aborts early after the init sequence.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
My apologies for the whitespace-mangled patch. I checked that the patch was sent correctly to the mail server of my provider, but after the round trip it was mangled. No idea where that happened, but I'll try to debug the problem before I send the next version.
Regards, Carl-Daniel