blob: 4354025ea9f43d00995786c25c37f518dbd1dfa3 [file] [log] [blame]
Justin Chevrier66e554b2015-02-08 21:58:10 +00001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Carl-Daniel Hailfinger
5 * Copyright (C) 2014 Justin Chevrier
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Justin Chevrier66e554b2015-02-08 21:58:10 +000015 */
16
17/*
18 * Connections are as follows:
19 *
20 * +------+-----+----------+
21 * | SPI | Pin | PICkit2 |
22 * +------+-----+----------+
23 * | /CS | 1 | VPP/MCLR |
24 * | VCC | 2 | VDD |
25 * | GND | 3 | GND |
26 * | MISO | 4 | PGD |
27 * | SCLK | 5 | PDC |
28 * | MOSI | 6 | AUX |
29 * +------+-----+----------+
30 *
31 * Inspiration and some specifics of the interface came via the AVRDude
32 * PICkit2 code: https://github.com/steve-m/avrdude/blob/master/pickit2.c
33 */
34
35#include "platform.h"
36
37#include <stdlib.h>
38#include <stdio.h>
39#include <string.h>
40#include <limits.h>
41#include <errno.h>
42
43#if IS_WINDOWS
44#include <lusb0_usb.h>
45#else
46#include <usb.h>
47#endif
48
49#include "flash.h"
50#include "chipdrivers.h"
51#include "programmer.h"
52#include "spi.h"
53
Stefan Taunerf31fe842016-02-22 08:59:15 +000054const struct dev_entry devs_pickit2_spi[] = {
55 {0x04D8, 0x0033, OK, "Microchip", "PICkit 2"},
56
57 {}
58};
59
Justin Chevrier66e554b2015-02-08 21:58:10 +000060static usb_dev_handle *pickit2_handle;
61
62/* Default USB transaction timeout in ms */
63#define DFLT_TIMEOUT 10000
64
65#define CMD_LENGTH 64
66#define ENDPOINT_OUT 0x01
67#define ENDPOINT_IN 0x81
68
Justin Chevrier66e554b2015-02-08 21:58:10 +000069#define CMD_GET_VERSION 0x76
70#define CMD_SET_VDD 0xA0
71#define CMD_SET_VPP 0xA1
72#define CMD_READ_VDD_VPP 0xA3
73#define CMD_EXEC_SCRIPT 0xA6
74#define CMD_CLR_DLOAD_BUFF 0xA7
75#define CMD_DOWNLOAD_DATA 0xA8
76#define CMD_CLR_ULOAD_BUFF 0xA9
77#define CMD_UPLOAD_DATA 0xAA
78#define CMD_END_OF_BUFFER 0xAD
79
80#define SCR_SPI_READ_BUF 0xC5
81#define SCR_SPI_WRITE_BUF 0xC6
82#define SCR_SET_AUX 0xCF
83#define SCR_LOOP 0xE9
84#define SCR_SET_ICSP_CLK_PERIOD 0xEA
85#define SCR_SET_PINS 0xF3
86#define SCR_BUSY_LED_OFF 0xF4
87#define SCR_BUSY_LED_ON 0xF5
88#define SCR_MCLR_GND_OFF 0xF6
89#define SCR_MCLR_GND_ON 0xF7
90#define SCR_VPP_PWM_OFF 0xF8
91#define SCR_VPP_PWM_ON 0xF9
92#define SCR_VPP_OFF 0xFA
93#define SCR_VPP_ON 0xFB
94#define SCR_VDD_OFF 0xFE
95#define SCR_VDD_ON 0xFF
96
Nico Huberd99a2bd2016-02-18 21:42:49 +000097/* Might be useful for other USB devices as well. static for now.
98 * device parameter allows user to specify one device of multiple installed */
Justin Chevrier66e554b2015-02-08 21:58:10 +000099static struct usb_device *get_device_by_vid_pid(uint16_t vid, uint16_t pid, unsigned int device)
100{
101 struct usb_bus *bus;
102 struct usb_device *dev;
103
104 for (bus = usb_get_busses(); bus; bus = bus->next)
105 for (dev = bus->devices; dev; dev = dev->next)
106 if ((dev->descriptor.idVendor == vid) &&
107 (dev->descriptor.idProduct == pid)) {
108 if (device == 0)
109 return dev;
110 device--;
111 }
112
113 return NULL;
114}
115
116static int pickit2_get_firmware_version(void)
117{
118 int ret;
119 uint8_t command[CMD_LENGTH] = {CMD_GET_VERSION, CMD_END_OF_BUFFER};
120
121 ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
122 ret = usb_interrupt_read(pickit2_handle, ENDPOINT_IN, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100123
Justin Chevrier66e554b2015-02-08 21:58:10 +0000124 msg_pdbg("PICkit2 Firmware Version: %d.%d\n", (int)command[0], (int)command[1]);
125 if (ret != CMD_LENGTH) {
126 msg_perr("Command Get Firmware Version failed (%s)!\n", usb_strerror());
127 return 1;
128 }
129
130 return 0;
131}
132
133static int pickit2_set_spi_voltage(int millivolt)
134{
135 double voltage_selector;
136 switch (millivolt) {
137 case 0:
138 /* Admittedly this one is an assumption. */
139 voltage_selector = 0;
140 break;
141 case 1800:
142 voltage_selector = 1.8;
143 break;
144 case 2500:
145 voltage_selector = 2.5;
146 break;
147 case 3500:
148 voltage_selector = 3.5;
149 break;
150 default:
151 msg_perr("Unknown voltage %i mV! Aborting.\n", millivolt);
152 return 1;
153 }
154 msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000,
155 millivolt % 1000);
156
157 uint8_t command[CMD_LENGTH] = {
158 CMD_SET_VDD,
159 voltage_selector * 2048 + 672,
160 (voltage_selector * 2048 + 672) / 256,
161 voltage_selector * 36,
162 CMD_SET_VPP,
163 0x40,
164 voltage_selector * 18.61,
165 voltage_selector * 13,
166 CMD_END_OF_BUFFER
167 };
168
169 int ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
170
171 if (ret != CMD_LENGTH) {
172 msg_perr("Command Set Voltage failed (%s)!\n", usb_strerror());
173 return 1;
174 }
175
176 return 0;
177}
178
179struct pickit2_spispeeds {
180 const char *const name;
181 const int speed;
182};
183
184static const struct pickit2_spispeeds spispeeds[] = {
185 { "1M", 0x1 },
186 { "500k", 0x2 },
187 { "333k", 0x3 },
188 { "250k", 0x4 },
189 { NULL, 0x0 },
190};
191
192static int pickit2_set_spi_speed(unsigned int spispeed_idx)
193{
194 msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed_idx].name);
195
196 uint8_t command[CMD_LENGTH] = {
197 CMD_EXEC_SCRIPT,
198 2,
199 SCR_SET_ICSP_CLK_PERIOD,
200 spispeed_idx,
201 CMD_END_OF_BUFFER
202 };
203
204 int ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
205
206 if (ret != CMD_LENGTH) {
207 msg_perr("Command Set SPI Speed failed (%s)!\n", usb_strerror());
208 return 1;
209 }
210
211 return 0;
212}
213
214static int pickit2_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
215 const unsigned char *writearr, unsigned char *readarr)
216{
217
218 /* Maximum number of bytes per transaction (including command overhead) is 64. Lets play it safe
219 * and always assume the worst case scenario of 20 bytes command overhead.
220 */
221 if (writecnt + readcnt + 20 > CMD_LENGTH) {
222 msg_perr("\nTotal packetsize (%i) is greater than 64 supported, aborting.\n",
223 writecnt + readcnt + 20);
224 return 1;
225 }
226
227 uint8_t buf[CMD_LENGTH] = {CMD_DOWNLOAD_DATA, writecnt};
228 int i = 2;
229 for (; i < writecnt + 2; i++) {
230 buf[i] = writearr[i - 2];
231 }
232
233 buf[i++] = CMD_CLR_ULOAD_BUFF;
234 buf[i++] = CMD_EXEC_SCRIPT;
235
236 /* Determine script length based on number of bytes to be read or written */
237 if (writecnt == 1 && readcnt == 1)
238 buf[i++] = 7;
239 else if (writecnt == 1 || readcnt == 1)
240 buf[i++] = 10;
241 else
242 buf[i++] = 13;
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100243
Justin Chevrier66e554b2015-02-08 21:58:10 +0000244 /* Assert CS# */
245 buf[i++] = SCR_VPP_OFF;
246 buf[i++] = SCR_MCLR_GND_ON;
247
248 buf[i++] = SCR_SPI_WRITE_BUF;
249
250 if (writecnt > 1) {
251 buf[i++] = SCR_LOOP;
252 buf[i++] = 1; /* Loop back one instruction */
253 buf[i++] = writecnt - 1; /* Number of times to loop */
254 }
255
256 if (readcnt)
257 buf[i++] = SCR_SPI_READ_BUF;
258
259 if (readcnt > 1) {
260 buf[i++] = SCR_LOOP;
261 buf[i++] = 1; /* Loop back one instruction */
262 buf[i++] = readcnt - 1; /* Number of times to loop */
263 }
264
265 /* De-assert CS# */
266 buf[i++] = SCR_MCLR_GND_OFF;
267 buf[i++] = SCR_VPP_PWM_ON;
268 buf[i++] = SCR_VPP_ON;
269
270 buf[i++] = CMD_UPLOAD_DATA;
271 buf[i++] = CMD_END_OF_BUFFER;
272
273 int ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)buf, CMD_LENGTH, DFLT_TIMEOUT);
274
275 if (ret != CMD_LENGTH) {
276 msg_perr("Send SPI failed, expected %i, got %i %s!\n", writecnt, ret, usb_strerror());
277 return 1;
278 }
279
280 if (readcnt) {
281 ret = usb_interrupt_read(pickit2_handle, ENDPOINT_IN, (char *)buf, CMD_LENGTH, DFLT_TIMEOUT);
282
283 if (ret != CMD_LENGTH) {
284 msg_perr("Receive SPI failed, expected %i, got %i %s!\n", readcnt, ret, usb_strerror());
285 return 1;
286 }
287
288 /* First byte indicates number of bytes transferred from upload buffer */
289 if (buf[0] != readcnt) {
290 msg_perr("Unexpected number of bytes transferred, expected %i, got %i!\n",
291 readcnt, ret);
292 return 1;
293 }
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100294
Justin Chevrier66e554b2015-02-08 21:58:10 +0000295 /* Actual data starts at byte number two */
296 memcpy(readarr, &buf[1], readcnt);
297 }
298
299 return 0;
300}
301
302/* Copied from dediprog.c */
303/* Might be useful for other USB devices as well. static for now. */
304static int parse_voltage(char *voltage)
305{
306 char *tmp = NULL;
307 int i;
308 int millivolt = 0, fraction = 0;
309
310 if (!voltage || !strlen(voltage)) {
311 msg_perr("Empty voltage= specified.\n");
312 return -1;
313 }
314 millivolt = (int)strtol(voltage, &tmp, 0);
315 voltage = tmp;
316 /* Handle "," and "." as decimal point. Everything after it is assumed
317 * to be in decimal notation.
318 */
319 if ((*voltage == '.') || (*voltage == ',')) {
320 voltage++;
321 for (i = 0; i < 3; i++) {
322 fraction *= 10;
323 /* Don't advance if the current character is invalid,
324 * but continue multiplying.
325 */
326 if ((*voltage < '0') || (*voltage > '9'))
327 continue;
328 fraction += *voltage - '0';
329 voltage++;
330 }
331 /* Throw away remaining digits. */
332 voltage += strspn(voltage, "0123456789");
333 }
334 /* The remaining string must be empty or "mV" or "V". */
335 tolower_string(voltage);
336
337 /* No unit or "V". */
338 if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) {
339 millivolt *= 1000;
340 millivolt += fraction;
341 } else if (!strncmp(voltage, "mv", 2) ||
342 !strncmp(voltage, "millivolt", 9)) {
343 /* No adjustment. fraction is discarded. */
344 } else {
345 /* Garbage at the end of the string. */
346 msg_perr("Garbage voltage= specified.\n");
347 return -1;
348 }
349 return millivolt;
350}
351
352static const struct spi_master spi_master_pickit2 = {
353 .type = SPI_CONTROLLER_PICKIT2,
354 .max_data_read = 40,
355 .max_data_write = 40,
356 .command = pickit2_spi_send_command,
357 .multicommand = default_spi_send_multicommand,
358 .read = default_spi_read,
359 .write_256 = default_spi_write_256,
360 .write_aai = default_spi_write_aai,
361};
362
363static int pickit2_shutdown(void *data)
364{
365 /* Set all pins to float and turn voltages off */
366 uint8_t command[CMD_LENGTH] = {
367 CMD_EXEC_SCRIPT,
368 8,
369 SCR_SET_PINS,
370 3, /* Bit-0=1(PDC In), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
371 SCR_SET_AUX,
372 1, /* Bit-0=1(Aux In), Bit-1=0(Aux LL) */
373 SCR_MCLR_GND_OFF,
374 SCR_VPP_OFF,
375 SCR_VDD_OFF,
376 SCR_BUSY_LED_OFF,
377 CMD_END_OF_BUFFER
378 };
379
380 int ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
381
382 if (ret != CMD_LENGTH) {
383 msg_perr("Command Shutdown failed (%s)!\n", usb_strerror());
384 ret = 1;
385 }
386 if (usb_release_interface(pickit2_handle, 0) != 0) {
387 msg_perr("Could not release USB interface!\n");
388 ret = 1;
389 }
390 if (usb_close(pickit2_handle) != 0) {
391 msg_perr("Could not close USB device!\n");
392 ret = 1;
393 }
394 return ret;
395}
396
397int pickit2_spi_init(void)
398{
Stefan Tauner0be072c2016-03-13 15:16:30 +0000399 unsigned int usedevice = 0; // FIXME: Allow selecting one of multiple devices
Justin Chevrier66e554b2015-02-08 21:58:10 +0000400
401 uint8_t buf[CMD_LENGTH] = {
402 CMD_EXEC_SCRIPT,
403 10, /* Script length */
404 SCR_SET_PINS,
405 2, /* Bit-0=0(PDC Out), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
406 SCR_SET_AUX,
407 0, /* Bit-0=0(Aux Out), Bit-1=0(Aux LL) */
408 SCR_VDD_ON,
409 SCR_MCLR_GND_OFF, /* Let CS# float */
410 SCR_VPP_PWM_ON,
411 SCR_VPP_ON, /* Pull CS# high */
412 SCR_BUSY_LED_ON,
413 CMD_CLR_DLOAD_BUFF,
414 CMD_CLR_ULOAD_BUFF,
415 CMD_END_OF_BUFFER
416 };
417
418
419 int spispeed_idx = 0;
420 char *spispeed = extract_programmer_param("spispeed");
421 if (spispeed != NULL) {
422 int i = 0;
423 for (; spispeeds[i].name; i++) {
424 if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
425 spispeed_idx = i;
426 break;
427 }
428 }
429 if (spispeeds[i].name == NULL) {
430 msg_perr("Error: Invalid 'spispeed' value.\n");
431 free(spispeed);
432 return 1;
433 }
434 free(spispeed);
435 }
436
437 int millivolt = 3500;
438 char *voltage = extract_programmer_param("voltage");
439 if (voltage != NULL) {
440 millivolt = parse_voltage(voltage);
441 free(voltage);
442 if (millivolt < 0)
443 return 1;
444 }
445
446 /* Here comes the USB stuff */
447 usb_init();
448 (void)usb_find_busses();
449 (void)usb_find_devices();
Stefan Taunerf31fe842016-02-22 08:59:15 +0000450 const uint16_t vid = devs_pickit2_spi[0].vendor_id;
451 const uint16_t pid = devs_pickit2_spi[0].device_id;
Elyes HAOUASac01baa2018-05-28 16:52:21 +0200452 struct usb_device *dev = get_device_by_vid_pid(vid, pid, usedevice);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000453 if (dev == NULL) {
454 msg_perr("Could not find a PICkit2 on USB!\n");
455 return 1;
456 }
457 msg_pdbg("Found USB device (%04x:%04x).\n", dev->descriptor.idVendor, dev->descriptor.idProduct);
458
459 pickit2_handle = usb_open(dev);
460 int ret = usb_set_configuration(pickit2_handle, 1);
461 if (ret != 0) {
462 msg_perr("Could not set USB device configuration: %i %s\n", ret, usb_strerror());
463 if (usb_close(pickit2_handle) != 0)
464 msg_perr("Could not close USB device!\n");
465 return 1;
466 }
467 ret = usb_claim_interface(pickit2_handle, 0);
468 if (ret != 0) {
469 msg_perr("Could not claim USB device interface %i: %i %s\n", 0, ret, usb_strerror());
470 if (usb_close(pickit2_handle) != 0)
471 msg_perr("Could not close USB device!\n");
472 return 1;
473 }
474
475 if (register_shutdown(pickit2_shutdown, NULL) != 0) {
476 return 1;
477 }
478
479 if (pickit2_get_firmware_version()) {
480 return 1;
481 }
482
483 /* Command Set SPI Speed */
484 if (pickit2_set_spi_speed(spispeed_idx)) {
485 return 1;
486 }
487
488 /* Command Set SPI Voltage */
489 msg_pdbg("Setting voltage to %i mV.\n", millivolt);
490 if (pickit2_set_spi_voltage(millivolt) != 0) {
491 return 1;
492 }
493
494 /* Perform basic setup.
495 * Configure pin directions and logic levels, turn Vdd on, turn busy LED on and clear buffers. */
496 ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)buf, CMD_LENGTH, DFLT_TIMEOUT);
497 if (ret != CMD_LENGTH) {
498 msg_perr("Command Setup failed (%s)!\n", usb_strerror());
499 return 1;
500 }
501
502 register_spi_master(&spi_master_pickit2);
503
504 return 0;
505}