diff mbox series

VIA VL805 support

Message ID 5ddf65c1-2361-98cd-37ce-abb373d85ab7@gmx.net
State New
Headers show
Series VIA VL805 support | expand

Commit Message

Carl-Daniel Hailfinger Jan. 15, 2020, 12:29 p.m. UTC
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>

Comments

Carl-Daniel Hailfinger Jan. 15, 2020, 12:49 p.m. UTC | #1
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
diff mbox series

Patch

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;
+}