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 <[email protected]> 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 <[email protected]> > > 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 > + * 1. data output (through 2x4503 buffers) to flash D0..D7 > + * 2. 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 > [email protected] > http://www.flashrom.org/mailman/listinfo/flashrom _______________________________________________ flashrom mailing list [email protected] http://www.flashrom.org/mailman/listinfo/flashrom
