blob: 6d9b28f6ce9a4cce32d01d2a26d78ff397bfb2d4 [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>
Thomas Heijligenb221cd72019-04-05 15:08:35 +020042#include <libusb.h>
Justin Chevrier66e554b2015-02-08 21:58:10 +000043
44#include "flash.h"
45#include "chipdrivers.h"
46#include "programmer.h"
47#include "spi.h"
48
Stefan Taunerf31fe842016-02-22 08:59:15 +000049const struct dev_entry devs_pickit2_spi[] = {
50 {0x04D8, 0x0033, OK, "Microchip", "PICkit 2"},
51
52 {}
53};
54
Thomas Heijligenb221cd72019-04-05 15:08:35 +020055static libusb_device_handle *pickit2_handle;
Justin Chevrier66e554b2015-02-08 21:58:10 +000056
57/* Default USB transaction timeout in ms */
58#define DFLT_TIMEOUT 10000
59
60#define CMD_LENGTH 64
61#define ENDPOINT_OUT 0x01
62#define ENDPOINT_IN 0x81
63
Justin Chevrier66e554b2015-02-08 21:58:10 +000064#define CMD_GET_VERSION 0x76
65#define CMD_SET_VDD 0xA0
66#define CMD_SET_VPP 0xA1
67#define CMD_READ_VDD_VPP 0xA3
68#define CMD_EXEC_SCRIPT 0xA6
69#define CMD_CLR_DLOAD_BUFF 0xA7
70#define CMD_DOWNLOAD_DATA 0xA8
71#define CMD_CLR_ULOAD_BUFF 0xA9
72#define CMD_UPLOAD_DATA 0xAA
73#define CMD_END_OF_BUFFER 0xAD
74
75#define SCR_SPI_READ_BUF 0xC5
76#define SCR_SPI_WRITE_BUF 0xC6
77#define SCR_SET_AUX 0xCF
78#define SCR_LOOP 0xE9
79#define SCR_SET_ICSP_CLK_PERIOD 0xEA
80#define SCR_SET_PINS 0xF3
81#define SCR_BUSY_LED_OFF 0xF4
82#define SCR_BUSY_LED_ON 0xF5
83#define SCR_MCLR_GND_OFF 0xF6
84#define SCR_MCLR_GND_ON 0xF7
85#define SCR_VPP_PWM_OFF 0xF8
86#define SCR_VPP_PWM_ON 0xF9
87#define SCR_VPP_OFF 0xFA
88#define SCR_VPP_ON 0xFB
89#define SCR_VDD_OFF 0xFE
90#define SCR_VDD_ON 0xFF
91
Justin Chevrier66e554b2015-02-08 21:58:10 +000092static int pickit2_get_firmware_version(void)
93{
94 int ret;
95 uint8_t command[CMD_LENGTH] = {CMD_GET_VERSION, CMD_END_OF_BUFFER};
96
Thomas Heijligenb221cd72019-04-05 15:08:35 +020097 int transferred;
98 ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
99 ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_IN, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100100
Justin Chevrier66e554b2015-02-08 21:58:10 +0000101 msg_pdbg("PICkit2 Firmware Version: %d.%d\n", (int)command[0], (int)command[1]);
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200102 if (ret != 0) {
103 msg_perr("Command Get Firmware Version failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000104 return 1;
105 }
106
107 return 0;
108}
109
110static int pickit2_set_spi_voltage(int millivolt)
111{
112 double voltage_selector;
113 switch (millivolt) {
114 case 0:
115 /* Admittedly this one is an assumption. */
116 voltage_selector = 0;
117 break;
118 case 1800:
119 voltage_selector = 1.8;
120 break;
121 case 2500:
122 voltage_selector = 2.5;
123 break;
124 case 3500:
125 voltage_selector = 3.5;
126 break;
127 default:
128 msg_perr("Unknown voltage %i mV! Aborting.\n", millivolt);
129 return 1;
130 }
131 msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000,
132 millivolt % 1000);
133
134 uint8_t command[CMD_LENGTH] = {
135 CMD_SET_VDD,
136 voltage_selector * 2048 + 672,
137 (voltage_selector * 2048 + 672) / 256,
138 voltage_selector * 36,
139 CMD_SET_VPP,
140 0x40,
141 voltage_selector * 18.61,
142 voltage_selector * 13,
143 CMD_END_OF_BUFFER
144 };
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200145 int transferred;
146 int ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000147
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200148 if (ret != 0) {
149 msg_perr("Command Set Voltage failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000150 return 1;
151 }
152
153 return 0;
154}
155
156struct pickit2_spispeeds {
157 const char *const name;
158 const int speed;
159};
160
161static const struct pickit2_spispeeds spispeeds[] = {
162 { "1M", 0x1 },
163 { "500k", 0x2 },
164 { "333k", 0x3 },
165 { "250k", 0x4 },
166 { NULL, 0x0 },
167};
168
169static int pickit2_set_spi_speed(unsigned int spispeed_idx)
170{
171 msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed_idx].name);
172
173 uint8_t command[CMD_LENGTH] = {
174 CMD_EXEC_SCRIPT,
175 2,
176 SCR_SET_ICSP_CLK_PERIOD,
177 spispeed_idx,
178 CMD_END_OF_BUFFER
179 };
180
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200181 int transferred;
182 int ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000183
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200184 if (ret != 0) {
185 msg_perr("Command Set SPI Speed failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000186 return 1;
187 }
188
189 return 0;
190}
191
192static int pickit2_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
193 const unsigned char *writearr, unsigned char *readarr)
194{
195
196 /* Maximum number of bytes per transaction (including command overhead) is 64. Lets play it safe
197 * and always assume the worst case scenario of 20 bytes command overhead.
198 */
199 if (writecnt + readcnt + 20 > CMD_LENGTH) {
200 msg_perr("\nTotal packetsize (%i) is greater than 64 supported, aborting.\n",
201 writecnt + readcnt + 20);
202 return 1;
203 }
204
205 uint8_t buf[CMD_LENGTH] = {CMD_DOWNLOAD_DATA, writecnt};
206 int i = 2;
207 for (; i < writecnt + 2; i++) {
208 buf[i] = writearr[i - 2];
209 }
210
211 buf[i++] = CMD_CLR_ULOAD_BUFF;
212 buf[i++] = CMD_EXEC_SCRIPT;
213
214 /* Determine script length based on number of bytes to be read or written */
215 if (writecnt == 1 && readcnt == 1)
216 buf[i++] = 7;
217 else if (writecnt == 1 || readcnt == 1)
218 buf[i++] = 10;
219 else
220 buf[i++] = 13;
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100221
Justin Chevrier66e554b2015-02-08 21:58:10 +0000222 /* Assert CS# */
223 buf[i++] = SCR_VPP_OFF;
224 buf[i++] = SCR_MCLR_GND_ON;
225
226 buf[i++] = SCR_SPI_WRITE_BUF;
227
228 if (writecnt > 1) {
229 buf[i++] = SCR_LOOP;
230 buf[i++] = 1; /* Loop back one instruction */
231 buf[i++] = writecnt - 1; /* Number of times to loop */
232 }
233
234 if (readcnt)
235 buf[i++] = SCR_SPI_READ_BUF;
236
237 if (readcnt > 1) {
238 buf[i++] = SCR_LOOP;
239 buf[i++] = 1; /* Loop back one instruction */
240 buf[i++] = readcnt - 1; /* Number of times to loop */
241 }
242
243 /* De-assert CS# */
244 buf[i++] = SCR_MCLR_GND_OFF;
245 buf[i++] = SCR_VPP_PWM_ON;
246 buf[i++] = SCR_VPP_ON;
247
248 buf[i++] = CMD_UPLOAD_DATA;
249 buf[i++] = CMD_END_OF_BUFFER;
250
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200251 int transferred;
252 int ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, buf, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000253
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200254 if (ret != 0) {
255 msg_perr("Send SPI failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000256 return 1;
257 }
258
259 if (readcnt) {
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200260 int length = 0;
261 ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_IN, buf, CMD_LENGTH, &length, DFLT_TIMEOUT);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000262
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200263 if (length == 0 || ret != 0) {
264 msg_perr("Receive SPI failed\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000265 return 1;
266 }
267
268 /* First byte indicates number of bytes transferred from upload buffer */
269 if (buf[0] != readcnt) {
270 msg_perr("Unexpected number of bytes transferred, expected %i, got %i!\n",
271 readcnt, ret);
272 return 1;
273 }
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100274
Justin Chevrier66e554b2015-02-08 21:58:10 +0000275 /* Actual data starts at byte number two */
276 memcpy(readarr, &buf[1], readcnt);
277 }
278
279 return 0;
280}
281
282/* Copied from dediprog.c */
283/* Might be useful for other USB devices as well. static for now. */
284static int parse_voltage(char *voltage)
285{
286 char *tmp = NULL;
287 int i;
288 int millivolt = 0, fraction = 0;
289
290 if (!voltage || !strlen(voltage)) {
291 msg_perr("Empty voltage= specified.\n");
292 return -1;
293 }
294 millivolt = (int)strtol(voltage, &tmp, 0);
295 voltage = tmp;
296 /* Handle "," and "." as decimal point. Everything after it is assumed
297 * to be in decimal notation.
298 */
299 if ((*voltage == '.') || (*voltage == ',')) {
300 voltage++;
301 for (i = 0; i < 3; i++) {
302 fraction *= 10;
303 /* Don't advance if the current character is invalid,
304 * but continue multiplying.
305 */
306 if ((*voltage < '0') || (*voltage > '9'))
307 continue;
308 fraction += *voltage - '0';
309 voltage++;
310 }
311 /* Throw away remaining digits. */
312 voltage += strspn(voltage, "0123456789");
313 }
314 /* The remaining string must be empty or "mV" or "V". */
315 tolower_string(voltage);
316
317 /* No unit or "V". */
318 if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) {
319 millivolt *= 1000;
320 millivolt += fraction;
321 } else if (!strncmp(voltage, "mv", 2) ||
322 !strncmp(voltage, "millivolt", 9)) {
323 /* No adjustment. fraction is discarded. */
324 } else {
325 /* Garbage at the end of the string. */
326 msg_perr("Garbage voltage= specified.\n");
327 return -1;
328 }
329 return millivolt;
330}
331
332static const struct spi_master spi_master_pickit2 = {
Justin Chevrier66e554b2015-02-08 21:58:10 +0000333 .max_data_read = 40,
334 .max_data_write = 40,
335 .command = pickit2_spi_send_command,
336 .multicommand = default_spi_send_multicommand,
337 .read = default_spi_read,
338 .write_256 = default_spi_write_256,
339 .write_aai = default_spi_write_aai,
340};
341
342static int pickit2_shutdown(void *data)
343{
344 /* Set all pins to float and turn voltages off */
345 uint8_t command[CMD_LENGTH] = {
346 CMD_EXEC_SCRIPT,
347 8,
348 SCR_SET_PINS,
349 3, /* Bit-0=1(PDC In), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
350 SCR_SET_AUX,
351 1, /* Bit-0=1(Aux In), Bit-1=0(Aux LL) */
352 SCR_MCLR_GND_OFF,
353 SCR_VPP_OFF,
354 SCR_VDD_OFF,
355 SCR_BUSY_LED_OFF,
356 CMD_END_OF_BUFFER
357 };
358
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200359 int transferred;
360 int ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000361
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200362 if (ret != 0) {
363 msg_perr("Command Shutdown failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000364 ret = 1;
365 }
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200366 if (libusb_release_interface(pickit2_handle, 0) != 0) {
Justin Chevrier66e554b2015-02-08 21:58:10 +0000367 msg_perr("Could not release USB interface!\n");
368 ret = 1;
369 }
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200370 libusb_close(pickit2_handle);
371 libusb_exit(NULL);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000372 return ret;
373}
374
375int pickit2_spi_init(void)
376{
Justin Chevrier66e554b2015-02-08 21:58:10 +0000377 uint8_t buf[CMD_LENGTH] = {
378 CMD_EXEC_SCRIPT,
379 10, /* Script length */
380 SCR_SET_PINS,
381 2, /* Bit-0=0(PDC Out), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
382 SCR_SET_AUX,
383 0, /* Bit-0=0(Aux Out), Bit-1=0(Aux LL) */
384 SCR_VDD_ON,
385 SCR_MCLR_GND_OFF, /* Let CS# float */
386 SCR_VPP_PWM_ON,
387 SCR_VPP_ON, /* Pull CS# high */
388 SCR_BUSY_LED_ON,
389 CMD_CLR_DLOAD_BUFF,
390 CMD_CLR_ULOAD_BUFF,
391 CMD_END_OF_BUFFER
392 };
393
394
395 int spispeed_idx = 0;
396 char *spispeed = extract_programmer_param("spispeed");
397 if (spispeed != NULL) {
398 int i = 0;
399 for (; spispeeds[i].name; i++) {
400 if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
401 spispeed_idx = i;
402 break;
403 }
404 }
405 if (spispeeds[i].name == NULL) {
406 msg_perr("Error: Invalid 'spispeed' value.\n");
407 free(spispeed);
408 return 1;
409 }
410 free(spispeed);
411 }
412
413 int millivolt = 3500;
414 char *voltage = extract_programmer_param("voltage");
415 if (voltage != NULL) {
416 millivolt = parse_voltage(voltage);
417 free(voltage);
418 if (millivolt < 0)
419 return 1;
420 }
421
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200422 if (libusb_init(NULL) < 0) {
423 msg_perr("Couldn't initialize libusb!\n");
424 return -1;
425 }
426
427#if LIBUSB_API_VERSION < 0x01000106
428 libusb_set_debug(NULL, 3);
429#else
430 libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
431#endif
432
Stefan Taunerf31fe842016-02-22 08:59:15 +0000433 const uint16_t vid = devs_pickit2_spi[0].vendor_id;
434 const uint16_t pid = devs_pickit2_spi[0].device_id;
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200435 pickit2_handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
436 if (pickit2_handle == NULL) {
437 msg_perr("Could not open device PICkit2!\n");
438 libusb_exit(NULL);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000439 return 1;
440 }
Justin Chevrier66e554b2015-02-08 21:58:10 +0000441
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200442 if (libusb_set_configuration(pickit2_handle, 1) != 0) {
443 msg_perr("Could not set USB device configuration.\n");
444 libusb_close(pickit2_handle);
445 libusb_exit(NULL);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000446 return 1;
447 }
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200448 if (libusb_claim_interface(pickit2_handle, 0) != 0) {
449 msg_perr("Could not claim USB device interface\n");
450 libusb_close(pickit2_handle);
451 libusb_exit(NULL);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000452 return 1;
453 }
454
455 if (register_shutdown(pickit2_shutdown, NULL) != 0) {
456 return 1;
457 }
458
459 if (pickit2_get_firmware_version()) {
460 return 1;
461 }
462
463 /* Command Set SPI Speed */
464 if (pickit2_set_spi_speed(spispeed_idx)) {
465 return 1;
466 }
467
468 /* Command Set SPI Voltage */
469 msg_pdbg("Setting voltage to %i mV.\n", millivolt);
470 if (pickit2_set_spi_voltage(millivolt) != 0) {
471 return 1;
472 }
473
474 /* Perform basic setup.
475 * Configure pin directions and logic levels, turn Vdd on, turn busy LED on and clear buffers. */
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200476 int transferred;
477 if (libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, buf, CMD_LENGTH, &transferred, DFLT_TIMEOUT) != 0) {
478 msg_perr("Command Setup failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000479 return 1;
480 }
481
482 register_spi_master(&spi_master_pickit2);
483
484 return 0;
485}