Signed-off-by: Urja Rannikko urjaman@gmail.com Signed-off-by: Stefan Tauner stefan.tauner@alumni.tuwien.ac.at --- Makefile | 23 ++- ch341a_spi.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ flashrom.8.tmpl | 12 +- flashrom.c | 12 ++ programmer.h | 13 ++ 5 files changed, 588 insertions(+), 3 deletions(-) create mode 100644 ch341a_spi.c
diff --git a/Makefile b/Makefile index 6862200..676bb66 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ UNSUPPORTED_FEATURES += CONFIG_PONY_SPI=yes else override CONFIG_PONY_SPI = no endif -# Dediprog, USB-Blaster, PICkit2 and FT2232 are not supported under DOS (missing USB support). +# Dediprog, USB-Blaster, PICkit2, CH341A and FT2232 are not supported under DOS (missing USB support). ifeq ($(CONFIG_DEDIPROG), yes) UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes else @@ -178,6 +178,11 @@ UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes else override CONFIG_PICKIT2_SPI = no endif +ifeq ($(CONFIG_CH341A_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes +else +override CONFIG_CH341A_SPI = no +endif endif
# FIXME: Should we check for Cygwin/MSVC as well? @@ -301,7 +306,7 @@ UNSUPPORTED_FEATURES += CONFIG_PONY_SPI=yes else override CONFIG_PONY_SPI = no endif -# Dediprog, USB-Blaster, PICkit2 and FT2232 are not supported with libpayload (missing libusb support) +# Dediprog, USB-Blaster, PICkit2, CH341A and FT2232 are not supported with libpayload (missing libusb support) ifeq ($(CONFIG_DEDIPROG), yes) UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes else @@ -322,6 +327,11 @@ UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes else override CONFIG_PICKIT2_SPI = no endif +ifeq ($(CONFIG_CH341A_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes +else +override CONFIG_CH341A_SPI = no +endif endif
ifneq ($(TARGET_OS), Linux) @@ -500,6 +510,9 @@ CONFIG_LINUX_SPI ?= yes # Always enable ITE IT8212F PATA controllers for now. CONFIG_IT8212 ?= yes
+# Winchiphead CH341A +CONFIG_CH341A_SPI ?= yes + # Disable wiki printing by default. It is only useful if you have wiki access. CONFIG_PRINT_WIKI ?= no
@@ -740,6 +753,12 @@ NEED_LINUX_I2C := yes PROGRAMMER_OBJS += mstarddc_spi.o endif
+ifeq ($(CONFIG_CH341A_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_CH341A_SPI=1' +PROGRAMMER_OBJS += ch341a_spi.o +NEED_LIBUSB1 := yes +endif + ifeq ($(NEED_SERIAL), yes) LIB_OBJS += serial.o endif diff --git a/ch341a_spi.c b/ch341a_spi.c new file mode 100644 index 0000000..36282ed --- /dev/null +++ b/ch341a_spi.c @@ -0,0 +1,531 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2011 asbokid ballymunboy@gmail.com + * Copyright (C) 2014 Pluto Yang yangyj.ee@gmail.com + * Copyright (C) 2015-2016 Stefan Tauner + * Copyright (C) 2015 Urja Rannikko urjaman@gmail.com + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#include <string.h> +#include <libusb.h> +#include "flash.h" +#include "programmer.h" + +/* LIBUSB_CALL ensures the right calling conventions on libusb callbacks. + * However, the macro is not defined everywhere. m( + */ +#ifndef LIBUSB_CALL +#define LIBUSB_CALL +#endif + +#define USB_TIMEOUT 1000 /* 1000 ms is plenty and we have no backup strategy anyway. */ +#define WRITE_EP 0x02 +#define READ_EP 0x82 + +#define CH341_PACKET_LENGTH 0x20 +#define CH341_MAX_PACKETS 256 +#define CH341_MAX_PACKET_LEN (CH341_PACKET_LENGTH * CH341_MAX_PACKETS) + +#define CH341A_CMD_SET_OUTPUT 0xA1 +#define CH341A_CMD_IO_ADDR 0xA2 +#define CH341A_CMD_PRINT_OUT 0xA3 +#define CH341A_CMD_SPI_STREAM 0xA8 +#define CH341A_CMD_SIO_STREAM 0xA9 +#define CH341A_CMD_I2C_STREAM 0xAA +#define CH341A_CMD_UIO_STREAM 0xAB + +#define CH341A_CMD_I2C_STM_START 0x74 +#define CH341A_CMD_I2C_STM_STOP 0x75 +#define CH341A_CMD_I2C_STM_OUT 0x80 +#define CH341A_CMD_I2C_STM_IN 0xC0 +#define CH341A_CMD_I2C_STM_MAX ( min( 0x3F, CH341_PACKET_LENGTH ) ) +#define CH341A_CMD_I2C_STM_SET 0x60 // bit 2: SPI with two data pairs D5,D4=out, D7,D6=in +#define CH341A_CMD_I2C_STM_US 0x40 +#define CH341A_CMD_I2C_STM_MS 0x50 +#define CH341A_CMD_I2C_STM_DLY 0x0F +#define CH341A_CMD_I2C_STM_END 0x00 + +#define CH341A_CMD_UIO_STM_IN 0x00 +#define CH341A_CMD_UIO_STM_DIR 0x40 +#define CH341A_CMD_UIO_STM_OUT 0x80 +#define CH341A_CMD_UIO_STM_US 0xC0 +#define CH341A_CMD_UIO_STM_END 0x20 + +#define CH341A_STM_I2C_20K 0x00 +#define CH341A_STM_I2C_100K 0x01 +#define CH341A_STM_I2C_400K 0x02 +#define CH341A_STM_I2C_750K 0x03 +#define CH341A_STM_SPI_DBL 0x04 + + +/* Number of parallel IN transfers. 32 seems to produce the most stable throughput on Windows. */ +#define USB_IN_TRANSFERS 32 + +/* We need to use many queued IN transfers for any resemblance of performance (especially on Windows) + * because USB spec says that transfers end on non-full packets and the device sends the 31 reply + * data bytes to each 32-byte packet with command + 31 bytes of data... */ +static struct libusb_transfer *transfer_out = NULL; +static struct libusb_transfer *transfer_ins[USB_IN_TRANSFERS] = {0}; + +/* Accumulate delays to be plucked between CS deassertion and CS assertions. */ +static unsigned int stored_delay_us = 0; + +static struct libusb_device_handle *handle = NULL; + +const struct dev_entry devs_ch341a_spi[] = { + {0x1A86, 0x5512, OK, "Winchiphead (WCH)", "CH341A"}, + + {0}, +}; + +enum trans_state {TRANS_ACTIVE = -2, TRANS_ERR = -1, TRANS_IDLE = 0}; + +static void print_hex(const void *buf, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) { + msg_pspew(" %02x", ((uint8_t *)buf)[i]); + if (i % CH341_PACKET_LENGTH == CH341_PACKET_LENGTH - 1) + msg_pspew("\n"); + } +} + +static void cb_common(const char *func, struct libusb_transfer *transfer) +{ + int *transfer_cnt = (int*)transfer->user_data; + + if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + /* Silently ACK and exit. */ + *transfer_cnt = TRANS_IDLE; + return; + } + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + msg_perr("\n%s: error: %s\n", func, libusb_error_name(transfer->status)); + *transfer_cnt = TRANS_ERR; + } else { + *transfer_cnt = transfer->actual_length; + } +} + +/* callback for bulk out async transfer */ +static void LIBUSB_CALL cb_out(struct libusb_transfer *transfer) +{ + cb_common(__func__, transfer); +} + +/* callback for bulk in async transfer */ +static void LIBUSB_CALL cb_in(struct libusb_transfer *transfer) +{ + cb_common(__func__, transfer); +} + +static int32_t usb_transfer(const char *func, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) +{ + if (handle == NULL) + return -1; + + int state_out = TRANS_IDLE; + transfer_out->buffer = (uint8_t*)writearr; + transfer_out->length = writecnt; + transfer_out->user_data = &state_out; + + /* Schedule write first */ + if (writecnt > 0) { + state_out = TRANS_ACTIVE; + int ret = libusb_submit_transfer(transfer_out); + if (ret) { + msg_perr("%s: failed to submit OUT transfer: %s\n", func, libusb_error_name(ret)); + state_out = TRANS_ERR; + goto err; + } + } + + /* Handle all asynchronous packets as long as we have stuff to write or read. The write(s) simply need + * to complete but we need to scheduling reads as long as we are not done. */ + unsigned int free_idx = 0; /* The IN transfer we expect to be free next. */ + unsigned int in_idx = 0; /* The IN transfer we expect to be completed next. */ + unsigned int in_done = 0; + unsigned int in_active = 0; + unsigned int out_done = 0; + int state_in[USB_IN_TRANSFERS] = {0}; + do { + /* Schedule new reads as long as there are free transfers and unscheduled bytes to read. */ + while ((in_done + in_active) < readcnt && state_in[free_idx] == TRANS_IDLE) { + unsigned int cur_todo = min(CH341_PACKET_LENGTH - 1, readcnt - in_done - in_active); + transfer_ins[free_idx]->length = cur_todo; + transfer_ins[free_idx]->buffer = readarr; + transfer_ins[free_idx]->user_data = &state_in[free_idx]; + int ret = libusb_submit_transfer(transfer_ins[free_idx]); + if (ret) { + state_in[free_idx] = TRANS_ERR; + msg_perr("%s: failed to submit IN transfer: %s\n", + func, libusb_error_name(ret)); + goto err; + } + readarr += cur_todo; + in_active += cur_todo; + state_in[free_idx] = TRANS_ACTIVE; + free_idx = (free_idx + 1) % USB_IN_TRANSFERS; /* Increment (and wrap around). */ + } + + /* Actually get some work done. */ + libusb_handle_events_timeout(NULL, &(struct timeval){1, 0}); + + /* Check for the write */ + if (out_done < writecnt) { + if (state_out == TRANS_ERR) { + goto err; + } else if (state_out > 0) { + out_done += state_out; + state_out = TRANS_IDLE; + } + } + /* Check for completed transfers. */ + while (state_in[in_idx] != TRANS_IDLE && state_in[in_idx] != TRANS_ACTIVE) { + if (state_in[in_idx] == TRANS_ERR) { + goto err; + } + /* If a transfer is done, record the number of bytes read and reuse it later. */ + in_done += state_in[in_idx]; + in_active -= state_in[in_idx]; + state_in[in_idx] = TRANS_IDLE; + in_idx = (in_idx + 1) % USB_IN_TRANSFERS; /* Increment (and wrap around). */ + } + } while ((out_done < writecnt) || (in_done < readcnt)); + + if (out_done > 0) { + msg_pspew("Wrote %d bytes:\n", out_done); + print_hex(writearr, out_done); + msg_pspew("\n\n"); + } + if (in_done > 0) { + msg_pspew("Read %d bytes:\n", in_done); + print_hex(readarr, in_done); + msg_pspew("\n\n"); + } + return 0; +err: + /* Clean up on errors. */ + msg_perr("%s: Failed to %s %d bytes\n", func, (state_out == TRANS_ERR) ? "write" : "read", + (state_out == TRANS_ERR) ? writecnt : readcnt); + /* First, we must cancel any ongoing requests and wait for them to be canceled. */ + if ((writecnt > 0) && (state_out == TRANS_ACTIVE)) { + if (libusb_cancel_transfer(transfer_out) != 0) + state_out = TRANS_ERR; + } + if (readcnt > 0) { + unsigned int i; + for (i = 0; i < USB_IN_TRANSFERS; i++) { + if (state_in[i] == TRANS_ACTIVE) + if (libusb_cancel_transfer(transfer_ins[i]) != 0) + state_in[i] = TRANS_ERR; + } + } + + /* Wait for cancellations to complete. */ + while (1) { + bool finished = true; + if ((writecnt > 0) && (state_out == TRANS_ACTIVE)) + finished = false; + if (readcnt > 0) { + unsigned int i; + for (i = 0; i < USB_IN_TRANSFERS; i++) { + if (state_in[i] == TRANS_ACTIVE) + finished = false; + } + } + if (finished) + break; + libusb_handle_events_timeout(NULL, &(struct timeval){1, 0}); + } + return -1; +} + +/* Set the I2C bus speed (speed(b1b0): 0 = 20kHz; 1 = 100kHz, 2 = 400kHz, 3 = 750kHz). + * Set the SPI bus data width (speed(b2): 0 = Single, 1 = Double). */ +static int32_t config_stream(uint32_t speed) +{ + if (handle == NULL) + return -1; + + uint8_t buf[] = { + CH341A_CMD_I2C_STREAM, + CH341A_CMD_I2C_STM_SET | (speed & 0x7), + CH341A_CMD_I2C_STM_END + }; + + int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL); + if (ret < 0) { + msg_perr("Could not configure stream interface.\n"); + } + return ret; +} + +/* ch341 requires LSB first, swap the bit order before send and after receive */ +static uint8_t swap_byte(uint8_t x) +{ + x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa); + x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc); + x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0); + return x; +} + +/* The assumed map between UIO command bits, pins on CH341A chip and pins on SPI chip: + * UIO CH341A SPI CH341A SPI name + * 0 D0/15 CS/1 (CS0) + * 1 D1/16 unused (CS1) + * 2 D2/17 unused (CS2) + * 3 D3/18 SCK/6 (DCK) + * 4 D4/19 unused (DOUT2) + * 5 D5/20 SI/5 (DOUT) + * - The UIO stream commands seem to only have 6 bits of output, and D6/D7 are the SPI inputs, + * mapped as follows: + * D6/21 unused (DIN2) + * D7/22 SO/2 (DIN) + */ +static int32_t enable_pins(bool enable) +{ + uint8_t buf[] = { + CH341A_CMD_UIO_STREAM, + CH341A_CMD_UIO_STM_OUT | 0x37, // CS high (all of them), SCK=0, DOUT*=1 + CH341A_CMD_UIO_STM_DIR | (enable ? 0x3F : 0x00), // Interface output enable / disable + CH341A_CMD_UIO_STM_END, + }; + + int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL); + if (ret < 0) { + msg_perr("Could not %sable output pins.\n", enable ? "en" : "dis"); + } + return ret; +} + +/* De-assert and assert CS in one operation. */ +static void pluck_cs(uint8_t *ptr) +{ + /* This was measured to give a minumum deassertion time of 2.25 us, + * >20x more than needed for most SPI chips (100ns). */ + int delay_cnt = 2; + if (stored_delay_us) { + delay_cnt = (stored_delay_us * 4) / 3; + stored_delay_us = 0; + } + *ptr++ = CH341A_CMD_UIO_STREAM; + *ptr++ = CH341A_CMD_UIO_STM_OUT | 0x37; /* deasserted */ + int i; + for (i = 0; i < delay_cnt; i++) + *ptr++ = CH341A_CMD_UIO_STM_OUT | 0x37; /* "delay" */ + *ptr++ = CH341A_CMD_UIO_STM_OUT | 0x36; /* asserted */ + *ptr++ = CH341A_CMD_UIO_STM_END; +} + +void ch341a_spi_delay(unsigned int usecs) +{ + /* There is space for 28 bytes instructions of 750 ns each in the CS packet (32 - 4 for the actual CS + * instructions), thus max 21 us, but we avoid getting too near to this boundary and use + * internal_delay() for durations over 20 us. */ + if ((usecs + stored_delay_us) > 20) { + unsigned int inc = 20 - stored_delay_us; + internal_delay(usecs - inc); + usecs = inc; + } + stored_delay_us += usecs; +} + +static int ch341a_spi_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) +{ + if (handle == NULL) + return -1; + + /* How many packets ... */ + const size_t packets = (writecnt + readcnt + CH341_PACKET_LENGTH - 2) / (CH341_PACKET_LENGTH - 1); + + uint8_t wbuf[packets+1][CH341_PACKET_LENGTH]; + uint8_t rbuf[writecnt + readcnt]; + memset(wbuf[0], 0, CH341_PACKET_LENGTH); // dont want to write stack random to device... + + uint8_t *ptr = wbuf[0]; + /* CS usage is optimized by doing both transitions in one packet. + * Final transition to deselected state is in the pin disable. */ + pluck_cs(ptr); + unsigned int write_left = writecnt; + unsigned int read_left = readcnt; + unsigned int p; + for (p = 0; p < packets; p++) { + unsigned int write_now = min(CH341_PACKET_LENGTH - 1, write_left); + unsigned int read_now = min ((CH341_PACKET_LENGTH - 1) - write_now, read_left); + ptr = wbuf[p+1]; + *ptr++ = CH341A_CMD_SPI_STREAM; + unsigned int i; + for (i = 0; i < write_now; ++i) + *ptr++ = swap_byte(*writearr++); + if (read_now) { + memset(ptr, 0xFF, read_now); + read_left -= read_now; + } + write_left -= write_now; + } + + int32_t ret = usb_transfer(__func__, CH341_PACKET_LENGTH + packets + writecnt + readcnt, + writecnt + readcnt, wbuf[0], rbuf); + if (ret < 0) + return -1; + + unsigned int i; + for (i = 0; i < readcnt; i++) { + /* FIXME: does working on words instead of bytes improve speed? */ + *readarr++ = swap_byte(rbuf[writecnt + i]); + } + + return 0; +} + +static const struct spi_master spi_master_ch341a_spi = { + .type = SPI_CONTROLLER_CH341A_SPI, + /* TODO: flashrom's current maximum is 256 B. Device was tested on Linux to accept atleast 16 kB. */ + .max_data_read = 16 * 1024, + .max_data_write = 16 * 1024, + .command = ch341a_spi_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 int ch341a_spi_shutdown(void *data) +{ + if (handle == NULL) + return -1; + + enable_pins(false); + libusb_free_transfer(transfer_out); + transfer_out = NULL; + int i; + for (i = 0; i < USB_IN_TRANSFERS; i++) { + libusb_free_transfer(transfer_ins[i]); + transfer_ins[i] = NULL; + } + libusb_release_interface(handle, 0); + libusb_close(handle); + libusb_exit(NULL); + handle = NULL; + return 0; +} + +int ch341a_spi_init(void) +{ + if (handle != NULL) { + msg_cerr("%s: handle already set! Please report a bug at flashrom@flashrom.org\n", __func__); + return -1; + } + + int32_t ret = libusb_init(NULL); + if (ret < 0) { + msg_perr("Couldnt initialize libusb!\n"); + return -1; + } + + libusb_set_debug(NULL, 3); // Enable information, warning and error messages (only). + + uint16_t vid = devs_ch341a_spi[0].vendor_id; + uint16_t pid = devs_ch341a_spi[0].device_id; + handle = libusb_open_device_with_vid_pid(NULL, vid, pid); + if (handle == NULL) { + msg_perr("Couldn't open device %04x:%04x.\n", vid, pid); + return -1; + } + +/* libusb_detach_kernel_driver() and friends basically only work on Linux. We simply try to detach on Linux + * without a lot of passion here. If that works fine else we will fail on claiming the interface anyway. */ +#if IS_LINUX + ret = libusb_detach_kernel_driver(handle, 0); + if (ret == LIBUSB_ERROR_NOT_SUPPORTED) { + msg_pwarn("Detaching kernel drivers is not supported. Further accesses may fail.\n"); + } else if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND) { + msg_pwarn("Failed to detach kernel driver: '%s'. Further accesses will probably fail.\n", + libusb_error_name(ret)); + } +#endif + + ret = libusb_claim_interface(handle, 0); + if (ret != 0) { + msg_perr("Failed to claim interface 0: '%s'\n", libusb_error_name(ret)); + goto close_handle; + } + + struct libusb_device *dev; + if (!(dev = libusb_get_device(handle))) { + msg_perr("Failed to get device from device handle.\n"); + goto close_handle; + } + + struct libusb_device_descriptor desc; + ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) { + msg_perr("Failed to get device descriptor: '%s'\n", libusb_error_name(ret)); + goto release_interface; + } + + msg_pdbg("Device revision is %d.%01d.%01d\n", + (desc.bcdDevice >> 8) & 0x00FF, + (desc.bcdDevice >> 4) & 0x000F, + (desc.bcdDevice >> 0) & 0x000F); + + /* Allocate and pre-fill transfer structures. */ + transfer_out = libusb_alloc_transfer(0); + if (!transfer_out) { + msg_perr("Failed to alloc libusb OUT transfer\n"); + goto release_interface; + } + int i; + for (i = 0; i < USB_IN_TRANSFERS; i++) { + transfer_ins[i] = libusb_alloc_transfer(0); + if (transfer_ins[i] == NULL) { + msg_perr("Failed to alloc libusb IN transfer %d\n", i); + goto dealloc_transfers; + } + } + /* We use these helpers but dont fill the actual buffer yet. */ + libusb_fill_bulk_transfer(transfer_out, handle, WRITE_EP, NULL, 0, cb_out, NULL, USB_TIMEOUT); + for (i = 0; i < USB_IN_TRANSFERS; i++) + libusb_fill_bulk_transfer(transfer_ins[i], handle, READ_EP, NULL, 0, cb_in, NULL, USB_TIMEOUT); + + if ((config_stream(CH341A_STM_I2C_100K) < 0) || (enable_pins(true) < 0)) + goto dealloc_transfers; + + register_shutdown(ch341a_spi_shutdown, NULL); + register_spi_master(&spi_master_ch341a_spi); + + return 0; + +dealloc_transfers: + for (i = 0; i < USB_IN_TRANSFERS; i++) { + if (transfer_ins[i] == NULL) + break; + libusb_free_transfer(transfer_ins[i]); + transfer_ins[i] = NULL; + } + libusb_free_transfer(transfer_out); + transfer_out = NULL; +release_interface: + libusb_release_interface(handle, 0); +close_handle: + libusb_close(handle); + handle = NULL; + return -1; +} diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index 7c8c70c..0dd8322 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -269,6 +269,8 @@ bitbanging adapter) .sp .BR "* pickit2_spi" " (for SPI flash ROMs accessible via Microchip PICkit2)" .sp +.BR "* ch341a_spi" " (for SPI flash ROMs attached to WCH CH341A)" +.sp Some programmers have optional or mandatory parameters which are described in detail in the .B PROGRAMMER-SPECIFIC INFORMATION @@ -1013,6 +1015,10 @@ an operation), without the parameter, once the flash read/write operation you intended to perform has completed successfully. .sp Please also note that the mstarddc_spi driver only works on Linux. +.SS +.BR "ch341a_spi " programmer +The WCH CH341A programmer does not support any parameters currently. SPI frequency is fixed at 2 MHz, and CS0 is +used as per the device. .SH EXAMPLES To back up and update your BIOS, run .sp @@ -1072,6 +1078,9 @@ needs userspace access to a serial port. .BR dediprog ", " ft2232_spi ", " usbblaster_spi " and " pickit2_spi need access to the respective USB device via libusb-0.1. .sp +.BR ch341a_spi +needs access to the respective USB device via libusb-1.0. +.sp .B dummy needs no access permissions at all. .sp @@ -1079,7 +1088,8 @@ needs no access permissions at all. .BR gfxnvidia ", " drkaiser ", " satasii ", " satamv ", " atahpt" and " atavia have to be run as superuser/root, and need additional raw access permission. .sp -.BR serprog ", " buspirate_spi ", " dediprog ", " usbblaster_spi ", " ft2232_spi " and " pickit2_spi +.BR serprog ", " buspirate_spi ", " dediprog ", " usbblaster_spi ", " ft2232_spi ", " pickit2_spi " and " \ +ch341a_spi can be run as normal user on most operating systems if appropriate device permissions are set. .sp diff --git a/flashrom.c b/flashrom.c index 3853e19..0ff088f 100644 --- a/flashrom.c +++ b/flashrom.c @@ -380,6 +380,18 @@ const struct programmer_entry programmer_table[] = { }, #endif
+#if CONFIG_CH341A_SPI == 1 + { + .name = "ch341a_spi", + .type = USB, + .devs.dev = devs_ch341a_spi, + .init = ch341a_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = ch341a_spi_delay, + }, +#endif + {0}, /* This entry corresponds to PROGRAMMER_INVALID. */ };
diff --git a/programmer.h b/programmer.h index 97f0ffa..b9f8708 100644 --- a/programmer.h +++ b/programmer.h @@ -105,6 +105,9 @@ enum programmer { #if CONFIG_PICKIT2_SPI == 1 PROGRAMMER_PICKIT2_SPI, #endif +#if CONFIG_CH341A_SPI == 1 + PROGRAMMER_CH341A_SPI, +#endif PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -516,6 +519,13 @@ int linux_spi_init(void); int dediprog_init(void); #endif
+/* ch341a_spi.c */ +#if CONFIG_CH341A_SPI == 1 +int ch341a_spi_init(void); +void ch341a_spi_delay(unsigned int usecs); +extern const struct dev_entry devs_ch341a_spi[]; +#endif + /* flashrom.c */ struct decode_sizes { uint32_t parallel; @@ -575,6 +585,9 @@ enum spi_controller { #if CONFIG_PICKIT2_SPI == 1 SPI_CONTROLLER_PICKIT2, #endif +#if CONFIG_CH341A_SPI == 1 + SPI_CONTROLLER_CH341A_SPI, +#endif };
#define MAX_DATA_UNSPECIFIED 0