Just for reference - see this patch too http://patchwork.coreboot.org/patch/1736/
Best regards, Anton Kochkov.
On Thu, Mar 21, 2013 at 1:58 AM, Ondrej Zary linux@rainbow-software.org wrote:
Hello, this patch adds support for Willem EPROM programmer to flashrom. It includes parallel and LPC/FWH chips (using the socket on PCB4.0 - hope that it will work with LPC/FWH adapter on other PCB versions).
It seems to work, according to some basic tests (W49F002U, AT29C010A, SST 39SF020A, SST 49LF002A).
I don't like the parallel port access code (taken from rayer_spi) - flashrom should have a parallel port access layer (.programmer_type = LPT) which could do the initialization (setting port address/path), caching and access the port either directly or using ppdev on Linux. Maybe something simple like would be enough: lpt_set_data_mask(uint8_t data, uint8_t mask); lpt_set_ctrl_mask(uint8_t data, uint8_t mask); lpt_get_status();
Signed-off-by: Ondrej Zary linux@rainbow-software.org
diff -urp -x .svn flashrom-orig//flashrom.c flashrom/flashrom.c --- flashrom-orig//flashrom.c 2013-03-20 22:07:14.000000000 +0100 +++ flashrom/flashrom.c 2013-03-17 11:32:37.000000000 +0100 @@ -308,6 +308,19 @@ const struct programmer_entry programmer }, #endif
+#if CONFIG_WILLEM == 1
{
.name = "willem",
.type = OTHER,
/* FIXME */
.devs.note = "Willem EPROM progammer on parallel port\n",
.init = willem_init,
.map_flash_region = fallback_map,
.unmap_flash_region = fallback_unmap,
.delay = internal_delay,
},
+#endif
{0}, /* This entry corresponds to PROGRAMMER_INVALID. */
};
diff -urp -x .svn flashrom-orig//Makefile flashrom/Makefile --- flashrom-orig//Makefile 2013-03-20 22:07:14.000000000 +0100 +++ flashrom/Makefile 2013-03-17 11:33:31.000000000 +0100 @@ -210,6 +210,11 @@ UNSUPPORTED_FEATURES += CONFIG_SATAMV=ye else override CONFIG_SATAMV = no endif +ifeq ($(CONFIG_WILLEM), yes) +UNSUPPORTED_FEATURES += CONFIG_WILLEM=yes +else +override CONFIG_WILLEM = no +endif endif
ifeq ($(TARGET_OS), libpayload) @@ -289,6 +294,11 @@ UNSUPPORTED_FEATURES += CONFIG_SATAMV=ye else override CONFIG_SATAMV = no endif +ifeq ($(CONFIG_WILLEM), yes) +UNSUPPORTED_FEATURES += CONFIG_WILLEM=yes +else +override CONFIG_WILLEM = no +endif endif
############################################################################### @@ -382,6 +392,9 @@ CONFIG_SATAMV ?= yes # Enable Linux spidev interface by default. We disable it on non-Linux targets. CONFIG_LINUX_SPI ?= yes
+# Always enable Willem EPROM programmer hardware for now. +CONFIG_WILLEM ?= yes
# Disable wiki printing by default. It is only useful if you have wiki access. CONFIG_PRINT_WIKI ?= no
@@ -549,6 +562,13 @@ FEATURE_CFLAGS += $(shell LC_ALL=C grep PROGRAMMER_OBJS += linux_spi.o endif
+ifeq ($(CONFIG_WILLEM), yes) +FEATURE_CFLAGS += -D'CONFIG_WILLEM=1' +PROGRAMMER_OBJS += willem.o +# Actually, NEED_PCI is wrong. NEED_IOPORT_ACCESS would be more correct. +NEED_PCI := yes +endif
ifeq ($(NEED_SERIAL), yes) LIB_OBJS += serial.o endif diff -urp -x .svn flashrom-orig//programmer.h flashrom/programmer.h --- flashrom-orig//programmer.h 2013-03-20 22:07:14.000000000 +0100 +++ flashrom/programmer.h 2013-03-20 22:08:55.000000000 +0100 @@ -87,6 +87,9 @@ enum programmer { #if CONFIG_LINUX_SPI == 1 PROGRAMMER_LINUX_SPI, #endif +#if CONFIG_WILLEM == 1
PROGRAMMER_WILLEM,
+#endif PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -459,6 +462,11 @@ int linux_spi_init(void); int dediprog_init(void); #endif
+/* willem.c */ +#if CONFIG_WILLEM == 1 +int willem_init(void); +#endif
/* flashrom.c */ struct decode_sizes { uint32_t parallel; --- flashrom-orig/willem.c 1970-01-01 01:00:00.000000000 +0100 +++ flashrom/willem.c 2013-03-20 22:56:04.000000000 +0100 @@ -0,0 +1,296 @@ +/*
- This file is part of the flashrom project.
- Copyright (C) 2013 Ondrej Zary
- Copyright (C) 2011 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 Willem EPROM Programmer on parallel port.
- This driver can access parallel flash and LPC/FWH chips.
- For SPI chips, see rayer_spi.
- Willem HW description:
- LPT STROBE signal is used to control VPP (0 means ENABLED)
- LPT INIT signal is used to control VCC (1 means ENABLED)
- LPT AUTOLF signal is used to switch data direction and flash OE signal (1=4503 enabled, OE disabled)
- LPT SELIN signal is called S4 on PCB4.0 schematic and used as flash CE or WE signal (selected by DIP switch)
- LPT ACK input is used for reading flash D0..D7 through 4014 shift register
- LPT BUSY input is used for reading data in serial memory programming
- LPT PAPER_END, SELECT and ERROR inputs are not used
- LPT D0..D7 pins are used for two purposes, switched by AUTOLF
- data output (through 2x4503 buffers) to flash D0..D7
- CLK (D0) and D (D1) outputs to 3x4015 address shift registers
- D2 is also used as (inverted) CLK for 4014 shift register
- */
+#if defined(__i386__) || defined(__x86_64__)
+#include <stdlib.h> +#include <string.h> +#include "flash.h" +#include "programmer.h" +#include "hwaccess.h"
+static uint16_t lpt_iobase; +/* Cached value of last byte sent. */ +static uint8_t lpt_outbyte[3];
+static void willem_chip_writeb(const struct flashctx *flash, uint8_t val,
chipaddr addr);
+static uint8_t willem_chip_readb(const struct flashctx *flash,
const chipaddr addr);
+static const struct par_programmer par_programmer_willem = {
.chip_readb = willem_chip_readb,
.chip_readw = fallback_chip_readw,
.chip_readl = fallback_chip_readl,
.chip_readn = fallback_chip_readn,
.chip_writeb = willem_chip_writeb,
.chip_writew = fallback_chip_writew,
.chip_writel = fallback_chip_writel,
.chip_writen = fallback_chip_writen,
+};
+static int willem_shutdown(void *data) +{
/* Disable VPP and VCC. */
lpt_outbyte[2] = (1 << 0);
OUTB(lpt_outbyte[2], lpt_iobase + 2);
return 0;
+}
+static void willem_set_address(uint32_t address, int highest_bit); +static void willem_set_data(uint8_t data); +static uint8_t willem_get_data(void); +static void willem_set_write(int enable);
+int willem_init(void) +{
char *arg = NULL;
int lpc_fwh = 0, vpp_enable = 0;
/* This is copied from rayer_spi */
/* Non-default port requested? */
arg = extract_programmer_param("iobase");
if (arg) {
char *endptr = NULL;
unsigned long tmp;
tmp = strtoul(arg, &endptr, 0);
/* Port 0, port >0x10000, unaligned ports and garbage strings
* are rejected.
*/
if (!tmp || (tmp >= 0x10000) || (tmp & 0x3) ||
(*endptr != '\0')) {
/* Using ports below 0x100 is a really bad idea, and
* should only be done if no port between 0x100 and
* 0xfffc works due to routing issues.
*/
msg_perr("Error: iobase= specified, but the I/O base "
"given was invalid.\nIt must be a multiple of "
"0x4 and lie between 0x100 and 0xfffc.\n");
free(arg);
return 1;
} else {
lpt_iobase = (uint16_t)tmp;
msg_pinfo("Non-default I/O base requested. This will "
"not change the hardware settings.\n");
}
} else {
/* Pick a default value for the I/O base. */
lpt_iobase = 0x378;
}
free(arg);
msg_pdbg("Using address 0x%x as I/O base for parallel port access.\n",
lpt_iobase);
arg = extract_programmer_param("type");
if (arg) {
if (!strcmp(arg, "lpc_fwh"))
lpc_fwh = 1;
else if (!strcmp(arg, "12v"))
vpp_enable = 1;
free(arg);
}
if (rget_io_perms())
return 1;
/* Get the initial value before writing to any line. */
lpt_outbyte[0] = INB(lpt_iobase);
lpt_outbyte[2] = INB(lpt_iobase + 2);
if (register_shutdown(willem_shutdown, NULL))
return 1;
/* Enable VCC, disable VPP */
lpt_outbyte[2] = (1 << 2) | (1 << 0);
if (lpc_fwh || vpp_enable) /* enable VPP for LPC/FWH (used to generate VCC) */
lpt_outbyte[2] &= ~(1 << 0); /* inverted */
OUTB(lpt_outbyte[2], lpt_iobase + 2);
max_rom_decode.parallel = 1 << 24; /* 24 address lines = 16 MB */
register_par_programmer(&par_programmer_willem, lpc_fwh ? (BUS_LPC | BUS_FWH) : BUS_PARALLEL);
return 0;
+}
+/* clock-in the address into 4015 shift registers (3 x 8-bit) that drive A0..A23 signals */ +/* A23 is not connected anywhere on PCB4.0 */ +static void willem_set_address(uint32_t address, int highest_bit) +{
int i;
lpt_outbyte[2] &= ~(1 << 1); /* set address mode */
OUTB(lpt_outbyte[2], lpt_iobase + 2);
/* start with MSB */
for (i = highest_bit; i >= 0; i--) {
/* clear CLK and D */
lpt_outbyte[0] &= ~((1 << 0) | (1 << 1));
if (address & (1 << i))
lpt_outbyte[0] |= (1 << 1); /* D1 */
OUTB(lpt_outbyte[0], lpt_iobase);
/* set CLK */
lpt_outbyte[0] |= (1 << 0); /* D0 */
OUTB(lpt_outbyte[0], lpt_iobase);
}
/* clear CLK */
lpt_outbyte[0] &= ~(1 << 0); /* D0 */
OUTB(lpt_outbyte[0], lpt_iobase);
+}
+/* set data on LPT D0..D7 pins (connected to 4503 buffers that drive D0..D7 signals */ +static void willem_set_data(uint8_t data) +{
lpt_outbyte[2] |= (1 << 1); /* set data mode */
OUTB(lpt_outbyte[2], lpt_iobase + 2);
lpt_outbyte[0] = data;
OUTB(lpt_outbyte[0], lpt_iobase);
+}
+/* read data through 4014 shift register */ +static uint8_t willem_get_data(void) +{
int i;
uint8_t data = 0;
lpt_outbyte[2] |= (1 << 1); /* set data mode and disable OE# */
OUTB(lpt_outbyte[2], lpt_iobase + 2);
lpt_outbyte[2] &= ~(1 << 1); /* set address mode (so data lines are not driven by 4503 buffers) and enable OE# */
OUTB(lpt_outbyte[2], lpt_iobase + 2);
lpt_outbyte[0] |= (1 << 1); /* set parallel input - P/S pin high (D1) */
lpt_outbyte[0] |= (1 << 2); /* set CLK to low (inverted) (D2) */
OUTB(lpt_outbyte[0], lpt_iobase);
+// programmer_delay(100);
/* pulse CLK to input D0..D8 data into 4014 */
lpt_outbyte[0] &= ~(1 << 2); /* set CLK to high (inverted) (D2) */
OUTB(lpt_outbyte[0], lpt_iobase);
lpt_outbyte[0] |= (1 << 2); /* set CLK to low (inverted) (D2) */
lpt_outbyte[0] &= ~(1 << 1); /* clear parallel input - P/S pin low (D1) */
OUTB(lpt_outbyte[0], lpt_iobase);
/* now clock-out the data (MSB first) and read from ACK pin (inverted) */
for (i = 0; i < 8; i++) {
data <<= 1;
if (!(INB(lpt_iobase + 1) & (1 << 6))) /* inverted */
data |= 1;
lpt_outbyte[0] &= ~(1 << 2); /* set CLK to high (inverted) (D2) */
OUTB(lpt_outbyte[0], lpt_iobase);
lpt_outbyte[0] |= (1 << 2); /* set CLK to low (inverted) (D2) */
OUTB(lpt_outbyte[0], lpt_iobase);
}
return data;
+}
+/* Enable WE (S4 signal) */ +static void willem_set_write(int enable) +{
lpt_outbyte[2] &= ~(1 << 3); /* SELIN */
if (enable)
lpt_outbyte[2] |= (1 << 3);
OUTB(lpt_outbyte[2], lpt_iobase + 2);
+}
+/* compute the next highest power of 2 */ +int round_powerof2(unsigned int v) +{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
+}
+static void willem_set_vcc(int enable) +{
lpt_outbyte[2] &= ~(1 << 2); /* INIT */
if (enable)
lpt_outbyte[2] |= (1 << 2);
OUTB(lpt_outbyte[2], lpt_iobase + 2);
+}
+static void willem_chip_set_address(const struct flashctx *flash, chipaddr addr) +{
/* speedup: don't set all 24 address bits, just the relevant ones for the current chip */
int chip_bits = ffs(round_powerof2(flash->chip->total_size * 1024)) - 1;
if (flash->chip->bustype == BUS_LPC || flash->chip->bustype == BUS_FWH) {
/* R/C# high */
willem_set_vcc(1);
willem_set_address(addr & 0x7ff, 11); /* lower 11 bits (row address) */
/* R/C# low */
willem_set_vcc(0);
/* VCC switching is slow so we need a delay */
programmer_delay(50); /* 15us is enough for PCB4.0 but use 50us to be safe */
willem_set_address((addr >> 11) & 0x7ff, chip_bits - 11); /* upper (upto) 11 bits (column address) */
/* R/C# high */
willem_set_vcc(1);
/* VCC switching is slow so we need a delay */
programmer_delay(50); /* 15us is enough for PCB4.0 but use 50us to be safe */
} else {
willem_set_address(addr, chip_bits);
}
+}
+static void willem_chip_writeb(const struct flashctx *flash, uint8_t val,
chipaddr addr)
+{
willem_chip_set_address(flash, addr);
willem_set_data(val);
willem_set_write(1);
willem_set_write(0);
+}
+static uint8_t willem_chip_readb(const struct flashctx *flash,
const chipaddr addr)
+{
willem_chip_set_address(flash, addr);
return willem_get_data();
+}
+#else +#error PCI port I/O access is not supported on this architecture yet. +#endif
-- Ondrej Zary
flashrom mailing list flashrom@flashrom.org http://www.flashrom.org/mailman/listinfo/flashrom