xref: /aosp_15_r20/external/flashrom/pickit2_spi.c (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
1 /*
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.
15  */
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 <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <limits.h>
39 #include <errno.h>
40 #include <libusb.h>
41 
42 #include "flash.h"
43 #include "chipdrivers.h"
44 #include "programmer.h"
45 #include "spi.h"
46 
47 static const struct dev_entry devs_pickit2_spi[] = {
48 	{0x04D8, 0x0033, OK, "Microchip", "PICkit 2"},
49 
50 	{0}
51 };
52 
53 struct pickit2_spi_data {
54 	libusb_device_handle *pickit2_handle;
55 };
56 
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 
64 #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 
pickit2_interrupt_transfer(libusb_device_handle * handle,unsigned char endpoint,unsigned char * data)92 static 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 
pickit2_get_firmware_version(libusb_device_handle * pickit2_handle)98 static int pickit2_get_firmware_version(libusb_device_handle *pickit2_handle)
99 {
100 	int ret;
101 	uint8_t command[CMD_LENGTH] = {CMD_GET_VERSION, CMD_END_OF_BUFFER};
102 
103 	ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command);
104 
105 	if (ret != 0) {
106 		msg_perr("Command Get Firmware Version failed!\n");
107 		return 1;
108 	}
109 
110 	ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_IN, command);
111 
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]);
118 	return 0;
119 }
120 
pickit2_set_spi_voltage(libusb_device_handle * pickit2_handle,int millivolt)121 static int pickit2_set_spi_voltage(libusb_device_handle *pickit2_handle, 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 	};
156 	int ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command);
157 
158 	if (ret != 0) {
159 		msg_perr("Command Set Voltage failed!\n");
160 		return 1;
161 	}
162 
163 	return 0;
164 }
165 
166 struct pickit2_spispeeds {
167 	const char *const name;
168 	const int speed;
169 };
170 
171 static const struct pickit2_spispeeds spispeeds[] = {
172 	{ "1M",		0x1 },
173 	{ "500k",	0x2 },
174 	{ "333k",	0x3 },
175 	{ "250k",	0x4 },
176 	{ NULL,		0x0 },
177 };
178 
pickit2_set_spi_speed(libusb_device_handle * pickit2_handle,unsigned int spispeed_idx)179 static int pickit2_set_spi_speed(libusb_device_handle *pickit2_handle, 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 
191 	int ret = pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command);
192 
193 	if (ret != 0) {
194 		msg_perr("Command Set SPI Speed failed!\n");
195 		return 1;
196 	}
197 
198 	return 0;
199 }
200 
pickit2_spi_send_command(const struct flashctx * flash,unsigned int writecnt,unsigned int readcnt,const unsigned char * writearr,unsigned char * readarr)201 static int pickit2_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
202 				     const unsigned char *writearr, unsigned char *readarr)
203 {
204 	struct pickit2_spi_data *pickit2_data = flash->mst->spi.data;
205 	const unsigned int total_packetsize = writecnt + readcnt + 20;
206 
207 	/* Maximum number of bytes per transaction (including command overhead) is 64. Lets play it safe
208 	 * and always assume the worst case scenario of 20 bytes command overhead.
209 	 */
210 	if (total_packetsize > CMD_LENGTH) {
211 		msg_perr("\nTotal packetsize (%i) is greater than %i supported, aborting.\n",
212 			 total_packetsize, CMD_LENGTH);
213 		return 1;
214 	}
215 
216 	uint8_t buf[CMD_LENGTH] = {CMD_DOWNLOAD_DATA, writecnt};
217 	unsigned int i = 2;
218 	for (; i < writecnt + 2; i++) {
219 		buf[i] = writearr[i - 2];
220 	}
221 
222 	buf[i++] = CMD_CLR_ULOAD_BUFF;
223 	buf[i++] = CMD_EXEC_SCRIPT;
224 
225 	/* Determine script length based on number of bytes to be read or written */
226 	if (writecnt == 1 && readcnt == 1)
227 		buf[i++] = 7;
228 	else if (writecnt == 1 || readcnt == 1)
229 		buf[i++] = 10;
230 	else
231 		buf[i++] = 13;
232 
233 	/* Assert CS# */
234 	buf[i++] = SCR_VPP_OFF;
235 	buf[i++] = SCR_MCLR_GND_ON;
236 
237 	buf[i++] = SCR_SPI_WRITE_BUF;
238 
239 	if (writecnt > 1) {
240 		buf[i++] = SCR_LOOP;
241 		buf[i++] = 1; /* Loop back one instruction */
242 		buf[i++] = writecnt - 1; /* Number of times to loop */
243 	}
244 
245 	if (readcnt)
246 		buf[i++] = SCR_SPI_READ_BUF;
247 
248 	if (readcnt > 1) {
249 		buf[i++] = SCR_LOOP;
250 		buf[i++] = 1; /* Loop back one instruction */
251 		buf[i++] = readcnt - 1; /* Number of times to loop */
252 	}
253 
254 	/* De-assert CS# */
255 	buf[i++] = SCR_MCLR_GND_OFF;
256 	buf[i++] = SCR_VPP_PWM_ON;
257 	buf[i++] = SCR_VPP_ON;
258 
259 	buf[i++] = CMD_UPLOAD_DATA;
260 	buf[i++] = CMD_END_OF_BUFFER;
261 
262 	int ret = pickit2_interrupt_transfer(pickit2_data->pickit2_handle, ENDPOINT_OUT, buf);
263 
264 	if (ret != 0) {
265 		msg_perr("Send SPI failed!\n");
266 		return 1;
267 	}
268 
269 	if (readcnt) {
270 		int length = 0;
271 		ret = libusb_interrupt_transfer(pickit2_data->pickit2_handle,
272 					ENDPOINT_IN, buf, CMD_LENGTH, &length, DFLT_TIMEOUT);
273 
274 		if (length == 0 || ret != 0) {
275 			msg_perr("Receive SPI failed\n");
276 			return 1;
277 		}
278 
279 		/* First byte indicates number of bytes transferred from upload buffer */
280 		if (buf[0] != readcnt) {
281 			msg_perr("Unexpected number of bytes transferred, expected %i, got %i!\n",
282 				 readcnt, ret);
283 			return 1;
284 		}
285 
286 		/* Actual data starts at byte number two */
287 		memcpy(readarr, &buf[1], readcnt);
288 	}
289 
290 	return 0;
291 }
292 
293 /* Copied from dediprog.c */
294 /* Might be useful for other USB devices as well. static for now. */
parse_voltage(char * voltage)295 static int parse_voltage(char *voltage)
296 {
297 	char *tmp = NULL;
298 	int i;
299 	int millivolt = 0, fraction = 0;
300 
301 	if (!voltage || !strlen(voltage)) {
302 		msg_perr("Empty voltage= specified.\n");
303 		return -1;
304 	}
305 	millivolt = (int)strtol(voltage, &tmp, 0);
306 	voltage = tmp;
307 	/* Handle "," and "." as decimal point. Everything after it is assumed
308 	 * to be in decimal notation.
309 	 */
310 	if ((*voltage == '.') || (*voltage == ',')) {
311 		voltage++;
312 		for (i = 0; i < 3; i++) {
313 			fraction *= 10;
314 			/* Don't advance if the current character is invalid,
315 			 * but continue multiplying.
316 			 */
317 			if ((*voltage < '0') || (*voltage > '9'))
318 				continue;
319 			fraction += *voltage - '0';
320 			voltage++;
321 		}
322 		/* Throw away remaining digits. */
323 		voltage += strspn(voltage, "0123456789");
324 	}
325 	/* The remaining string must be empty or "mV" or "V". */
326 	tolower_string(voltage);
327 
328 	/* No unit or "V". */
329 	if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) {
330 		millivolt *= 1000;
331 		millivolt += fraction;
332 	} else if (!strncmp(voltage, "mv", 2) ||
333 		   !strncmp(voltage, "millivolt", 9)) {
334 		/* No adjustment. fraction is discarded. */
335 	} else {
336 		/* Garbage at the end of the string. */
337 		msg_perr("Garbage voltage= specified.\n");
338 		return -1;
339 	}
340 	return millivolt;
341 }
342 
pickit2_shutdown(void * data)343 static int pickit2_shutdown(void *data)
344 {
345 	struct pickit2_spi_data *pickit2_data = data;
346 
347 	/* Set all pins to float and turn voltages off */
348 	uint8_t command[CMD_LENGTH] = {
349 		CMD_EXEC_SCRIPT,
350 		8,
351 		SCR_SET_PINS,
352 		3, /* Bit-0=1(PDC In), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
353 		SCR_SET_AUX,
354 		1, /* Bit-0=1(Aux In), Bit-1=0(Aux LL) */
355 		SCR_MCLR_GND_OFF,
356 		SCR_VPP_OFF,
357 		SCR_VDD_OFF,
358 		SCR_BUSY_LED_OFF,
359 		CMD_END_OF_BUFFER
360 	};
361 
362 	int ret = pickit2_interrupt_transfer(pickit2_data->pickit2_handle, ENDPOINT_OUT, command);
363 
364 	if (ret != 0) {
365 		msg_perr("Command Shutdown failed!\n");
366 		ret = 1;
367 	}
368 	if (libusb_release_interface(pickit2_data->pickit2_handle, 0) != 0) {
369 		msg_perr("Could not release USB interface!\n");
370 		ret = 1;
371 	}
372 	libusb_close(pickit2_data->pickit2_handle);
373 	libusb_exit(NULL);
374 
375 	free(data);
376 	return ret;
377 }
378 
379 static const struct spi_master spi_master_pickit2 = {
380 	.max_data_read	= 40,
381 	.max_data_write	= 40,
382 	.command	= pickit2_spi_send_command,
383 	.read		= default_spi_read,
384 	.write_256	= default_spi_write_256,
385 	.shutdown	= pickit2_shutdown,
386 };
387 
pickit2_spi_init(const struct programmer_cfg * cfg)388 static int pickit2_spi_init(const struct programmer_cfg *cfg)
389 {
390 	uint8_t buf[CMD_LENGTH] = {
391 		CMD_EXEC_SCRIPT,
392 		10,			/* Script length */
393 		SCR_SET_PINS,
394 		2, /* Bit-0=0(PDC Out), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
395 		SCR_SET_AUX,
396 		0, /* Bit-0=0(Aux Out), Bit-1=0(Aux LL) */
397 		SCR_VDD_ON,
398 		SCR_MCLR_GND_OFF,	/* Let CS# float */
399 		SCR_VPP_PWM_ON,
400 		SCR_VPP_ON,		/* Pull CS# high */
401 		SCR_BUSY_LED_ON,
402 		CMD_CLR_DLOAD_BUFF,
403 		CMD_CLR_ULOAD_BUFF,
404 		CMD_END_OF_BUFFER
405 	};
406 
407 	libusb_device_handle *pickit2_handle;
408 	struct pickit2_spi_data *pickit2_data;
409 	int spispeed_idx = 0;
410 	char *param_str;
411 
412 	param_str = extract_programmer_param_str(cfg, "spispeed");
413 	if (param_str != NULL) {
414 		int i = 0;
415 		for (; spispeeds[i].name; i++) {
416 			if (strcasecmp(spispeeds[i].name, param_str) == 0) {
417 				spispeed_idx = i;
418 				break;
419 			}
420 		}
421 		if (spispeeds[i].name == NULL) {
422 			msg_perr("Error: Invalid 'spispeed' value.\n");
423 			free(param_str);
424 			return 1;
425 		}
426 		free(param_str);
427 	}
428 
429 	int millivolt = 3500;
430 	param_str = extract_programmer_param_str(cfg, "voltage");
431 	if (param_str != NULL) {
432 		millivolt = parse_voltage(param_str);
433 		free(param_str);
434 		if (millivolt < 0)
435 			return 1;
436 	}
437 
438 	if (libusb_init(NULL) < 0) {
439 		msg_perr("Couldn't initialize libusb!\n");
440 		return -1;
441 	}
442 
443 #if LIBUSB_API_VERSION < 0x01000106
444 	libusb_set_debug(NULL, 3);
445 #else
446 	libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
447 #endif
448 
449 	const uint16_t vid = devs_pickit2_spi[0].vendor_id;
450 	const uint16_t pid = devs_pickit2_spi[0].device_id;
451 	pickit2_handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
452 	if (pickit2_handle == NULL) {
453 		msg_perr("Could not open device PICkit2!\n");
454 		libusb_exit(NULL);
455 		return 1;
456 	}
457 
458 	if (libusb_set_configuration(pickit2_handle, 1) != 0) {
459 		msg_perr("Could not set USB device configuration.\n");
460 		libusb_close(pickit2_handle);
461 		libusb_exit(NULL);
462 		return 1;
463 	}
464 	if (libusb_claim_interface(pickit2_handle, 0) != 0) {
465 		msg_perr("Could not claim USB device interface\n");
466 		libusb_close(pickit2_handle);
467 		libusb_exit(NULL);
468 		return 1;
469 	}
470 
471 	pickit2_data = calloc(1, sizeof(*pickit2_data));
472 	if (!pickit2_data) {
473 		msg_perr("Unable to allocate space for SPI master data\n");
474 		libusb_close(pickit2_handle);
475 		libusb_exit(NULL);
476 		return 1;
477 	}
478 	pickit2_data->pickit2_handle = pickit2_handle;
479 
480 	if (pickit2_get_firmware_version(pickit2_handle))
481 		goto init_err_cleanup_exit;
482 
483 	/* Command Set SPI Speed */
484 	if (pickit2_set_spi_speed(pickit2_handle, spispeed_idx))
485 		goto init_err_cleanup_exit;
486 
487 	/* Command Set SPI Voltage */
488 	msg_pdbg("Setting voltage to %i mV.\n", millivolt);
489 	if (pickit2_set_spi_voltage(pickit2_handle, millivolt) != 0)
490 		goto init_err_cleanup_exit;
491 
492 	/* Perform basic setup.
493 	 * Configure pin directions and logic levels, turn Vdd on, turn busy LED on and clear buffers. */
494 	if (pickit2_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, buf) != 0) {
495 		msg_perr("Command Setup failed!\n");
496 		goto init_err_cleanup_exit;
497 	}
498 
499 	return register_spi_master(&spi_master_pickit2, pickit2_data);
500 
501 init_err_cleanup_exit:
502 	pickit2_shutdown(pickit2_data);
503 	return 1;
504 }
505 
506 const struct programmer_entry programmer_pickit2_spi = {
507 	.name			= "pickit2_spi",
508 	.type			= USB,
509 	.devs.dev		= devs_pickit2_spi,
510 	.init			= pickit2_spi_init,
511 };
512