blob: e364b06cc0c102de5b2589c02777355962b1909d [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
Thomas Heijligencc853d82021-05-04 15:32:17 +020049static const struct dev_entry devs_pickit2_spi[] = {
Stefan Taunerf31fe842016-02-22 08:59:15 +000050 {0x04D8, 0x0033, OK, "Microchip", "PICkit 2"},
51
Evgeny Zinoviev83c56b82019-11-05 17:47:43 +030052 {0}
Stefan Taunerf31fe842016-02-22 08:59:15 +000053};
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
Angel Pons75d6ec62022-07-16 12:12:50 +020092static int pickit2_interrupt_transfer(libusb_device_handle *handle, unsigned char endpoint, unsigned char *data)
93{
94 int transferred;
95 return libusb_interrupt_transfer(handle, endpoint, data, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
96}
97
Justin Chevrier66e554b2015-02-08 21:58:10 +000098static int pickit2_get_firmware_version(void)
99{
100 int ret;
101 uint8_t command[CMD_LENGTH] = {CMD_GET_VERSION, CMD_END_OF_BUFFER};
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100102
Angel Pons75d6ec62022-07-16 12:12:50 +0200103 ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command);
Elyes HAOUAS3384fb62019-07-18 14:00:13 +0200104
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200105 if (ret != 0) {
106 msg_perr("Command Get Firmware Version failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000107 return 1;
108 }
109
Angel Pons75d6ec62022-07-16 12:12:50 +0200110 ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_IN, command);
Elyes HAOUAS3384fb62019-07-18 14:00:13 +0200111
112 if (ret != 0) {
113 msg_perr("Command Get Firmware Version failed!\n");
114 return 1;
115 }
116
117 msg_pdbg("PICkit2 Firmware Version: %d.%d\n", (int)command[0], (int)command[1]);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000118 return 0;
119}
120
121static int pickit2_set_spi_voltage(int millivolt)
122{
123 double voltage_selector;
124 switch (millivolt) {
125 case 0:
126 /* Admittedly this one is an assumption. */
127 voltage_selector = 0;
128 break;
129 case 1800:
130 voltage_selector = 1.8;
131 break;
132 case 2500:
133 voltage_selector = 2.5;
134 break;
135 case 3500:
136 voltage_selector = 3.5;
137 break;
138 default:
139 msg_perr("Unknown voltage %i mV! Aborting.\n", millivolt);
140 return 1;
141 }
142 msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000,
143 millivolt % 1000);
144
145 uint8_t command[CMD_LENGTH] = {
146 CMD_SET_VDD,
147 voltage_selector * 2048 + 672,
148 (voltage_selector * 2048 + 672) / 256,
149 voltage_selector * 36,
150 CMD_SET_VPP,
151 0x40,
152 voltage_selector * 18.61,
153 voltage_selector * 13,
154 CMD_END_OF_BUFFER
155 };
Angel Pons75d6ec62022-07-16 12:12:50 +0200156 int ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000157
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200158 if (ret != 0) {
159 msg_perr("Command Set Voltage failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000160 return 1;
161 }
162
163 return 0;
164}
165
166struct pickit2_spispeeds {
167 const char *const name;
168 const int speed;
169};
170
171static const struct pickit2_spispeeds spispeeds[] = {
172 { "1M", 0x1 },
173 { "500k", 0x2 },
174 { "333k", 0x3 },
175 { "250k", 0x4 },
176 { NULL, 0x0 },
177};
178
179static int pickit2_set_spi_speed(unsigned int spispeed_idx)
180{
181 msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed_idx].name);
182
183 uint8_t command[CMD_LENGTH] = {
184 CMD_EXEC_SCRIPT,
185 2,
186 SCR_SET_ICSP_CLK_PERIOD,
187 spispeed_idx,
188 CMD_END_OF_BUFFER
189 };
190
Angel Pons75d6ec62022-07-16 12:12:50 +0200191 int ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000192
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200193 if (ret != 0) {
194 msg_perr("Command Set SPI Speed failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000195 return 1;
196 }
197
198 return 0;
199}
200
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000201static int pickit2_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
Justin Chevrier66e554b2015-02-08 21:58:10 +0000202 const unsigned char *writearr, unsigned char *readarr)
203{
Felix Singer48c3f182022-07-29 03:16:19 +0200204 const unsigned int total_packetsize = writecnt + readcnt + 20;
Justin Chevrier66e554b2015-02-08 21:58:10 +0000205
206 /* Maximum number of bytes per transaction (including command overhead) is 64. Lets play it safe
207 * and always assume the worst case scenario of 20 bytes command overhead.
208 */
Felix Singer48c3f182022-07-29 03:16:19 +0200209 if (total_packetsize > CMD_LENGTH) {
Felix Singerd07c4a42022-07-29 03:09:02 +0200210 msg_perr("\nTotal packetsize (%i) is greater than %i supported, aborting.\n",
Felix Singer48c3f182022-07-29 03:16:19 +0200211 total_packetsize, CMD_LENGTH);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000212 return 1;
213 }
214
215 uint8_t buf[CMD_LENGTH] = {CMD_DOWNLOAD_DATA, writecnt};
Nico Huber519be662018-12-23 20:03:35 +0100216 unsigned int i = 2;
Justin Chevrier66e554b2015-02-08 21:58:10 +0000217 for (; i < writecnt + 2; i++) {
218 buf[i] = writearr[i - 2];
219 }
220
221 buf[i++] = CMD_CLR_ULOAD_BUFF;
222 buf[i++] = CMD_EXEC_SCRIPT;
223
224 /* Determine script length based on number of bytes to be read or written */
225 if (writecnt == 1 && readcnt == 1)
226 buf[i++] = 7;
227 else if (writecnt == 1 || readcnt == 1)
228 buf[i++] = 10;
229 else
230 buf[i++] = 13;
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100231
Justin Chevrier66e554b2015-02-08 21:58:10 +0000232 /* Assert CS# */
233 buf[i++] = SCR_VPP_OFF;
234 buf[i++] = SCR_MCLR_GND_ON;
235
236 buf[i++] = SCR_SPI_WRITE_BUF;
237
238 if (writecnt > 1) {
239 buf[i++] = SCR_LOOP;
240 buf[i++] = 1; /* Loop back one instruction */
241 buf[i++] = writecnt - 1; /* Number of times to loop */
242 }
243
244 if (readcnt)
245 buf[i++] = SCR_SPI_READ_BUF;
246
247 if (readcnt > 1) {
248 buf[i++] = SCR_LOOP;
249 buf[i++] = 1; /* Loop back one instruction */
250 buf[i++] = readcnt - 1; /* Number of times to loop */
251 }
252
253 /* De-assert CS# */
254 buf[i++] = SCR_MCLR_GND_OFF;
255 buf[i++] = SCR_VPP_PWM_ON;
256 buf[i++] = SCR_VPP_ON;
257
258 buf[i++] = CMD_UPLOAD_DATA;
259 buf[i++] = CMD_END_OF_BUFFER;
260
Angel Pons75d6ec62022-07-16 12:12:50 +0200261 int ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, buf);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000262
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200263 if (ret != 0) {
264 msg_perr("Send SPI failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000265 return 1;
266 }
267
268 if (readcnt) {
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200269 int length = 0;
270 ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_IN, buf, CMD_LENGTH, &length, DFLT_TIMEOUT);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000271
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200272 if (length == 0 || ret != 0) {
273 msg_perr("Receive SPI failed\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000274 return 1;
275 }
276
277 /* First byte indicates number of bytes transferred from upload buffer */
278 if (buf[0] != readcnt) {
279 msg_perr("Unexpected number of bytes transferred, expected %i, got %i!\n",
280 readcnt, ret);
281 return 1;
282 }
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100283
Justin Chevrier66e554b2015-02-08 21:58:10 +0000284 /* Actual data starts at byte number two */
285 memcpy(readarr, &buf[1], readcnt);
286 }
287
288 return 0;
289}
290
291/* Copied from dediprog.c */
292/* Might be useful for other USB devices as well. static for now. */
293static int parse_voltage(char *voltage)
294{
295 char *tmp = NULL;
296 int i;
297 int millivolt = 0, fraction = 0;
298
299 if (!voltage || !strlen(voltage)) {
300 msg_perr("Empty voltage= specified.\n");
301 return -1;
302 }
303 millivolt = (int)strtol(voltage, &tmp, 0);
304 voltage = tmp;
305 /* Handle "," and "." as decimal point. Everything after it is assumed
306 * to be in decimal notation.
307 */
308 if ((*voltage == '.') || (*voltage == ',')) {
309 voltage++;
310 for (i = 0; i < 3; i++) {
311 fraction *= 10;
312 /* Don't advance if the current character is invalid,
313 * but continue multiplying.
314 */
315 if ((*voltage < '0') || (*voltage > '9'))
316 continue;
317 fraction += *voltage - '0';
318 voltage++;
319 }
320 /* Throw away remaining digits. */
321 voltage += strspn(voltage, "0123456789");
322 }
323 /* The remaining string must be empty or "mV" or "V". */
324 tolower_string(voltage);
325
326 /* No unit or "V". */
327 if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) {
328 millivolt *= 1000;
329 millivolt += fraction;
330 } else if (!strncmp(voltage, "mv", 2) ||
331 !strncmp(voltage, "millivolt", 9)) {
332 /* No adjustment. fraction is discarded. */
333 } else {
334 /* Garbage at the end of the string. */
335 msg_perr("Garbage voltage= specified.\n");
336 return -1;
337 }
338 return millivolt;
339}
340
341static const struct spi_master spi_master_pickit2 = {
Justin Chevrier66e554b2015-02-08 21:58:10 +0000342 .max_data_read = 40,
343 .max_data_write = 40,
344 .command = pickit2_spi_send_command,
345 .multicommand = default_spi_send_multicommand,
346 .read = default_spi_read,
347 .write_256 = default_spi_write_256,
348 .write_aai = default_spi_write_aai,
349};
350
351static int pickit2_shutdown(void *data)
352{
353 /* Set all pins to float and turn voltages off */
354 uint8_t command[CMD_LENGTH] = {
355 CMD_EXEC_SCRIPT,
356 8,
357 SCR_SET_PINS,
358 3, /* Bit-0=1(PDC In), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
359 SCR_SET_AUX,
360 1, /* Bit-0=1(Aux In), Bit-1=0(Aux LL) */
361 SCR_MCLR_GND_OFF,
362 SCR_VPP_OFF,
363 SCR_VDD_OFF,
364 SCR_BUSY_LED_OFF,
365 CMD_END_OF_BUFFER
366 };
367
Angel Pons75d6ec62022-07-16 12:12:50 +0200368 int ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000369
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200370 if (ret != 0) {
371 msg_perr("Command Shutdown failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000372 ret = 1;
373 }
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200374 if (libusb_release_interface(pickit2_handle, 0) != 0) {
Justin Chevrier66e554b2015-02-08 21:58:10 +0000375 msg_perr("Could not release USB interface!\n");
376 ret = 1;
377 }
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200378 libusb_close(pickit2_handle);
379 libusb_exit(NULL);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000380 return ret;
381}
382
Thomas Heijligencc853d82021-05-04 15:32:17 +0200383static int pickit2_spi_init(void)
Justin Chevrier66e554b2015-02-08 21:58:10 +0000384{
Justin Chevrier66e554b2015-02-08 21:58:10 +0000385 uint8_t buf[CMD_LENGTH] = {
386 CMD_EXEC_SCRIPT,
387 10, /* Script length */
388 SCR_SET_PINS,
389 2, /* Bit-0=0(PDC Out), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
390 SCR_SET_AUX,
391 0, /* Bit-0=0(Aux Out), Bit-1=0(Aux LL) */
392 SCR_VDD_ON,
393 SCR_MCLR_GND_OFF, /* Let CS# float */
394 SCR_VPP_PWM_ON,
395 SCR_VPP_ON, /* Pull CS# high */
396 SCR_BUSY_LED_ON,
397 CMD_CLR_DLOAD_BUFF,
398 CMD_CLR_ULOAD_BUFF,
399 CMD_END_OF_BUFFER
400 };
401
402
403 int spispeed_idx = 0;
404 char *spispeed = extract_programmer_param("spispeed");
405 if (spispeed != NULL) {
406 int i = 0;
407 for (; spispeeds[i].name; i++) {
408 if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
409 spispeed_idx = i;
410 break;
411 }
412 }
413 if (spispeeds[i].name == NULL) {
414 msg_perr("Error: Invalid 'spispeed' value.\n");
415 free(spispeed);
416 return 1;
417 }
418 free(spispeed);
419 }
420
421 int millivolt = 3500;
422 char *voltage = extract_programmer_param("voltage");
423 if (voltage != NULL) {
424 millivolt = parse_voltage(voltage);
425 free(voltage);
426 if (millivolt < 0)
427 return 1;
428 }
429
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200430 if (libusb_init(NULL) < 0) {
431 msg_perr("Couldn't initialize libusb!\n");
432 return -1;
433 }
434
435#if LIBUSB_API_VERSION < 0x01000106
436 libusb_set_debug(NULL, 3);
437#else
438 libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
439#endif
440
Stefan Taunerf31fe842016-02-22 08:59:15 +0000441 const uint16_t vid = devs_pickit2_spi[0].vendor_id;
442 const uint16_t pid = devs_pickit2_spi[0].device_id;
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200443 pickit2_handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
444 if (pickit2_handle == NULL) {
445 msg_perr("Could not open device PICkit2!\n");
446 libusb_exit(NULL);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000447 return 1;
448 }
Justin Chevrier66e554b2015-02-08 21:58:10 +0000449
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200450 if (libusb_set_configuration(pickit2_handle, 1) != 0) {
451 msg_perr("Could not set USB device configuration.\n");
452 libusb_close(pickit2_handle);
453 libusb_exit(NULL);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000454 return 1;
455 }
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200456 if (libusb_claim_interface(pickit2_handle, 0) != 0) {
457 msg_perr("Could not claim USB device interface\n");
458 libusb_close(pickit2_handle);
459 libusb_exit(NULL);
Justin Chevrier66e554b2015-02-08 21:58:10 +0000460 return 1;
461 }
462
463 if (register_shutdown(pickit2_shutdown, NULL) != 0) {
464 return 1;
465 }
466
467 if (pickit2_get_firmware_version()) {
468 return 1;
469 }
470
471 /* Command Set SPI Speed */
472 if (pickit2_set_spi_speed(spispeed_idx)) {
473 return 1;
474 }
475
476 /* Command Set SPI Voltage */
477 msg_pdbg("Setting voltage to %i mV.\n", millivolt);
478 if (pickit2_set_spi_voltage(millivolt) != 0) {
479 return 1;
480 }
481
482 /* Perform basic setup.
483 * Configure pin directions and logic levels, turn Vdd on, turn busy LED on and clear buffers. */
Angel Pons75d6ec62022-07-16 12:12:50 +0200484 if (pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, buf) != 0) {
Thomas Heijligenb221cd72019-04-05 15:08:35 +0200485 msg_perr("Command Setup failed!\n");
Justin Chevrier66e554b2015-02-08 21:58:10 +0000486 return 1;
487 }
488
489 register_spi_master(&spi_master_pickit2);
490
491 return 0;
492}
Thomas Heijligencc853d82021-05-04 15:32:17 +0200493
494const struct programmer_entry programmer_pickit2_spi = {
495 .name = "pickit2_spi",
496 .type = USB,
497 .devs.dev = devs_pickit2_spi,
498 .init = pickit2_spi_init,
499 .map_flash_region = fallback_map,
500 .unmap_flash_region = fallback_unmap,
501 .delay = internal_delay,
502};