Hi,
Review-ish... with some only-commentary included too.
On Mon, Jan 18, 2016 at 1:28 AM, Stefan Tauner stefan.tauner@alumni.tuwien.ac.at wrote:
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). */
Yeah this obvious construct is something I didnt use because I'm such an embedded guy that I'd never ever use % in performance related code... (i think i had some if idx>=USB_IN_TRANSFERS) idx = 0 thing)... thanks for making it saner.
}
/* 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;
+}
I had a fancier delay implementation done (all the way upto ms scale using the I2C commands), but since i didnt get that fully tested, this one will do, i'll post it as an improvement later.
+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? */
... drop the comment? i dont really care, but i think our consensus on IRC was that a words-wide implementation would have more issues (alignment and such) than performance. Or make a comment with that info.
*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,
On linux. Not on windows usb "stack" i think... set these back to 1k? Or test for the limit (maybe something like 4k?) and change the 1000ms timeout, since i think the big packet would timeout on windows if nothing else failed...
.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
Kind regards, Stefan Tauner
With atleast the limits dropped (or tested), this is: Acked-by: Urja Rannikko urjaman@gmail.com