Index: Makefile =================================================================== --- Makefile (revision 1817) +++ Makefile (working copy) @@ -137,7 +137,7 @@ else override CONFIG_PONY_SPI = no endif -# Dediprog and FT2232 are not supported under DOS (missing USB support). +# Dediprog, FT2232 and PICkit2 are not supported under DOS (missing USB support). ifeq ($(CONFIG_DEDIPROG), yes) UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes else @@ -153,7 +153,12 @@ else override CONFIG_USBBLASTER_SPI = no endif +ifeq ($(CONFIG_PICKIT2_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes +else +override CONFIG_PICKIT2_SPI = no endif +endif # FIXME: Should we check for Cygwin/MSVC as well? ifeq ($(TARGET_OS), MinGW) @@ -271,7 +276,7 @@ else override CONFIG_PONY_SPI = no endif -# Dediprog and FT2232 are not supported with libpayload (missing libusb support) +# Dediprog, FT2232 and PICkit2 are not supported with libpayload (missing libusb support) ifeq ($(CONFIG_DEDIPROG), yes) UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes else @@ -287,7 +292,12 @@ else override CONFIG_USBBLASTER_SPI = no endif +ifeq ($(CONFIG_PICKIT2_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes +else +override CONFIG_PICKIT2_SPI = no endif +endif ifneq ($(TARGET_OS), Linux) ifeq ($(CONFIG_LINUX_SPI), yes) @@ -408,6 +418,9 @@ # Always enable Altera USB-Blaster dongles for now. CONFIG_USBBLASTER_SPI ?= yes +# Always enable PICkit2 SPI dongles for now. +CONFIG_PICKIT2_SPI ?= yes + # Always enable dummy tracing for now. CONFIG_DUMMY ?= yes @@ -583,6 +596,12 @@ PROGRAMMER_OBJS += usbblaster_spi.o endif +ifeq ($(CONFIG_PICKIT2_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_PICKIT2_SPI=1' +PROGRAMMER_OBJS += pickit2_spi.o +NEED_USB := yes +endif + ifeq ($(NEED_FTDI), yes) FTDILIBS := $(shell pkg-config --libs libftdi 2>/dev/null || printf "%s" "-lftdi -lusb") FEATURE_CFLAGS += $(shell LC_ALL=C grep -q "FT232H := yes" .features && printf "%s" "-D'HAVE_FT232H=1'") Index: flashrom.8.tmpl =================================================================== --- flashrom.8.tmpl (revision 1817) +++ flashrom.8.tmpl (working copy) @@ -225,6 +225,8 @@ .sp .BR "* usbblaster_spi" " (for SPI flash ROMs attached to an Altera USB-Blaster compatible cable)" .sp +.BR "* pickit2_spi" " (for SPI flash ROMs accessible via Microchip PICkit2)" +.sp Some programmers have optional or mandatory parameters which are described in detail in the .B PROGRAMMER SPECIFIC INFO @@ -724,6 +726,34 @@ .BR "http://dangerousprototypes.com/docs/Practical_guide_to_Bus_Pirate_pull-up_resistors " . Only the external supply voltage (Vpu) is supported as of this writing. .SS +.BR "pickit2_spi " programmer +.B voltage +parameter specifies the voltage the PICkit2 should use. The default unit is +Volt if no unit is specified. You can use +.BR mV ", " milliVolt ", " V " or " Volt +as unit specifier. Syntax is +.sp +.B " flashrom \-p pickit2_spi:voltage=value" +.sp +where +.B value +can be +.BR 0V ", " 1.8V ", " 2.5V ", " 3.5V +or the equivalent in mV. +.sp +An optional +.B spispeed +parameter specifies the frequency of the SPI bus. +Syntax is +.sp +.B " flashrom \-p pickit2_spi:spispeed=frequency" +.sp +where +.B frequency +can be +.BR 250k ", " 333k ", " 500k " or " 1M " +(in Hz). The default is a frequency of 1 MHz. +.SS .BR "dediprog " programmer An optional .B voltage @@ -935,7 +965,7 @@ .B buspirate_spi needs userspace access to a serial port. .sp -.BR dediprog ", " ft2232_spi " and " usbblaster_spi +.BR dediprog ", " ft2232_spi " and " usbblaster_spi and " pickit2_spi need access to the USB device via libusb. .sp .B dummy @@ -945,7 +975,7 @@ .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 " and " ft2232_spi +.BR serprog ", " buspirate_spi ", " dediprog ", " usbblaster_spi " and " ft2232_spi and " pickit2_spi can be run as normal user on most operating systems if appropriate device permissions are set. .sp Index: flashrom.c =================================================================== --- flashrom.c (revision 1817) +++ flashrom.c (working copy) @@ -345,6 +345,19 @@ }, #endif +#if CONFIG_PICKIT2_SPI == 1 + { + .name = "pickit2_spi", + .type = OTHER, + /* FIXME */ + .devs.note = "Microchip PICkit2\n", + .init = pickit2_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + {0}, /* This entry corresponds to PROGRAMMER_INVALID. */ }; Index: pickit2_spi.c =================================================================== --- pickit2_spi.c (revision 0) +++ pickit2_spi.c (working copy) @@ -0,0 +1,527 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2010 Carl-Daniel Hailfinger + * Copyright (C) 2014 Justin Chevrier + * + * 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 + */ + +/* + * Connections are as follows: + * + * +------+-----+----------+ + * | SPI | Pin | PICkit2 | + * +------+-----+----------+ + * | /CS | 1 | VPP/MCLR | + * | VCC | 2 | VDD | + * | GND | 3 | GND | + * | MISO | 4 | PGD | + * | SCLK | 5 | PDC | + * | MOSI | 6 | AUX | + * +------+-----+----------+ + * + * Inspiration and some specifics of the interface came via the AVRDude + * PICkit2 code: https://github.com/steve-m/avrdude/blob/master/pickit2.c + */ + +#include +#include +#include +#include +#include +#include "flash.h" +#include "chipdrivers.h" +#include "programmer.h" +#include "spi.h" + +static usb_dev_handle *pickit2_handle; + +/* Default USB transaction timeout in ms */ +#define DEFAULT_TIMEOUT 10000 + +#define CMD_LENGTH 64 +#define ENDPOINT_OUT 0x01 +#define ENDPOINT_IN 0x81 + +#define PICKIT2_VID 0x04D8 +#define PICKIT2_PID 0x0033 + +#define CMD_GET_VERSION 0x76 +#define CMD_SET_VDD 0xA0 +#define CMD_SET_VPP 0xA1 +#define CMD_READ_VDD_VPP 0xA3 +#define CMD_EXEC_SCRIPT 0xA6 +#define CMD_CLR_DLOAD_BUFF 0xA7 +#define CMD_DOWNLOAD_DATA 0xA8 +#define CMD_CLR_ULOAD_BUFF 0xA9 +#define CMD_UPLOAD_DATA 0xAA +#define CMD_END_OF_BUFFER 0xAD + +#define SCR_SPI_READ_BUF 0xC5 +#define SCR_SPI_WRITE_BUF 0xC6 +#define SCR_SET_AUX 0xCF +#define SCR_LOOP 0xE9 +#define SCR_SET_ICSP_CLK_PERIOD 0xEA +#define SCR_SET_PINS 0xF3 +#define SCR_BUSY_LED_OFF 0xF4 +#define SCR_BUSY_LED_ON 0xF5 +#define SCR_MCLR_GND_OFF 0xF6 +#define SCR_MCLR_GND_ON 0xF7 +#define SCR_VPP_PWM_OFF 0xF8 +#define SCR_VPP_PWM_ON 0xF9 +#define SCR_VPP_OFF 0xFA +#define SCR_VPP_ON 0xFB +#define SCR_VDD_OFF 0xFE +#define SCR_VDD_ON 0xFF + +/* Copied from dediprog.c */ +/* Might be useful for other USB devices as well. static for now. */ +/* device parameter allows user to specify one device of multiple installed */ +static struct usb_device *get_device_by_vid_pid(uint16_t vid, uint16_t pid, unsigned int device) +{ + struct usb_bus *bus; + struct usb_device *dev; + + for (bus = usb_get_busses(); bus; bus = bus->next) + for (dev = bus->devices; dev; dev = dev->next) + if ((dev->descriptor.idVendor == vid) && + (dev->descriptor.idProduct == pid)) { + if (device == 0) + return dev; + device--; + } + + return NULL; +} + +static int pickit2_get_firmware_version(void) +{ + int ret; + uint8_t command[CMD_LENGTH] = {CMD_GET_VERSION, CMD_END_OF_BUFFER}; + + ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DEFAULT_TIMEOUT); + ret = usb_interrupt_read(pickit2_handle, ENDPOINT_IN, (char *)command, CMD_LENGTH, DEFAULT_TIMEOUT); + + msg_pdbg("PICkit2 Firmware Version: %d.%d\n", (int)command[0], (int)command[1]); + if (ret != CMD_LENGTH) { + msg_perr("Command Get Firmware Version failed (%s)!\n", + usb_strerror()); + return 1; + } + + return 0; +} + +static int pickit2_set_spi_voltage(int millivolt) +{ + int ret; + double voltage_selector; + + switch (millivolt) { + case 0: + /* Admittedly this one is an assumption. */ + voltage_selector = 0; + break; + case 1800: + voltage_selector = 1.8; + break; + case 2500: + voltage_selector = 2.5; + break; + case 3500: + voltage_selector = 3.5; + break; + default: + msg_perr("Unknown voltage %i mV! Aborting.\n", millivolt); + return 1; + } + msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000, + millivolt % 1000); + + uint8_t command[CMD_LENGTH] = { + CMD_SET_VDD, + voltage_selector * 2048 + 672, + (voltage_selector * 2048 + 672) / 256, + voltage_selector * 36, + CMD_SET_VPP, + 0x40, + voltage_selector * 18.61, + voltage_selector * 13, + CMD_END_OF_BUFFER + }; + + ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DEFAULT_TIMEOUT); + + if (ret != CMD_LENGTH) { + msg_perr("Command Set Voltage failed (%s)!\n", + usb_strerror()); + return 1; + } + + return 0; +} + + +struct pickit2_spispeeds { + const char *const name; + const int speed; +}; + +static const struct pickit2_spispeeds spispeeds[] = { + { "1M", 0x1 }, + { "500k", 0x2 }, + { "333k", 0x3 }, + { "250k", 0x4 }, + { NULL, 0x0 }, +}; + +static int pickit2_set_spi_speed(unsigned int spispeed_idx) +{ + int ret; + + msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed_idx].name); + + uint8_t command[CMD_LENGTH] = { + CMD_EXEC_SCRIPT, + 2, + SCR_SET_ICSP_CLK_PERIOD, + spispeed_idx, + CMD_END_OF_BUFFER + }; + + ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DEFAULT_TIMEOUT); + + if (ret != CMD_LENGTH) { + msg_perr("Command Set SPI Speed failed (%s)!\n", + usb_strerror()); + return 1; + } + + return 0; +} + +static int pickit2_spi_send_command(struct flashctx *flash, + unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + int ret, i; + + uint8_t buf[CMD_LENGTH] = {CMD_DOWNLOAD_DATA, writecnt}; + + /* Maximum number of bytes per transaction (including command overhead) is 64. Lets play it safe + * and always assume the worst case scenario of 20 bytes command overhead. + */ + msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt); + if (writecnt + readcnt + 20 > CMD_LENGTH) { + msg_perr("\nTotal packetsize (%i) is greater than 64 supported, aborting.\n", writecnt + readcnt + 20); + return 1; + } + + for (i = 2; i < writecnt + 2; i++) { + buf[i] = writearr[i - 2]; + } + + buf[i++] = CMD_CLR_ULOAD_BUFF; + buf[i++] = CMD_EXEC_SCRIPT; + + /* Determine script length based on number of bytes to be read or written */ + if (writecnt == 1 && readcnt == 1) + buf[i++] = 7; + else if (writecnt == 1 || readcnt == 1) + buf[i++] = 10; + else + buf[i++] = 13; + + /* Assert CS# */ + buf[i++] = SCR_VPP_OFF; + buf[i++] = SCR_MCLR_GND_ON; + + buf[i++] = SCR_SPI_WRITE_BUF; + + if (writecnt > 1) { + buf[i++] = SCR_LOOP; + buf[i++] = 1; /* Loop back one instruction */ + buf[i++] = writecnt - 1; /* Number of times to loop */ + } + + if (readcnt) + buf[i++] = SCR_SPI_READ_BUF; + + if (readcnt > 1) { + buf[i++] = SCR_LOOP; + buf[i++] = 1; /* Loop back one instruction */ + buf[i++] = readcnt - 1; /* Number of times to loop */ + } + + /* De-assert CS# */ + buf[i++] = SCR_MCLR_GND_OFF; + buf[i++] = SCR_VPP_PWM_ON; + buf[i++] = SCR_VPP_ON; + + buf[i++] = CMD_UPLOAD_DATA; + buf[i++] = CMD_END_OF_BUFFER; + + ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)buf, CMD_LENGTH, DEFAULT_TIMEOUT); + + if (ret != CMD_LENGTH) { + msg_perr("Send SPI failed, expected %i, got %i %s!\n", + writecnt, ret, usb_strerror()); + return 1; + } + + if (readcnt) { + ret = usb_interrupt_read(pickit2_handle, ENDPOINT_IN, (char *)buf, CMD_LENGTH, DEFAULT_TIMEOUT); + + if (ret != CMD_LENGTH) { + msg_perr("Receive SPI failed, expected %i, got %i %s!\n", + readcnt, ret, usb_strerror()); + return 1; + } + + /* First byte indicates number of bytes transferred from upload buffer */ + if (buf[0] != readcnt) { + msg_perr("Unexpected number of bytes transferred, expected %i, got %i!\n", + readcnt, ret); + return 1; + } + + /* Actual data starts at byte number two */ + memcpy(readarr, &buf[1], readcnt); + } + + return 0; +} + +/* Copied from dediprog.c */ +/* Might be useful for other USB devices as well. static for now. */ +static int parse_voltage(char *voltage) +{ + char *tmp = NULL; + int i; + int millivolt = 0, fraction = 0; + + if (!voltage || !strlen(voltage)) { + msg_perr("Empty voltage= specified.\n"); + return -1; + } + millivolt = (int)strtol(voltage, &tmp, 0); + voltage = tmp; + /* Handle "," and "." as decimal point. Everything after it is assumed + * to be in decimal notation. + */ + if ((*voltage == '.') || (*voltage == ',')) { + voltage++; + for (i = 0; i < 3; i++) { + fraction *= 10; + /* Don't advance if the current character is invalid, + * but continue multiplying. + */ + if ((*voltage < '0') || (*voltage > '9')) + continue; + fraction += *voltage - '0'; + voltage++; + } + /* Throw away remaining digits. */ + voltage += strspn(voltage, "0123456789"); + } + /* The remaining string must be empty or "mV" or "V". */ + tolower_string(voltage); + + /* No unit or "V". */ + if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) { + millivolt *= 1000; + millivolt += fraction; + } else if (!strncmp(voltage, "mv", 2) || + !strncmp(voltage, "milliv", 6)) { + /* No adjustment. fraction is discarded. */ + } else { + /* Garbage at the end of the string. */ + msg_perr("Garbage voltage= specified.\n"); + return -1; + } + return millivolt; +} + +static const struct spi_programmer spi_programmer_pickit2 = { + .type = SPI_CONTROLLER_PICKIT2, + .max_data_read = 40, + .max_data_write = 40, + .command = pickit2_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 pickit2_shutdown(void *data) +{ + int ret; + msg_pspew("%s\n", __func__); + + /* Set all pins to float and turn voltages off */ + uint8_t command[CMD_LENGTH] = { + CMD_EXEC_SCRIPT, + 8, + SCR_SET_PINS, + /* Bit-0=1(PDC In), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */ + 3, + SCR_SET_AUX, + /* Bit-0=1(Aux In), Bit-1=0(Aux LL) */ + 1, + SCR_MCLR_GND_OFF, + SCR_VPP_OFF, + SCR_VDD_OFF, + SCR_BUSY_LED_OFF, + CMD_END_OF_BUFFER + }; + + ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DEFAULT_TIMEOUT); + + if (ret != CMD_LENGTH) { + msg_perr("Command Shutdown failed (%s)!\n", + usb_strerror()); + return 1; + } + + if (usb_release_interface(pickit2_handle, 0)) { + msg_perr("Could not release USB interface!\n"); + return 1; + } + if (usb_close(pickit2_handle)) { + msg_perr("Could not close USB device!\n"); + return 1; + } + return 0; +} + +int pickit2_spi_init(void) +{ + struct usb_device *dev; + char *voltage, *spispeed; + int spispeed_idx = 0; + int millivolt = 3500; + long usedevice = 0; + int i, ret; + + uint8_t buf[CMD_LENGTH] = { + CMD_EXEC_SCRIPT, + 10, /* Script length */ + SCR_SET_PINS, + /* Bit-0=0(PDC Out), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */ + 2, + SCR_SET_AUX, + /* Bit-0=0(Aux Out), Bit-1=0(Aux LL) */ + 0, + SCR_VDD_ON, + SCR_MCLR_GND_OFF, /* Let CS# float */ + SCR_VPP_PWM_ON, + SCR_VPP_ON, /* Pull CS# high */ + SCR_BUSY_LED_ON, + CMD_CLR_DLOAD_BUFF, + CMD_CLR_ULOAD_BUFF, + CMD_END_OF_BUFFER + }; + + msg_pspew("%s\n", __func__); + + spispeed = extract_programmer_param("spispeed"); + if (spispeed) { + for (i = 0; spispeeds[i].name; ++i) { + if (!strcasecmp(spispeeds[i].name, spispeed)) { + spispeed_idx = i; + break; + } + } + if (!spispeeds[i].name) { + msg_perr("Error: Invalid 'spispeed' value.\n"); + free(spispeed); + return 1; + } + free(spispeed); + } + voltage = extract_programmer_param("voltage"); + if (voltage) { + millivolt = parse_voltage(voltage); + free(voltage); + if (millivolt < 0) + return 1; + msg_pinfo("Setting voltage to %i mV\n", millivolt); + } + + /* Here comes the USB stuff */ + usb_init(); + usb_find_busses(); + usb_find_devices(); + dev = get_device_by_vid_pid(PICKIT2_VID, PICKIT2_PID, (unsigned int) usedevice); + if (!dev) { + msg_perr("Could not find a PICkit2 on USB!\n"); + return 1; + } + msg_pdbg("Found USB device (%04x:%04x).\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + pickit2_handle = usb_open(dev); + ret = usb_set_configuration(pickit2_handle, 1); + if (ret < 0) { + msg_perr("Could not set USB device configuration: %i %s\n", + ret, usb_strerror()); + if (usb_close(pickit2_handle)) + msg_perr("Could not close USB device!\n"); + return 1; + } + ret = usb_claim_interface(pickit2_handle, 0); + if (ret < 0) { + msg_perr("Could not claim USB device interface %i: %i %s\n", + 0, ret, usb_strerror()); + if (usb_close(pickit2_handle)) + msg_perr("Could not close USB device!\n"); + return 1; + } + + if (register_shutdown(pickit2_shutdown, NULL)) + return 1; + + if (pickit2_get_firmware_version()) { + return 1; + } + + /* Command Set SPI Speed */ + if (pickit2_set_spi_speed(spispeed_idx)) { + return 1; + } + + /* Command Set SPI Voltage */ + if (pickit2_set_spi_voltage(millivolt)) { + return 1; + } + + /* Perform basic setup */ + /* Configure pins for correct directions and logic levels, + * turn Vdd on, turn busy LED on and clear buffers + */ + ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)buf, CMD_LENGTH, DEFAULT_TIMEOUT); + + if (ret != CMD_LENGTH) { + msg_perr("Command Setup failed (%s)!\n", + usb_strerror()); + return 1; + } + + register_spi_programmer(&spi_programmer_pickit2); + + return 0; +} Index: programmer.h =================================================================== --- programmer.h (revision 1817) +++ programmer.h (working copy) @@ -96,6 +96,9 @@ #if CONFIG_USBBLASTER_SPI == 1 PROGRAMMER_USBBLASTER_SPI, #endif +#if CONFIG_PICKIT2_SPI == 1 + PROGRAMMER_PICKIT2_SPI, +#endif PROGRAMMER_INVALID /* This must always be the last entry. */ }; @@ -464,6 +467,11 @@ extern const struct dev_entry devs_usbblasterspi[]; #endif +/* pickit2_spi.c */ +#if CONFIG_PICKIT2_SPI == 1 +int pickit2_spi_init(void); +#endif + /* rayer_spi.c */ #if CONFIG_RAYER_SPI == 1 int rayer_spi_init(void); @@ -546,6 +554,9 @@ #if CONFIG_USBBLASTER_SPI == 1 SPI_CONTROLLER_USBBLASTER, #endif +#if CONFIG_PICKIT2_SPI == 1 + SPI_CONTROLLER_PICKIT2, +#endif }; #define MAX_DATA_UNSPECIFIED 0