diff --git a/Makefile b/Makefile index cd729724b..ddfd71169 100644 --- a/Makefile +++ b/Makefile @@ -154,12 +154,17 @@ UNSUPPORTED_FEATURES += CONFIG_PONY_SPI=yes else override CONFIG_PONY_SPI = no endif -# Dediprog, USB-Blaster, PICkit2, CH341A and FT2232 are not supported under DOS (missing USB support). +# Dediprog, Developerbox, USB-Blaster, PICkit2, CH341A and FT2232 are not supported under DOS (missing USB support). ifeq ($(CONFIG_DEDIPROG), yes) UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes else override CONFIG_DEDIPROG = no endif +ifeq ($(CONFIG_DEVELOPERBOX_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_DEVELOPERBOX_SPI=yes +else +override CONFIG_DEVELOPERBOX_SPI = no +endif ifeq ($(CONFIG_FT2232_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes else @@ -311,12 +316,17 @@ UNSUPPORTED_FEATURES += CONFIG_PONY_SPI=yes else override CONFIG_PONY_SPI = no endif -# Dediprog, USB-Blaster, PICkit2, CH341A and FT2232 are not supported with libpayload (missing libusb support). +# Dediprog, Developerbox, USB-Blaster, PICkit2, CH341A and FT2232 are not supported with libpayload (missing libusb support). ifeq ($(CONFIG_DEDIPROG), yes) UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes else override CONFIG_DEDIPROG = no endif +ifeq ($(CONFIG_DEVELOPERBOX_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_DEVELOPERBOX_SPI=yes +else +override CONFIG_DEVELOPERBOX_SPI = no +endif ifeq ($(CONFIG_FT2232_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes else @@ -627,6 +637,9 @@ CONFIG_BUSPIRATE_SPI ?= yes # Always enable Dediprog SF100 for now. CONFIG_DEDIPROG ?= yes +# Always enable Developerbox emergency recovery for now. +CONFIG_DEVELOPERBOX_SPI ?= yes + # Always enable Marvell SATA controllers for now. CONFIG_SATAMV ?= yes @@ -671,6 +684,7 @@ ifeq ($(CONFIG_ENABLE_LIBUSB1_PROGRAMMERS), no) override CONFIG_CH341A_SPI = no override CONFIG_DEDIPROG = no override CONFIG_DIGILENT_SPI = no +override CONFIG_DEVELOPERBOX_SPI = no endif ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no) override CONFIG_INTERNAL = no @@ -907,6 +921,12 @@ PROGRAMMER_OBJS += dediprog.o NEED_LIBUSB1 += CONFIG_DEDIPROG endif +ifeq ($(CONFIG_DEVELOPERBOX_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_DEVELOPERBOX_SPI=1' +PROGRAMMER_OBJS += developerbox_spi.o +NEED_LIBUSB1 += CONFIG_DEVELOPERBOX_SPI +endif + ifeq ($(CONFIG_SATAMV), yes) FEATURE_CFLAGS += -D'CONFIG_SATAMV=1' PROGRAMMER_OBJS += satamv.o diff --git a/developerbox_spi.c b/developerbox_spi.c new file mode 100644 index 000000000..8482dea17 --- /dev/null +++ b/developerbox_spi.c @@ -0,0 +1,232 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2018 Linaro Limited + * + * 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. + */ + +/* + * Bit bang driver for the 96Boards Developerbox (a.k.a. Synquacer E-series) + * on-board debug UART. The Developerbox implements its debug UART using a + * CP2102N, a USB to UART bridge which also provides four GPIO pins. On + * Developerbox these can be hooked up to the onboard SPI NOR FLASH and used + * for emergency de-brick without any additional hardware programmer. Bit + * banging over USB is extremely slow compared to a proper SPI programmer so + * this is only practical as a de-brick tool. + * + * Schematic is available here: + * https://www.96boards.org/documentation/enterprise/developerbox/hardware-docs/ + * + * To prepare a Developerbox for programming via the debug UART, DSW4 must be + * changed from the default 00000000 to 10001000 (i.e. DSW4-1 and DSW4-5 + * should be turned on). + */ + +#include "platform.h" + +#include +#include +#include +#include "programmer.h" +#include "spi.h" + +/* Bit positions for each pin. */ +#define DEVELOPERBOX_SPI_SCK 0 +#define DEVELOPERBOX_SPI_CS 1 +#define DEVELOPERBOX_SPI_MISO 2 +#define DEVELOPERBOX_SPI_MOSI 3 + +/* Config request types */ +#define REQTYPE_HOST_TO_DEVICE 0x40 +#define REQTYPE_DEVICE_TO_HOST 0xc0 + +/* Config request codes */ +#define CP210X_VENDOR_SPECIFIC 0xff + +/* CP210X_VENDOR_SPECIFIC */ +#define CP210X_WRITE_LATCH 0x37e1 +#define CP210X_READ_LATCH 0x00c2 + +const struct dev_entry devs_developerbox_spi[] = { + {0x10c4, 0xea60, OK, "Silicon Labs", "CP2102N USB to UART Bridge Controller"}, + {0}, +}; + +struct libusb_context *usb_ctx; +static libusb_device_handle *cp210x_handle; + +static int cp210x_gpio_get(void) +{ + int res; + uint8_t gpio; + + res = libusb_control_transfer(cp210x_handle, REQTYPE_DEVICE_TO_HOST, + CP210X_VENDOR_SPECIFIC, CP210X_READ_LATCH, + 0, &gpio, 1, 0); + if (res < 0) { + msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res)); + return 0; + } + + return gpio; +} + +static void cp210x_gpio_set(uint8_t val, uint8_t mask) +{ + int res; + uint16_t gpio; + + gpio = ((val & 0xf) << 8) | (mask & 0xf); + + /* Set relay state on the card */ + res = libusb_control_transfer(cp210x_handle, REQTYPE_HOST_TO_DEVICE, + CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH, + gpio, NULL, 0, 0); + if (res < 0) + msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res)); +} + +static void cp210x_bitbang_set_cs(int val) +{ + cp210x_gpio_set(val << DEVELOPERBOX_SPI_CS, 1 << DEVELOPERBOX_SPI_CS); +} + +static void cp210x_bitbang_set_sck(int val) +{ + cp210x_gpio_set(val << DEVELOPERBOX_SPI_SCK, 1 << DEVELOPERBOX_SPI_SCK); +} + +static void cp210x_bitbang_set_mosi(int val) +{ + cp210x_gpio_set(val << DEVELOPERBOX_SPI_MOSI, 1 << DEVELOPERBOX_SPI_MOSI); +} + +static int cp210x_bitbang_get_miso(void) +{ + return !!(cp210x_gpio_get() & (1 << DEVELOPERBOX_SPI_MISO)); +} + +static void cp210x_bitbang_set_sck_set_mosi(int sck, int mosi) +{ + cp210x_gpio_set(sck << DEVELOPERBOX_SPI_SCK | mosi << DEVELOPERBOX_SPI_MOSI, + 1 << DEVELOPERBOX_SPI_SCK | 1 << DEVELOPERBOX_SPI_MOSI); +} + +static const struct bitbang_spi_master bitbang_spi_master_cp210x = { + .type = BITBANG_SPI_MASTER_DEVELOPERBOX, + .set_cs = cp210x_bitbang_set_cs, + .set_sck = cp210x_bitbang_set_sck, + .set_mosi = cp210x_bitbang_set_mosi, + .get_miso = cp210x_bitbang_get_miso, + .set_sck_set_mosi = cp210x_bitbang_set_sck_set_mosi, +}; + +static struct libusb_device_handle *get_device_by_vid_pid_serial(uint16_t vid, uint16_t pid, + const char *serialno) +{ + struct libusb_device **list; + ssize_t count = libusb_get_device_list(usb_ctx, &list); + if (count < 0) { + msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count)); + return NULL; + } + + ssize_t i = 0; + for (i = 0; i < count; i++) { + struct libusb_device *dev = list[i]; + struct libusb_device_descriptor desc; + struct libusb_device_handle *handle; + + int res = libusb_get_device_descriptor(dev, &desc); + if (res != 0) { + msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(res)); + continue; + } + + if ((desc.idVendor != vid) && (desc.idProduct != pid)) + continue; + + msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n", + desc.idVendor, desc.idProduct, + libusb_get_bus_number(dev), libusb_get_device_address(dev)); + + res = libusb_open(dev, &handle); + if (res != 0) { + msg_perr("Opening the USB device failed (%s)!\n", libusb_error_name(res)); + continue; + } + + if (serialno) { + unsigned char myserial[64]; + res = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, myserial, + sizeof(myserial)); + if (res < 0) { + msg_perr("Reading the USB serialno failed (%s)!\n", libusb_error_name(res)); + libusb_close(handle); + continue; + } + msg_pdbg("Serial number is %s\n", myserial); + + /* Filter out any serial number that does not commence with serialno */ + if (0 != strncmp(serialno, (char *) myserial, strlen(serialno))) { + libusb_close(handle); + continue; + } + } + + libusb_free_device_list(list, 1); + return handle; + } + + libusb_free_device_list(list, 1); + return NULL; +} + +static int developerbox_spi_shutdown(void *data) +{ + libusb_close(cp210x_handle); + libusb_exit(usb_ctx); + + return 0; +} + +int developerbox_spi_init(void) +{ + libusb_init(&usb_ctx); + if (!usb_ctx) { + msg_perr("Could not initialize libusb!\n"); + return 1; + } + + char *serialno = extract_programmer_param("serial"); + if (serialno) + msg_pdbg("Looking for serial number commencing %s\n", serialno); + cp210x_handle = get_device_by_vid_pid_serial( + devs_developerbox_spi[0].vendor_id, devs_developerbox_spi[0].device_id, serialno); + free(serialno); + if (!cp210x_handle) { + msg_perr("Could not find a Developerbox programmer on USB.\n"); + goto err_exit; + } + + if (register_shutdown(developerbox_spi_shutdown, NULL)) + goto err_exit; + + if (register_spi_bitbang_master(&bitbang_spi_master_cp210x)) + goto err_exit; + + return 0; + +err_exit: + libusb_exit(usb_ctx); + return 1; +} diff --git a/flashrom.c b/flashrom.c index 1866a18ae..094630ccf 100644 --- a/flashrom.c +++ b/flashrom.c @@ -255,6 +255,18 @@ const struct programmer_entry programmer_table[] = { }, #endif +#if CONFIG_DEVELOPERBOX_SPI == 1 + { + .name = "developerbox", + .type = USB, + .devs.dev = devs_developerbox_spi, + .init = developerbox_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + #if CONFIG_RAYER_SPI == 1 { .name = "rayer_spi", diff --git a/programmer.h b/programmer.h index 7e530b632..300cf5f16 100644 --- a/programmer.h +++ b/programmer.h @@ -73,6 +73,9 @@ enum programmer { #if CONFIG_DEDIPROG == 1 PROGRAMMER_DEDIPROG, #endif +#if CONFIG_DEVELOPERBOX_SPI == 1 + PROGRAMMER_DEVELOPERBOX_SPI, +#endif #if CONFIG_RAYER_SPI == 1 PROGRAMMER_RAYER_SPI, #endif @@ -172,6 +175,9 @@ enum bitbang_spi_master_type { #if CONFIG_OGP_SPI == 1 BITBANG_SPI_MASTER_OGP, #endif +#if CONFIG_DEVELOPERBOX_SPI == 1 + BITBANG_SPI_MASTER_DEVELOPERBOX, +#endif }; struct bitbang_spi_master { @@ -548,6 +554,12 @@ int dediprog_init(void); extern const struct dev_entry devs_dediprog[]; #endif +/* developerbox_spi.c */ +#if CONFIG_DEVELOPERBOX_SPI == 1 +int developerbox_spi_init(void); +extern const struct dev_entry devs_developerbox_spi[]; +#endif + /* ch341a_spi.c */ #if CONFIG_CH341A_SPI == 1 int ch341a_spi_init(void);