Index: Makefile
===================================================================
--- Makefile	(revision 1657)
+++ Makefile	(working copy)
@@ -117,7 +117,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
@@ -128,7 +128,12 @@
 else
 override CONFIG_FT2232_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)
@@ -229,7 +234,7 @@
 else
 override CONFIG_SERPROG = 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
@@ -240,7 +245,12 @@
 else
 override CONFIG_FT2232_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)
@@ -382,6 +392,9 @@
 # Enable Linux spidev interface by default. We disable it on non-Linux targets.
 CONFIG_LINUX_SPI ?= yes
 
+# Always enable PICkit2 SPI dongles for now.
+CONFIG_PICKIT2_SPI ?= yes
+
 # Disable wiki printing by default. It is only useful if you have wiki access.
 CONFIG_PRINT_WIKI ?= no
 
@@ -549,6 +562,12 @@
 PROGRAMMER_OBJS += linux_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_SERIAL), yes)
 LIB_OBJS += serial.o
 endif
Index: flashrom.8
===================================================================
--- flashrom.8	(revision 1657)
+++ flashrom.8	(working copy)
@@ -220,6 +220,8 @@
 .sp
 .BR "* linux_spi" " (for SPI flash ROMs accessible via /dev/spidevX.Y on Linux)"
 .sp
+.BR "* pickit2_spi" " (for SPI flash ROMs accessible via Microchip PICkit2 on Linux)"
+.sp
 Some programmers have optional or mandatory parameters which are described
 in detail in the
 .B PROGRAMMER SPECIFIC INFO
Index: flashrom.c
===================================================================
--- flashrom.c	(revision 1657)
+++ flashrom.c	(working copy)
@@ -308,6 +308,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,519 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2013 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <usb.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+static usb_dev_handle *pickit2_handle;
+
+#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
+
+/* 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;
+	unsigned char 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;
+	uint16_t 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);
+
+	unsigned char command[CMD_LENGTH] = {
+		CMD_SET_VDD,
+		(uint8_t)(voltage_selector * 2048+672),
+		(uint8_t)((voltage_selector * 2048+672)/256),
+		(uint8_t)(voltage_selector * 36),
+		CMD_SET_VPP,
+		0x40,
+		(uint8_t)(voltage_selector * 18.61),
+		(uint8_t)(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);
+
+	unsigned char 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;
+
+	unsigned char 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) {
+		memset(readarr, 0, readcnt);
+		memset(buf, 0, sizeof(buf));
+
+		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;
+}
+
+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 int pickit2_setup(void)
+{
+	int ret;
+	/* Configure pins for correct directions and logic levels,
+	 * turn Vdd on, turn busy LED on and clear buffers
+	 */
+	unsigned char 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
+	};
+
+	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;
+	}
+
+	return 0;
+}
+
+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 */
+	unsigned char 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;
+
+	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 */
+	if (pickit2_setup()) {
+		return 1;
+	}
+
+	register_spi_programmer(&spi_programmer_pickit2);
+
+	return 0;
+}
Index: programmer.h
===================================================================
--- programmer.h	(revision 1657)
+++ programmer.h	(working copy)
@@ -87,6 +87,9 @@
 #if CONFIG_LINUX_SPI == 1
 	PROGRAMMER_LINUX_SPI,
 #endif
+#if CONFIG_PICKIT2_SPI == 1
+	PROGRAMMER_PICKIT2_SPI,
+#endif
 	PROGRAMMER_INVALID /* This must always be the last entry. */
 };
 
@@ -459,6 +462,11 @@
 int dediprog_init(void);
 #endif
 
+/* pickit2_spi.c */
+#if CONFIG_PICKIT2_SPI == 1
+int pickit2_spi_init(void);
+#endif
+
 /* flashrom.c */
 struct decode_sizes {
 	uint32_t parallel;
@@ -506,6 +514,9 @@
 #if CONFIG_LINUX_SPI == 1
 	SPI_CONTROLLER_LINUX,
 #endif
+#if CONFIG_PICKIT2_SPI == 1
+	SPI_CONTROLLER_PICKIT2,
+#endif
 #if CONFIG_SERPROG == 1
 	SPI_CONTROLLER_SERPROG,
 #endif
