mirror of
				https://review.coreboot.org/flashrom.git
				synced 2025-10-31 05:10:41 +01:00 
			
		
		
		
	 519be66fc5
			
		
	
	519be66fc5
	
	
	
		
			
			Mostly by changing to `unsigned` types where applicable, sometimes `signed` types, and casting as a last resort. Change-Id: I08895543ffb7a48058bcf91ef6500ca113f2d305 Signed-off-by: Nico Huber <nico.h@gmx.de> Reviewed-on: https://review.coreboot.org/c/flashrom/+/30409 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Jacob Garber <jgarber1@ualberta.ca>
		
			
				
	
	
		
			492 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			492 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This file is part of the flashrom project.
 | |
|  *
 | |
|  * Copyright (C) 2010 Carl-Daniel Hailfinger
 | |
|  * Copyright (C) 2014 Justin Chevrier
 | |
|  *
 | |
|  * 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.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Connections are as follows:
 | |
|  *
 | |
|  *      +------+-----+----------+
 | |
|  *      | SPI  | Pin | PICkit2  |
 | |
|  *      +------+-----+----------+
 | |
|  *      | /CS  | 1   | VPP/MCLR |
 | |
|  *      | VCC  | 2   | VDD      |
 | |
|  *      | GND  | 3   | GND      |
 | |
|  *      | MISO | 4   | PGD      |
 | |
|  *      | SCLK | 5   | PDC      |
 | |
|  *      | MOSI | 6   | AUX      |
 | |
|  *      +------+-----+----------+
 | |
|  *
 | |
|  * Inspiration and some specifics of the interface came via the AVRDude
 | |
|  * PICkit2 code: https://github.com/steve-m/avrdude/blob/master/pickit2.c
 | |
|  */
 | |
| 
 | |
| #include "platform.h"
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <limits.h>
 | |
| #include <errno.h>
 | |
| #include <libusb.h>
 | |
| 
 | |
| #include "flash.h"
 | |
| #include "chipdrivers.h"
 | |
| #include "programmer.h"
 | |
| #include "spi.h"
 | |
| 
 | |
| const struct dev_entry devs_pickit2_spi[] = {
 | |
| 	{0x04D8, 0x0033, OK, "Microchip", "PICkit 2"},
 | |
| 
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| static libusb_device_handle *pickit2_handle;
 | |
| 
 | |
| /* Default USB transaction timeout in ms */
 | |
| #define DFLT_TIMEOUT            10000
 | |
| 
 | |
| #define CMD_LENGTH              64
 | |
| #define ENDPOINT_OUT            0x01
 | |
| #define ENDPOINT_IN             0x81
 | |
| 
 | |
| #define CMD_GET_VERSION         0x76
 | |
| #define CMD_SET_VDD             0xA0
 | |
| #define CMD_SET_VPP             0xA1
 | |
| #define CMD_READ_VDD_VPP        0xA3
 | |
| #define CMD_EXEC_SCRIPT         0xA6
 | |
| #define CMD_CLR_DLOAD_BUFF      0xA7
 | |
| #define CMD_DOWNLOAD_DATA       0xA8
 | |
| #define CMD_CLR_ULOAD_BUFF      0xA9
 | |
| #define CMD_UPLOAD_DATA         0xAA
 | |
| #define CMD_END_OF_BUFFER       0xAD
 | |
| 
 | |
| #define SCR_SPI_READ_BUF        0xC5
 | |
| #define SCR_SPI_WRITE_BUF       0xC6
 | |
| #define SCR_SET_AUX             0xCF
 | |
| #define SCR_LOOP                0xE9
 | |
| #define SCR_SET_ICSP_CLK_PERIOD 0xEA
 | |
| #define SCR_SET_PINS            0xF3
 | |
| #define SCR_BUSY_LED_OFF        0xF4
 | |
| #define SCR_BUSY_LED_ON         0xF5
 | |
| #define SCR_MCLR_GND_OFF        0xF6
 | |
| #define SCR_MCLR_GND_ON         0xF7
 | |
| #define SCR_VPP_PWM_OFF         0xF8
 | |
| #define SCR_VPP_PWM_ON          0xF9
 | |
| #define SCR_VPP_OFF             0xFA
 | |
| #define SCR_VPP_ON              0xFB
 | |
| #define SCR_VDD_OFF             0xFE
 | |
| #define SCR_VDD_ON              0xFF
 | |
| 
 | |
| static int pickit2_get_firmware_version(void)
 | |
| {
 | |
| 	int ret;
 | |
| 	uint8_t command[CMD_LENGTH] = {CMD_GET_VERSION, CMD_END_OF_BUFFER};
 | |
| 	int transferred;
 | |
| 
 | |
| 	ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		msg_perr("Command Get Firmware Version failed!\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_IN, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		msg_perr("Command Get Firmware Version failed!\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	msg_pdbg("PICkit2 Firmware Version: %d.%d\n", (int)command[0], (int)command[1]);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int pickit2_set_spi_voltage(int millivolt)
 | |
| {
 | |
| 	double voltage_selector;
 | |
| 	switch (millivolt) {
 | |
| 	case 0:
 | |
| 		/* Admittedly this one is an assumption. */
 | |
| 		voltage_selector = 0;
 | |
| 		break;
 | |
| 	case 1800:
 | |
| 		voltage_selector = 1.8;
 | |
| 		break;
 | |
| 	case 2500:
 | |
| 		voltage_selector = 2.5;
 | |
| 		break;
 | |
| 	case 3500:
 | |
| 		voltage_selector = 3.5;
 | |
| 		break;
 | |
| 	default:
 | |
| 		msg_perr("Unknown voltage %i mV! Aborting.\n", millivolt);
 | |
| 		return 1;
 | |
| 	}
 | |
| 	msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000,
 | |
| 		 millivolt % 1000);
 | |
| 
 | |
| 	uint8_t command[CMD_LENGTH] = {
 | |
| 		CMD_SET_VDD,
 | |
| 		voltage_selector * 2048 + 672,
 | |
| 		(voltage_selector * 2048 + 672) / 256,
 | |
| 		voltage_selector * 36,
 | |
| 		CMD_SET_VPP,
 | |
| 		0x40,
 | |
| 		voltage_selector * 18.61,
 | |
| 		voltage_selector * 13,
 | |
| 		CMD_END_OF_BUFFER
 | |
| 	};
 | |
| 	int transferred;
 | |
| 	int ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		msg_perr("Command Set Voltage failed!\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| struct pickit2_spispeeds {
 | |
| 	const char *const name;
 | |
| 	const int speed;
 | |
| };
 | |
| 
 | |
| static const struct pickit2_spispeeds spispeeds[] = {
 | |
| 	{ "1M",		0x1 },
 | |
| 	{ "500k",	0x2 },
 | |
| 	{ "333k",	0x3 },
 | |
| 	{ "250k",	0x4 },
 | |
| 	{ NULL,		0x0 },
 | |
| };
 | |
| 
 | |
| static int pickit2_set_spi_speed(unsigned int spispeed_idx)
 | |
| {
 | |
| 	msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed_idx].name);
 | |
| 
 | |
| 	uint8_t command[CMD_LENGTH] = {
 | |
| 		CMD_EXEC_SCRIPT,
 | |
| 		2,
 | |
| 		SCR_SET_ICSP_CLK_PERIOD,
 | |
| 		spispeed_idx,
 | |
| 		CMD_END_OF_BUFFER
 | |
| 	};
 | |
| 
 | |
| 	int transferred;
 | |
| 	int ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		msg_perr("Command Set SPI Speed failed!\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int pickit2_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
 | |
| 				     const unsigned char *writearr, unsigned char *readarr)
 | |
| {
 | |
| 
 | |
| 	/* Maximum number of bytes per transaction (including command overhead) is 64. Lets play it safe
 | |
| 	 * and always assume the worst case scenario of 20 bytes command overhead.
 | |
| 	 */
 | |
| 	if (writecnt + readcnt + 20 > CMD_LENGTH) {
 | |
| 		msg_perr("\nTotal packetsize (%i) is greater than 64 supported, aborting.\n",
 | |
| 			 writecnt + readcnt + 20);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	uint8_t buf[CMD_LENGTH] = {CMD_DOWNLOAD_DATA, writecnt};
 | |
| 	unsigned int i = 2;
 | |
| 	for (; i < writecnt + 2; i++) {
 | |
| 		buf[i] = writearr[i - 2];
 | |
| 	}
 | |
| 
 | |
| 	buf[i++] = CMD_CLR_ULOAD_BUFF;
 | |
| 	buf[i++] = CMD_EXEC_SCRIPT;
 | |
| 
 | |
| 	/* Determine script length based on number of bytes to be read or written */
 | |
| 	if (writecnt == 1 && readcnt == 1)
 | |
| 		buf[i++] = 7;
 | |
| 	else if (writecnt == 1 || readcnt == 1)
 | |
| 		buf[i++] = 10;
 | |
| 	else
 | |
| 		buf[i++] = 13;
 | |
| 
 | |
| 	/* Assert CS# */
 | |
| 	buf[i++] = SCR_VPP_OFF;
 | |
| 	buf[i++] = SCR_MCLR_GND_ON;
 | |
| 
 | |
| 	buf[i++] = SCR_SPI_WRITE_BUF;
 | |
| 
 | |
| 	if (writecnt > 1) {
 | |
| 		buf[i++] = SCR_LOOP;
 | |
| 		buf[i++] = 1; /* Loop back one instruction */
 | |
| 		buf[i++] = writecnt - 1; /* Number of times to loop */
 | |
| 	}
 | |
| 
 | |
| 	if (readcnt)
 | |
| 		buf[i++] = SCR_SPI_READ_BUF;
 | |
| 
 | |
| 	if (readcnt > 1) {
 | |
| 		buf[i++] = SCR_LOOP;
 | |
| 		buf[i++] = 1; /* Loop back one instruction */
 | |
| 		buf[i++] = readcnt - 1; /* Number of times to loop */
 | |
| 	}
 | |
| 
 | |
| 	/* De-assert CS# */
 | |
| 	buf[i++] = SCR_MCLR_GND_OFF;
 | |
| 	buf[i++] = SCR_VPP_PWM_ON;
 | |
| 	buf[i++] = SCR_VPP_ON;
 | |
| 
 | |
| 	buf[i++] = CMD_UPLOAD_DATA;
 | |
| 	buf[i++] = CMD_END_OF_BUFFER;
 | |
| 
 | |
| 	int transferred;
 | |
| 	int ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, buf, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		msg_perr("Send SPI failed!\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	if (readcnt) {
 | |
| 		int length = 0;
 | |
| 		ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_IN, buf, CMD_LENGTH, &length, DFLT_TIMEOUT);
 | |
| 
 | |
| 		if (length == 0 || ret != 0) {
 | |
| 			msg_perr("Receive SPI failed\n");
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		/* First byte indicates number of bytes transferred from upload buffer */
 | |
| 		if (buf[0] != readcnt) {
 | |
| 			msg_perr("Unexpected number of bytes transferred, expected %i, got %i!\n",
 | |
| 				 readcnt, ret);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		/* Actual data starts at byte number two */
 | |
| 		memcpy(readarr, &buf[1], readcnt);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Copied from dediprog.c */
 | |
| /* Might be useful for other USB devices as well. static for now. */
 | |
| static int parse_voltage(char *voltage)
 | |
| {
 | |
| 	char *tmp = NULL;
 | |
| 	int i;
 | |
| 	int millivolt = 0, fraction = 0;
 | |
| 
 | |
| 	if (!voltage || !strlen(voltage)) {
 | |
| 		msg_perr("Empty voltage= specified.\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	millivolt = (int)strtol(voltage, &tmp, 0);
 | |
| 	voltage = tmp;
 | |
| 	/* Handle "," and "." as decimal point. Everything after it is assumed
 | |
| 	 * to be in decimal notation.
 | |
| 	 */
 | |
| 	if ((*voltage == '.') || (*voltage == ',')) {
 | |
| 		voltage++;
 | |
| 		for (i = 0; i < 3; i++) {
 | |
| 			fraction *= 10;
 | |
| 			/* Don't advance if the current character is invalid,
 | |
| 			 * but continue multiplying.
 | |
| 			 */
 | |
| 			if ((*voltage < '0') || (*voltage > '9'))
 | |
| 				continue;
 | |
| 			fraction += *voltage - '0';
 | |
| 			voltage++;
 | |
| 		}
 | |
| 		/* Throw away remaining digits. */
 | |
| 		voltage += strspn(voltage, "0123456789");
 | |
| 	}
 | |
| 	/* The remaining string must be empty or "mV" or "V". */
 | |
| 	tolower_string(voltage);
 | |
| 
 | |
| 	/* No unit or "V". */
 | |
| 	if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) {
 | |
| 		millivolt *= 1000;
 | |
| 		millivolt += fraction;
 | |
| 	} else if (!strncmp(voltage, "mv", 2) ||
 | |
| 		   !strncmp(voltage, "millivolt", 9)) {
 | |
| 		/* No adjustment. fraction is discarded. */
 | |
| 	} else {
 | |
| 		/* Garbage at the end of the string. */
 | |
| 		msg_perr("Garbage voltage= specified.\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	return millivolt;
 | |
| }
 | |
| 
 | |
| static const struct spi_master spi_master_pickit2 = {
 | |
| 	.max_data_read	= 40,
 | |
| 	.max_data_write	= 40,
 | |
| 	.command	= pickit2_spi_send_command,
 | |
| 	.multicommand	= default_spi_send_multicommand,
 | |
| 	.read		= default_spi_read,
 | |
| 	.write_256	= default_spi_write_256,
 | |
| 	.write_aai	= default_spi_write_aai,
 | |
| };
 | |
| 
 | |
| static int pickit2_shutdown(void *data)
 | |
| {
 | |
| 	/* Set all pins to float and turn voltages off */
 | |
| 	uint8_t command[CMD_LENGTH] = {
 | |
| 		CMD_EXEC_SCRIPT,
 | |
| 		8,
 | |
| 		SCR_SET_PINS,
 | |
| 		3, /* Bit-0=1(PDC In), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
 | |
| 		SCR_SET_AUX,
 | |
| 		1, /* Bit-0=1(Aux In), Bit-1=0(Aux LL) */
 | |
| 		SCR_MCLR_GND_OFF,
 | |
| 		SCR_VPP_OFF,
 | |
| 		SCR_VDD_OFF,
 | |
| 		SCR_BUSY_LED_OFF,
 | |
| 		CMD_END_OF_BUFFER
 | |
| 	};
 | |
| 
 | |
| 	int transferred;
 | |
| 	int ret = libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, command, CMD_LENGTH, &transferred, DFLT_TIMEOUT);
 | |
| 
 | |
| 	if (ret != 0) {
 | |
| 		msg_perr("Command Shutdown failed!\n");
 | |
| 		ret = 1;
 | |
| 	}
 | |
| 	if (libusb_release_interface(pickit2_handle, 0) != 0) {
 | |
| 		msg_perr("Could not release USB interface!\n");
 | |
| 		ret = 1;
 | |
| 	}
 | |
| 	libusb_close(pickit2_handle);
 | |
| 	libusb_exit(NULL);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int pickit2_spi_init(void)
 | |
| {
 | |
| 	uint8_t buf[CMD_LENGTH] = {
 | |
| 		CMD_EXEC_SCRIPT,
 | |
| 		10,			/* Script length */
 | |
| 		SCR_SET_PINS,
 | |
| 		2, /* Bit-0=0(PDC Out), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
 | |
| 		SCR_SET_AUX,
 | |
| 		0, /* Bit-0=0(Aux Out), Bit-1=0(Aux LL) */
 | |
| 		SCR_VDD_ON,
 | |
| 		SCR_MCLR_GND_OFF,	/* Let CS# float */
 | |
| 		SCR_VPP_PWM_ON,
 | |
| 		SCR_VPP_ON,		/* Pull CS# high */
 | |
| 		SCR_BUSY_LED_ON,
 | |
| 		CMD_CLR_DLOAD_BUFF,
 | |
| 		CMD_CLR_ULOAD_BUFF,
 | |
| 		CMD_END_OF_BUFFER
 | |
| 	};
 | |
| 
 | |
| 
 | |
| 	int spispeed_idx = 0;
 | |
| 	char *spispeed = extract_programmer_param("spispeed");
 | |
| 	if (spispeed != NULL) {
 | |
| 		int i = 0;
 | |
| 		for (; spispeeds[i].name; i++) {
 | |
| 			if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
 | |
| 				spispeed_idx = i;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		if (spispeeds[i].name == NULL) {
 | |
| 			msg_perr("Error: Invalid 'spispeed' value.\n");
 | |
| 			free(spispeed);
 | |
| 			return 1;
 | |
| 		}
 | |
| 		free(spispeed);
 | |
| 	}
 | |
| 
 | |
| 	int millivolt = 3500;
 | |
| 	char *voltage = extract_programmer_param("voltage");
 | |
| 	if (voltage != NULL) {
 | |
| 		millivolt = parse_voltage(voltage);
 | |
| 		free(voltage);
 | |
| 		if (millivolt < 0)
 | |
| 			return 1;
 | |
| 	}
 | |
| 
 | |
| 	if (libusb_init(NULL) < 0) {
 | |
| 		msg_perr("Couldn't initialize libusb!\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| #if LIBUSB_API_VERSION < 0x01000106
 | |
| 	libusb_set_debug(NULL, 3);
 | |
| #else
 | |
| 	libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
 | |
| #endif
 | |
| 
 | |
| 	const uint16_t vid = devs_pickit2_spi[0].vendor_id;
 | |
| 	const uint16_t pid = devs_pickit2_spi[0].device_id;
 | |
| 	pickit2_handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
 | |
| 	if (pickit2_handle == NULL) {
 | |
| 		msg_perr("Could not open device PICkit2!\n");
 | |
| 		libusb_exit(NULL);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	if (libusb_set_configuration(pickit2_handle, 1) != 0) {
 | |
| 		msg_perr("Could not set USB device configuration.\n");
 | |
| 		libusb_close(pickit2_handle);
 | |
| 		libusb_exit(NULL);
 | |
| 		return 1;
 | |
| 	}
 | |
| 	if (libusb_claim_interface(pickit2_handle, 0) != 0) {
 | |
| 		msg_perr("Could not claim USB device interface\n");
 | |
| 		libusb_close(pickit2_handle);
 | |
| 		libusb_exit(NULL);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	if (register_shutdown(pickit2_shutdown, NULL) != 0) {
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	if (pickit2_get_firmware_version()) {
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	/* Command Set SPI Speed */
 | |
| 	if (pickit2_set_spi_speed(spispeed_idx)) {
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	/* Command Set SPI Voltage */
 | |
| 	msg_pdbg("Setting voltage to %i mV.\n", millivolt);
 | |
| 	if (pickit2_set_spi_voltage(millivolt) != 0) {
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	/* Perform basic setup.
 | |
| 	 * Configure pin directions and logic levels, turn Vdd on, turn busy LED on and clear buffers. */
 | |
| 	int transferred;
 | |
| 	if (libusb_interrupt_transfer(pickit2_handle, ENDPOINT_OUT, buf, CMD_LENGTH, &transferred, DFLT_TIMEOUT) != 0) {
 | |
| 		msg_perr("Command Setup failed!\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	register_spi_master(&spi_master_pickit2);
 | |
| 
 | |
| 	return 0;
 | |
| }
 |