Patchwork =?utf-8?q?=5BPATCH=5D=5BRFC=5D_uisp=5Fspi_programmer_s?= =?utf-8?q?upport?=

login
register
about
Submitter Andrew
Date 2014-05-25 13:49:04
Message ID <5c1b865b361d0e8f694580f6ac4c4aa1@mail.ncrmnt.org>
Download mbox | patch
Permalink /patch/4158/
State Superseded
Headers show

Comments

Andrew - 2014-05-25 13:49:04
Hello, all.

I've been playing around with buspirate and my uISP ( 
https://github.com/uISP/ ) dongle.
Even with newer firmware buspirate is really slow, and on average, takes 
3m 49s to read out
a 2MiB SPI flash.

So I decided to play around and see if I can make a faster programmer 
with my uISP
dongle. Basically it's an atmega8 with 12M crystal running vusb stack 
(no hardware usb). Any
other similar hardware would do. This hardware is really cheap (BOM is 
less than 5$ including
the pcb).

My first attempt was creating a serprog-compatible firmware for uISP. It 
can be found here:

https://github.com/uISP/uisp-app-serprog

The results weren't very exciting. It took 4m 30s to read out a 2MiB SPI 
flash (EN25QH16) on
12M crystal. Same for 20M crystal. It seems like it was a limitation of 
bulk out transfers via
vusb.

The second attempt required patching flashrom itself, but resulted in a 
MUCH simpler firmware
that fits roughly 115 lines of code (not counting vusb). It just uses 
control transfers for both
reads and writes. With vusb long transfers enabled, 20Mhz crystal, 10Mhz 
SPI speed and
max_data_read/max_data_write set to 4096 bytes reading a 2MiB SPI flash 
took only 2m 13s which I
consider a WIN.

4096 is not a vusb limit. USB spec limits maximum transfers to control 
endpoint to 4096 bytes anyway.

Using 254 byte max_data_read/max_data_write and long transfers disabled 
would be slower, but it is
possible to fit everything into 2K flash and use some attiny2313, which 
will make it even cheaper.
The protocol's simple as hell, so anyone with a better hardware around 
(STM32, atmega8u2, pic32, whatever)
can implement it with very little effort and get lighting-fast speeds.
My WIP patch to flashrom's attached, although I have a few silly 
questions left:

* Do I need to implement SPI frequency changing via a dedicated command 
or is it okay to hardcode it
   to 10Mhz in firmware?
* Right now I'm using the same spi_read as serprog does. Is okay, or 
should I add an option to disable
   it?
* firmware version checking, etc. (Is it a good idea to implement?)
* Is there a better way to benchmark read/write speed in flashrom, since 
delay loop calibration interferes
   with speed measurements (currently I just used "time flashrom ...")
Stefan Tauner - 2014-06-01 00:33:52
On Sun, 25 May 2014 17:49:04 +0400
Andrew <andrew@ncrmnt.org> wrote:

> Hello, all.
> 
> I've been playing around with buspirate and my uISP ( 
> https://github.com/uISP/ ) dongle.
> Even with newer firmware buspirate is really slow, and on average, takes 
> 3m 49s to read out
> a 2MiB SPI flash.
> 
> So I decided to play around and see if I can make a faster programmer 
> with my uISP
> dongle. Basically it's an atmega8 with 12M crystal running vusb stack 
> (no hardware usb). Any
> other similar hardware would do. This hardware is really cheap (BOM is 
> less than 5$ including
> the pcb).
> 
> My first attempt was creating a serprog-compatible firmware for uISP. It 
> can be found here:
> 
> https://github.com/uISP/uisp-app-serprog
> 
> The results weren't very exciting. It took 4m 30s to read out a 2MiB SPI 
> flash (EN25QH16) on
> 12M crystal. Same for 20M crystal. It seems like it was a limitation of 
> bulk out transfers via
> vusb.
> 
> The second attempt required patching flashrom itself, but resulted in a 
> MUCH simpler firmware
> that fits roughly 115 lines of code (not counting vusb). It just uses 
> control transfers for both
> reads and writes. With vusb long transfers enabled, 20Mhz crystal, 10Mhz 
> SPI speed and
> max_data_read/max_data_write set to 4096 bytes reading a 2MiB SPI flash 
> took only 2m 13s which I
> consider a WIN.
> 
> 4096 is not a vusb limit. USB spec limits maximum transfers to control 
> endpoint to 4096 bytes anyway.
> 
> Using 254 byte max_data_read/max_data_write and long transfers disabled 
> would be slower, but it is
> possible to fit everything into 2K flash and use some attiny2313, which 
> will make it even cheaper.
> The protocol's simple as hell, so anyone with a better hardware around 
> (STM32, atmega8u2, pic32, whatever)
> can implement it with very little effort and get lighting-fast speeds.
> My WIP patch to flashrom's attached, although I have a few silly 
> questions left:
> 
> * Do I need to implement SPI frequency changing via a dedicated command 
> or is it okay to hardcode it
>    to 10Mhz in firmware?
> * Right now I'm using the same spi_read as serprog does. Is okay, or 
> should I add an option to disable
>    it?
> * firmware version checking, etc. (Is it a good idea to implement?)
> * Is there a better way to benchmark read/write speed in flashrom, since 
> delay loop calibration interferes
>    with speed measurements (currently I just used "time flashrom ...")

Hi,

as you are probably aware of, we have quite some backlog of open patches
and yours is about the exact opposite of easy to deal with. :) Please
don't expect a detailed review any time soon. That said, thank you very
much for sending your patch (and your open hardware :). We would
certainly like to include a module for your programmer eventually.

I'll try to quickly answer your questions:

- SPI frequency is rather unimportant and adding an interface to change
  it can always be amended later IMHO.
- I don't understand your question fully. You have copied the function
  from serprog AFAICS. That's OK for now.
- serprog supports version checks but we had no need to use it yet(?)
  because we only added features (and opcodes) but did not refine
  existing functions in non-compatible ways. I have not looked at your
  protocol so I can't say if it is more likely to need it later. In
  general I think it would be a good idea to add this rather early.
- No benchmark functionality yet. If the calibration loop or anything
  else really makes any difference than your performance is good enough
  already IMHO ;) You could easily add some timekeeping to doit() and
  print the difference between two time stamps at the end, if you want
  more precession.

HTH for now, please keep us updated about your progress and thanks
again.
Andrew - 2014-06-02 07:35:31
Stefan Tauner wrote on 01.06.2014 04:33:
> On Sun, 25 May 2014 17:49:04 +0400
> Andrew <andrew@ncrmnt.org> wrote:
> 
>> Hello, all.
>> 
>> I've been playing around with buspirate and my uISP (
>> https://github.com/uISP/ ) dongle.
>> Even with newer firmware buspirate is really slow, and on average, 
>> takes
>> 3m 49s to read out
>> a 2MiB SPI flash.
>> 
>> So I decided to play around and see if I can make a faster programmer
>> with my uISP
>> dongle. Basically it's an atmega8 with 12M crystal running vusb stack
>> (no hardware usb). Any
>> other similar hardware would do. This hardware is really cheap (BOM is
>> less than 5$ including
>> the pcb).
>> 
>> My first attempt was creating a serprog-compatible firmware for uISP. 
>> It
>> can be found here:
>> 
>> https://github.com/uISP/uisp-app-serprog
>> 
>> The results weren't very exciting. It took 4m 30s to read out a 2MiB 
>> SPI
>> flash (EN25QH16) on
>> 12M crystal. Same for 20M crystal. It seems like it was a limitation 
>> of
>> bulk out transfers via
>> vusb.
>> 
>> The second attempt required patching flashrom itself, but resulted in 
>> a
>> MUCH simpler firmware
>> that fits roughly 115 lines of code (not counting vusb). It just uses
>> control transfers for both
>> reads and writes. With vusb long transfers enabled, 20Mhz crystal, 
>> 10Mhz
>> SPI speed and
>> max_data_read/max_data_write set to 4096 bytes reading a 2MiB SPI 
>> flash
>> took only 2m 13s which I
>> consider a WIN.
>> 
>> 4096 is not a vusb limit. USB spec limits maximum transfers to control
>> endpoint to 4096 bytes anyway.
>> 
>> Using 254 byte max_data_read/max_data_write and long transfers 
>> disabled
>> would be slower, but it is
>> possible to fit everything into 2K flash and use some attiny2313, 
>> which
>> will make it even cheaper.
>> The protocol's simple as hell, so anyone with a better hardware around
>> (STM32, atmega8u2, pic32, whatever)
>> can implement it with very little effort and get lighting-fast speeds.
>> My WIP patch to flashrom's attached, although I have a few silly
>> questions left:
>> 
>> * Do I need to implement SPI frequency changing via a dedicated 
>> command
>> or is it okay to hardcode it
>>    to 10Mhz in firmware?
>> * Right now I'm using the same spi_read as serprog does. Is okay, or
>> should I add an option to disable
>>    it?
>> * firmware version checking, etc. (Is it a good idea to implement?)
>> * Is there a better way to benchmark read/write speed in flashrom, 
>> since
>> delay loop calibration interferes
>>    with speed measurements (currently I just used "time flashrom ...")
> 
> Hi,
> 
> as you are probably aware of, we have quite some backlog of open 
> patches
> and yours is about the exact opposite of easy to deal with. :) Please
> don't expect a detailed review any time soon. That said, thank you very
> much for sending your patch (and your open hardware :). We would
> certainly like to include a module for your programmer eventually.
> 
> I'll try to quickly answer your questions:
> 
> - SPI frequency is rather unimportant and adding an interface to change
>   it can always be amended later IMHO.
> - I don't understand your question fully. You have copied the function
>   from serprog AFAICS. That's OK for now.
> - serprog supports version checks but we had no need to use it yet(?)
>   because we only added features (and opcodes) but did not refine
>   existing functions in non-compatible ways. I have not looked at your
>   protocol so I can't say if it is more likely to need it later. In
>   general I think it would be a good idea to add this rather early.
> - No benchmark functionality yet. If the calibration loop or anything
>   else really makes any difference than your performance is good enough
>   already IMHO ;) You could easily add some timekeeping to doit() and
>   print the difference between two time stamps at the end, if you want
>   more precession.
> 
> HTH for now, please keep us updated about your progress and thanks
> again.

Thanks for the reply. I will be refining and documenting the protocol 
and
hardware shortly and will resend the patch this week along with a more 
detailed
writeup, pictures and benchmarks.

So far the changes implemented are:
   * Protocol versioning support
   * Programmer now reports CPU frequency and maximum SPI frequency
   * SPI Frequency setting (for now with a programmer param)
   * Multi-CS support (if the programmer has several flashes attached to 
different chip-selects)
   * Any ideas what else to implement?
   * Programmer lock/unlock (Another accidentally started instance of 
flashrom won't screw us up anymore)

I will be also making some new hardware based on STM32, that should 
provide a real speed boost,
but since the actual chips are stuck somewhere between China and Russia 
this may take a while.
My goal is to make a blazing fast programmer with BOM cost of under 5$ 
(and possibly parallel
flash programming support as well).
Stefan Tauner - 2014-06-02 17:02:43
On Mon, 02 Jun 2014 11:35:31 +0400
Andrew <andrew@ncrmnt.org> wrote:

>    * Programmer lock/unlock (Another accidentally started instance of 
> flashrom won't screw us up anymore)

chromiumos' flashrom has something like that implemented globally IIRC.
It does not make too much sense to implement that for a single
programmer... could two instances get access to the interface
concurrently at all?

> I will be also making some new hardware based on STM32, that should 
> provide a real speed boost,
> but since the actual chips are stuck somewhere between China and Russia 
> this may take a while.

I have a discovery board laying around with an STM32F4... but have
never plugged it in yet :/

> My goal is to make a blazing fast programmer with BOM cost of under 5$ 
> (and possibly parallel
> flash programming support as well).

You might be interested in http://qiprog.org/
The GSoC student that made it last year sadly joined the US armed
forces, so don't expect too many answers regarding it from him.
Peter Stuge was the mentor and is probably the one to ask if you have
questions not answered by the website/code or last year's blog articles
http://blogs.coreboot.org/blog/author/mrnuke/
Andrew - 2014-06-02 18:32:16
Stefan Tauner писал 02.06.2014 21:02:
> On Mon, 02 Jun 2014 11:35:31 +0400
> Andrew <andrew@ncrmnt.org> wrote:
> 
>>    * Programmer lock/unlock (Another accidentally started instance of
>> flashrom won't screw us up anymore)
> 
> chromiumos' flashrom has something like that implemented globally IIRC.
> It does not make too much sense to implement that for a single
> programmer... could two instances get access to the interface
> concurrently at all?

Okay, I'll drop it then. Thanks.
For an stm32-based one it can be possible and makes sense to 
concurrently interact
with several SPI chips connected to several hardware SPI interfaces e.g. 
stm32 has the guts
to do so without any slowdowns, so I think of implementing this feature 
in the core code, yet
leave it out for avr.

For STM32 it might also make more sense to export a /dev/ttyACMx for 
each
SPI interface and use serprog protocol, but that needs testing.
On avr vusb-based cdc_acm is damn slow for device->host transfers and 
doesn't work
for some usb hosts. And I'm not sure it will be the fastest way on a 
stm32 either.

STM32 right now requires love, since ST's usb device stack is utter 
unusable crap and
definitely has a race condition somewhere that causes enumeration 
problems. I'm playing
with it for my pet antares project ( http://github.com/nekromant/antares 
), but ST's code
needs a lot of refactoring before it can be used at all (or even a 
rewrite from scratch).

> 
>> I will be also making some new hardware based on STM32, that should
>> provide a real speed boost,
>> but since the actual chips are stuck somewhere between China and 
>> Russia
>> this may take a while.
> 
> I have a discovery board laying around with an STM32F4... but have
> never plugged it in yet :/

Actually, you can use even an stm32f1 discovery as a flashrom programmer 
with stlink
as the interface ;) The stlink will deliver ~25Kb/sec speeds. I've done 
this a while
ago using this hack of mine: 
http://ncrmnt.org/wp/2013/05/06/stlink-as-a-serial-terminal/
when I had to urgently debrick a router.
But it was utter hackery and I didn't bother to send in the patch to 
flashrom.
ST also has COMPLETELY different usb device stacks for stm32f4x and 
stm32f1x with
COMPLETELY different API. (And both look weird as hell.) For now I 
target stm32f1x.


>> My goal is to make a blazing fast programmer with BOM cost of under 5$
>> (and possibly parallel
>> flash programming support as well).
> 
> You might be interested in http://qiprog.org/
> The GSoC student that made it last year sadly joined the US armed
> forces, so don't expect too many answers regarding it from him.
> Peter Stuge was the mentor and is probably the one to ask if you have
> questions not answered by the website/code or last year's blog articles
> http://blogs.coreboot.org/blog/author/mrnuke/

Thanks for the links, I'll have a look. But I guess it will be faster 
for me
to design it from scratch, I guess. Not a big deal, anyway.

Patch

Index: uisp_spi.c
===================================================================
--- uisp_spi.c	(revision 0)
+++ uisp_spi.c	(revision 0)
@@ -0,0 +1,270 @@ 
+/*
+ * This file is part of the flashrom project.
+ * uISP programmer interface
+ *
+ * Copyright (C) 2014 Andew "Necromant" Andrianov <andrew@ncrmnt.org>
+ *
+ * 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 <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <usb.h>
+#include "flash.h"
+#include "programmer.h"
+#include "spi.h"
+#include "chipdrivers.h"
+
+
+#define UISP_TIMEOUT 30000
+static usb_dev_handle *hndl;
+
+#define UISP_VID   0x1d50
+#define UISP_PID   0x6032
+#define UISP_MSTR  "www.ncrmnt.org"
+#define UISP_PSTR  "uISP-flashprog"
+
+enum { 
+	RQ_IO=0,
+	RQ_CS,
+	RQ_SETSPEED,
+	RQ_LED,
+} uisp_requsts; 
+
+const struct dev_entry devs_usbuispspi[] = {
+	{UISP_VID, UISP_PID, OK, UISP_MSTR, UISP_PSTR},
+
+	{}
+};
+
+static int  usb_get_string_ascii(usb_dev_handle *dev, int index, int langid, char *buf, int buflen)
+{
+	char    buffer[256];
+	int     rval, i;
+	
+	if((rval = usb_control_msg(dev, 
+				   USB_ENDPOINT_IN, 
+				   USB_REQ_GET_DESCRIPTOR, 
+				   (USB_DT_STRING << 8) + index, 
+				   langid, buffer, sizeof(buffer), 
+				   1000)) < 0)
+		return rval;
+	if(buffer[1] != USB_DT_STRING)
+		return 0;
+	if((unsigned char)buffer[0] < rval)
+		rval = (unsigned char)buffer[0];
+	rval /= 2;
+	/* lossy conversion to ISO Latin1 */
+	for(i=1; i<rval; i++) {
+		if(i > buflen)  /* destination buffer overflow */
+			break;
+		buf[i-1] = buffer[2 * i];
+		if(buffer[2 * i + 1] != 0)  /* outside of ISO Latin1 range */
+			buf[i-1] = '?';
+	}
+	buf[i-1] = 0;
+	return i-1;
+}
+
+
+static int usb_match_string(usb_dev_handle *handle, int index, char* string)
+{
+	char tmp[256];
+	if (string == NULL)
+		return 1; /* NULL matches anything */
+	usb_get_string_ascii(handle, index, 0x409, tmp, 256);
+	return (strcmp(string,tmp)==0);
+}
+
+static usb_dev_handle *usb_check_device(struct usb_device *dev,
+				 char *vendor_name, 
+				 char *product_name, 
+				 char *serial)
+{
+	usb_dev_handle      *handle = usb_open(dev);
+	if(!handle) {
+		fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror());
+		return NULL;
+	}
+	if (
+		usb_match_string(handle, dev->descriptor.iManufacturer, vendor_name) &&
+		usb_match_string(handle, dev->descriptor.iProduct,      product_name) &&
+		usb_match_string(handle, dev->descriptor.iSerialNumber, serial)
+		) {
+		return handle;
+	}
+	usb_close(handle);
+	return NULL;
+	
+}
+
+static usb_dev_handle *nc_usb_open(int vendor, int product, char *vendor_name, char *product_name, char *serial)
+{
+	struct usb_bus      *bus;
+	struct usb_device   *dev;
+	usb_dev_handle      *handle = NULL;
+	
+
+	usb_find_busses();
+	usb_find_devices();
+
+	for(bus=usb_get_busses(); bus; bus=bus->next) {
+		for(dev=bus->devices; dev; dev=dev->next) {
+			            if(dev->descriptor.idVendor == vendor && 
+				       dev->descriptor.idProduct == product) {
+					    handle = usb_check_device(dev, vendor_name, product_name, serial);
+					    if (handle)
+						    return handle;
+				    }
+		}
+	}
+	return NULL;
+}
+
+
+
+static int send_read(unsigned int readcnt, void *readarr)
+{
+	msg_pspew("%s\n", __func__);
+	int ret; 
+	ret = usb_control_msg(
+		hndl,             // handle obtained with usb_open()
+		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, // bRequestType
+		RQ_IO,      // bRequest
+		0,              // wValue
+		0,              // wIndex
+		readarr,             // pointer to source buffer
+		readcnt,  // wLength
+		UISP_TIMEOUT
+		);
+	msg_pspew("%s: control ret %d ", __func__, ret); 
+	if (ret != readcnt) {
+		perror(__func__);
+		return -1; 
+	}
+	return 0;
+}
+
+static int do_control(int rq, int value, int index)
+{
+	return  usb_control_msg(
+		hndl,             // handle obtained with usb_open()
+		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, // bRequestType
+		rq,      // bRequest
+		value,              // wValue
+		index,              // wIndex
+		NULL,             // pointer to destination buffer
+		0,  // wLength
+		UISP_TIMEOUT
+		);
+}
+
+static int send_write(unsigned int writecnt, void *writearr)
+{
+	msg_pspew("%s\n", __func__);
+	int ret; 
+	ret = usb_control_msg(
+		hndl,             // handle obtained with usb_open()
+		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, // bRequestType
+		RQ_IO,      // bRequest
+		0,              // wValue
+		0,              // wIndex
+		writearr,             // pointer to source buffer
+		writecnt,  // wLength
+		UISP_TIMEOUT
+		);
+	msg_pspew("%s: control ret %d ", __func__, ret); 
+	if (ret != writecnt) {
+		perror(__func__);
+		return -1; 
+	}
+	return 0;
+}
+
+
+static int uisp_spi_send_command(struct flashctx *flash,
+				     unsigned int writecnt,
+				     unsigned int readcnt,
+				     const unsigned char *writearr,
+				     unsigned char *readarr)
+{
+	msg_pspew("%s %d %d\n", __func__, readcnt, writecnt);
+	
+	int ret = 0;
+	
+	ret = do_control(RQ_CS, 0, 0);
+
+	if (!ret && writecnt)
+		ret = send_write(writecnt, (void *) writearr);
+
+	if (!ret && readcnt)
+		ret = send_read(readcnt, readarr);
+
+	ret = do_control(RQ_CS, 1, 0);
+
+	return ret;
+}
+
+
+static int uisp_spi_read(struct flashctx *flash, uint8_t *buf,
+			 unsigned int start, unsigned int len);
+
+/* We have 4096 maximum transfer length for control ep
+ * hardcoded in the usb stack. 
+ * Make sure to have long-transfers enabled
+ */
+static const struct spi_programmer spi_programmer_uisp = {
+	.type		= SPI_CONTROLLER_UISP,
+	.max_data_read	= 4096,
+	.max_data_write	= 4096,
+	.command	= uisp_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= uisp_spi_read,
+	.write_256	= default_spi_write_256,
+	.write_aai	= default_spi_write_aai,
+};
+
+static int uisp_spi_read(struct flashctx *flash, uint8_t *buf,
+			    unsigned int start, unsigned int len)
+{
+	unsigned int i, cur_len;
+	const unsigned int max_read = spi_programmer_uisp.max_data_read;
+	for (i = 0; i < len; i += cur_len) {
+		int ret;
+		cur_len = min(max_read, (len - i));
+		ret = spi_nbyte_read(flash, start + i, buf + i, cur_len);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+
+
+int uisp_spi_init(void)
+{	
+	msg_pspew("%s\n", __func__);
+	usb_init();
+	hndl = nc_usb_open(UISP_VID, UISP_PID, UISP_MSTR, UISP_PSTR, NULL);
+	if (!hndl)  
+		return 1;
+	
+	register_spi_programmer(&spi_programmer_uisp);
+	
+	return 0;
+}
Index: programmer.h
===================================================================
--- programmer.h	(revision 1797)
+++ programmer.h	(working copy)
@@ -90,6 +90,9 @@ 
 #if CONFIG_USBBLASTER_SPI == 1
 	PROGRAMMER_USBBLASTER_SPI,
 #endif
+#if CONFIG_UISP_SPI == 1
+	PROGRAMMER_UISP_SPI,
+#endif
 	PROGRAMMER_INVALID /* This must always be the last entry. */
 };
 
@@ -464,6 +467,12 @@ 
 int buspirate_spi_init(void);
 #endif
 
+/* uisp_spi.c */
+#if CONFIG_UISP_SPI == 1
+int uisp_spi_init(void);
+extern const struct dev_entry devs_usbuispspi[];
+#endif
+
 /* linux_spi.c */
 #if CONFIG_LINUX_SPI == 1
 int linux_spi_init(void);
@@ -528,6 +537,9 @@ 
 #if CONFIG_USBBLASTER_SPI == 1
 	SPI_CONTROLLER_USBBLASTER,
 #endif
+#if CONFIG_UISP_SPI == 1
+	SPI_CONTROLLER_UISP,
+#endif
 };
 
 #define MAX_DATA_UNSPECIFIED 0
Index: Makefile
===================================================================
--- Makefile	(revision 1797)
+++ Makefile	(working copy)
@@ -122,6 +122,11 @@ 
 else
 override CONFIG_BUSPIRATE_SPI = no
 endif
+ifeq ($(CONFIG_UISP_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_UISP_SPI=yes
+else
+override CONFIG_UISP_SPI = no
+endif
 ifeq ($(CONFIG_SERPROG), yes)
 UNSUPPORTED_FEATURES += CONFIG_SERPROG=yes
 else
@@ -246,6 +251,11 @@ 
 else
 override CONFIG_BUSPIRATE_SPI = no
 endif
+ifeq ($(CONFIG_UISP_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_UISP_SPI=yes
+else
+override CONFIG_UISP_SPI = no
+endif
 ifeq ($(CONFIG_SERPROG), yes)
 UNSUPPORTED_FEATURES += CONFIG_SERPROG=yes
 else
@@ -414,6 +424,9 @@ 
 # Always enable Bus Pirate SPI for now.
 CONFIG_BUSPIRATE_SPI ?= yes
 
+#Always enable uISP for now
+CONFIG_UISP_SPI ?= yes
+
 # Disable Dediprog SF100 until support is complete and tested.
 CONFIG_DEDIPROG ?= no
 
@@ -604,6 +617,13 @@ 
 NEED_SERIAL := yes
 endif
 
+ifeq ($(CONFIG_UISP_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_UISP_SPI=1'
+PROGRAMMER_OBJS += uisp_spi.o
+NEED_USB := yes
+endif
+
+
 ifeq ($(CONFIG_DEDIPROG), yes)
 FEATURE_CFLAGS += -D'CONFIG_DEDIPROG=1'
 PROGRAMMER_OBJS += dediprog.o
Index: flashrom.c
===================================================================
--- flashrom.c	(revision 1797)
+++ flashrom.c	(working copy)
@@ -321,6 +321,20 @@ 
 	},
 #endif
 
+#if CONFIG_UISP_SPI == 1
+	{
+		.name			= "uisp_spi",
+		.type			= USB,
+		.devs.note		= "Necromant's uISP dongle (flashprog app)\n",
+		.devs.dev               = devs_usbuispspi,
+		.init			= uisp_spi_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
+	},
+#endif
+
+
 	{0}, /* This entry corresponds to PROGRAMMER_INVALID. */
 };