Forked from
PureOS / Core Packages / flashrom
21 commits behind, 47 commits ahead of the upstream repository.
-
Edward O'Callaghan authored
The ChromiumOS flashrom fork has since const'ify flashctx in a few places. This aligns the function signatures to match with downstream to ease forward porting patches out of downstream back into mainline flashrom. This patch is minimum viable alignment and so feedback is welcome. Change-Id: Iff6dbda13cb0d941481c0d204b9c30895630fbd1 Signed-off-by:
Edward O'Callaghan <quasisec@google.com> Reviewed-on: https://review.coreboot.org/c/flashrom/+/40324 Reviewed-by:
Angel Pons <th3fanbus@gmail.com> Tested-by:
build bot (Jenkins) <no-reply@coreboot.org>
Edward O'Callaghan authoredThe ChromiumOS flashrom fork has since const'ify flashctx in a few places. This aligns the function signatures to match with downstream to ease forward porting patches out of downstream back into mainline flashrom. This patch is minimum viable alignment and so feedback is welcome. Change-Id: Iff6dbda13cb0d941481c0d204b9c30895630fbd1 Signed-off-by:
Edward O'Callaghan <quasisec@google.com> Reviewed-on: https://review.coreboot.org/c/flashrom/+/40324 Reviewed-by:
Angel Pons <th3fanbus@gmail.com> Tested-by:
build bot (Jenkins) <no-reply@coreboot.org>
digilent_spi.c 10.53 KiB
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk>
*
* Based on ft2232_spi.c:
*
* Copyright (C) 2011 asbokid <ballymunboy@gmail.com>
* Copyright (C) 2014 Pluto Yang <yangyj.ee@gmail.com>
* Copyright (C) 2015-2016 Stefan Tauner
* Copyright (C) 2015 Urja Rannikko <urjaman@gmail.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* The reverse-engineered protocol description was obtained from the
* iceBurn project <https://github.com/davidcarne/iceBurn> by
* David Carne <davidcarne@gmail.com>.
*/
#include <stdlib.h>
#include <string.h>
#include <libusb.h>
#include "programmer.h"
/* This is pretty much arbitrarily chosen. After one second without a
* response we can be pretty sure we're not going to succeed. */
#define USB_TIMEOUT 1000
#define CMD_WRITE_EP 0x01
#define CMD_READ_EP 0x82
#define DATA_WRITE_EP 0x03
#define DATA_READ_EP 0x84
static struct libusb_device_handle *handle = NULL;
static bool reset_board;
#define DIGILENT_VID 0x1443
#define DIGILENT_JTAG_PID 0x0007
const struct dev_entry devs_digilent_spi[] = {
{ DIGILENT_VID, DIGILENT_JTAG_PID, OK, "Digilent", "Development board JTAG" },
{ 0 },
};
/* Control endpoint commands. */
enum {
GET_BOARD_TYPE = 0xe2,
GET_BOARD_SERIAL = 0xe4,
};
/* Command bulk endpoint command groups. */
enum {
CMD_GPIO = 0x03,
CMD_BOARD = 0x04,
CMD_SPI = 0x06,
};
/* GPIO subcommands. */
enum {
CMD_GPIO_OPEN = 0x00,
CMD_GPIO_CLOSE = 0x01,
CMD_GPIO_SET_DIR = 0x04,
CMD_GPIO_SET_VAL = 0x06,
};
/* Board subcommands. */
enum {
CMD_BOARD_OPEN = 0x00,
CMD_BOARD_CLOSE = 0x01,
CMD_BOARD_SET_REG = 0x04,
CMD_BOARD_GET_REG = 0x05,
CMD_BOARD_PL_STAT = 0x85,
};
/* SPI subcommands. */
enum {
CMD_SPI_OPEN = 0x00,
CMD_SPI_CLOSE = 0x01,
CMD_SPI_SET_SPEED = 0x03,
CMD_SPI_SET_MODE = 0x05,
CMD_SPI_SET_CS = 0x06,
CMD_SPI_START_IO = 0x07,
CMD_SPI_TX_END = 0x87,
};
static int do_command(uint8_t *req, int req_len, uint8_t *res, int res_len)
{
int tx_len = 0;
int ret;
req[0] = req_len - 1;
ret = libusb_bulk_transfer(handle, CMD_WRITE_EP, req, req_len, &tx_len, USB_TIMEOUT);
if (ret) {
msg_perr("Failed to issue a command: '%s'\n", libusb_error_name(ret));
return -1;
}
if (tx_len != req_len) {
msg_perr("Short write issuing a command\n");
return -1;
}
ret = libusb_bulk_transfer(handle, CMD_READ_EP, res, res_len, &tx_len, USB_TIMEOUT);
if (ret) {
msg_perr("Failed to get a response: '%s'\n", libusb_error_name(ret));
return -1;
}
if (tx_len != res_len) {
msg_perr("Short read getting a response\n");
return -1;
}
if (res[0] != res_len -1) {
msg_perr("Response indicates incorrect length.\n");
return -1;
}
return 0;
}
static int gpio_open(void)
{
uint8_t req[] = { 0x00, CMD_GPIO, CMD_GPIO_OPEN, 0x00 };
uint8_t res[2];
return do_command(req, sizeof(req), res, sizeof(res));
}
static int gpio_set_dir(uint8_t direction)
{
uint8_t req[] = { 0x00, CMD_GPIO, CMD_GPIO_SET_DIR, 0x00,
direction, 0x00, 0x00, 0x00 };
uint8_t res[6];
return do_command(req, sizeof(req), res, sizeof(res));
}
static int gpio_set_value(uint8_t value)
{
uint8_t req[] = { 0x00, CMD_GPIO, CMD_GPIO_SET_VAL, 0x00,
value, 0x00, 0x00, 0x00 };
uint8_t res[2];
return do_command(req, sizeof(req), res, sizeof(res));
}
static int spi_open(void)
{
uint8_t req[] = { 0x00, CMD_SPI, CMD_SPI_OPEN, 0x00 };
uint8_t res[2];
return do_command(req, sizeof(req), res, sizeof(res));
}
static int spi_set_speed(uint32_t speed)
{
uint8_t req[] = { 0x00, CMD_SPI, CMD_SPI_SET_SPEED, 0x00,
(speed) & 0xff,
(speed >> 8) & 0xff,
(speed >> 16) & 0xff,
(speed >> 24) & 0xff };
uint8_t res[6];
uint32_t real_speed;
int ret;
ret = do_command(req, sizeof(req), res, sizeof(res));
if (ret)
return ret;
real_speed = (res[5] << 24) | (res[4] << 16) | (res[3] << 8) | res[2];
if (real_speed != speed)
msg_pwarn("SPI speed set to %d instead of %d\n", real_speed, speed);
return 0;
}
static int spi_set_mode(uint8_t mode)
{
uint8_t req[] = { 0x00, CMD_SPI, CMD_SPI_SET_MODE, 0x00, mode };
uint8_t res[2];
return do_command(req, sizeof(req), res, sizeof(res));
}
static int spi_set_cs(uint8_t cs)
{
uint8_t req[] = { 0x00, CMD_SPI, CMD_SPI_SET_CS, 0x00, cs };
uint8_t res[2];
return do_command(req, sizeof(req), res, sizeof(res));
}
static int spi_start_io(uint8_t read_follows, uint32_t write_len)
{
uint8_t req[] = { 0x00, CMD_SPI, CMD_SPI_START_IO, 0x00,
0x00, 0x00, /* meaning unknown */
read_follows,
(write_len) & 0xff,
(write_len >> 8) & 0xff,
(write_len >> 16) & 0xff,
(write_len >> 24) & 0xff };
uint8_t res[2];
return do_command(req, sizeof(req), res, sizeof(res));
}
static int spi_tx_end(uint8_t read_follows, uint32_t tx_len)
{
uint8_t req[] = { 0x00, CMD_SPI, CMD_SPI_TX_END, 0x00 };
uint8_t res[read_follows ? 10 : 6];
int ret;
uint32_t count;
ret = do_command(req, sizeof(req), res, sizeof(res));
if (ret != 0)
return ret;
if ((res[1] & 0x80) == 0) {
msg_perr("%s: response missing a write count\n", __func__);
return -1;
}
count = res[2] | (res[3] << 8) | (res[4] << 16) | res[5] << 24;
if (count != tx_len) {
msg_perr("%s: wrote only %d bytes instead of %d\n", __func__, count, tx_len);
return -1;
}
if (read_follows) {
if ((res[1] & 0x40) == 0) {
msg_perr("%s: response missing a read count\n", __func__);
return -1;
}
count = res[6] | (res[7] << 8) | (res[8] << 16) | res[9] << 24;
if (count != tx_len) {
msg_perr("%s: read only %d bytes instead of %d\n", __func__, count, tx_len);
return -1;
}
}
return 0;
}
static int digilent_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
const unsigned char *writearr, unsigned char *readarr)
{
int ret;
int len = writecnt + readcnt;
int tx_len = 0;
uint8_t buf[len];
uint8_t read_follows = readcnt > 0 ? 1 : 0;
memcpy(buf, writearr, writecnt);
memset(buf + writecnt, 0xff, readcnt);
ret = spi_set_cs(0);
if (ret != 0)
return ret;
ret = spi_start_io(read_follows, writecnt);
if (ret != 0)
return ret;
ret = libusb_bulk_transfer(handle, DATA_WRITE_EP, buf, len, &tx_len, USB_TIMEOUT);
if (ret != 0) {
msg_perr("%s: failed to write data: '%s'\n", __func__, libusb_error_name(ret));
return -1;
}
if (tx_len != len) {
msg_perr("%s: short write\n", __func__);
return -1;
}
if (read_follows) {
ret = libusb_bulk_transfer(handle, DATA_READ_EP, buf, len, &tx_len, USB_TIMEOUT);
if (ret != 0) {
msg_perr("%s: failed to read data: '%s'\n", __func__, libusb_error_name(ret));
return -1;
}
if (tx_len != len) {
msg_perr("%s: short read\n", __func__);
return -1;
}
}
ret = spi_tx_end(read_follows, len);
if (ret != 0)
return ret;
ret = spi_set_cs(1);
if (ret != 0)
return ret;
memcpy(readarr, &buf[writecnt], readcnt);
return 0;
}
static const struct spi_master spi_master_digilent_spi = {
.features = SPI_MASTER_4BA,
.max_data_read = 252,
.max_data_write = 252,
.command = digilent_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 digilent_spi_shutdown(void *data)
{
if (reset_board)
gpio_set_dir(0);
libusb_close(handle);
handle = NULL;
return 0;
}
static bool default_reset(void)
{
char board[17];
libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR,
GET_BOARD_TYPE, 0, 0,
(unsigned char *)board, sizeof(board) - 1, USB_TIMEOUT);
board[sizeof(board) -1] = '\0';
if (strcmp(board, "iCE40") == 0)
return true;
msg_pwarn("%s: unknown board '%s' not attempting a reset. "
"Override with '-p digilent_spi=reset=1'.\n", __func__, board);
return false;
}
struct digilent_spispeeds {
const char *const name;
const int speed;
};
static const struct digilent_spispeeds spispeeds[] = {
{ "4M", 4000000 },
{ "2M", 2000000 },
{ "1M", 1000000 },
{ "500k", 500000 },
{ "250k", 250000 },
{ "125k", 125000 },
{ "62.5k", 62500 },
{ NULL, 0 },
};
int digilent_spi_init(void)
{
char *p;
uint32_t speed_hz = spispeeds[0].speed;
int i;
if (handle != NULL) {
msg_cerr("%s: handle already set! Please report a bug at flashrom@flashrom.org\n", __func__);
return -1;
}
int32_t ret = libusb_init(NULL);
if (ret < 0) {
msg_perr("%s: couldn't initialize libusb!\n", __func__);
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
uint16_t vid = devs_digilent_spi[0].vendor_id;
uint16_t pid = devs_digilent_spi[0].device_id;
handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
if (handle == NULL) {
msg_perr("%s: couldn't open device %04x:%04x.\n", __func__, vid, pid);
return -1;
}
ret = libusb_claim_interface(handle, 0);
if (ret != 0) {
msg_perr("%s: failed to claim interface 0: '%s'\n", __func__, libusb_error_name(ret));
goto close_handle;
}
p = extract_programmer_param("spispeed");
if (p) {
for (i = 0; spispeeds[i].name; ++i) {
if (!strcasecmp(spispeeds[i].name, p)) {
speed_hz = spispeeds[i].speed;
break;
}
}
if (!spispeeds[i].name) {
msg_perr("Error: Invalid spispeed value: '%s'.\n", p);
free(p);
goto close_handle;
}
free(p);
}
p = extract_programmer_param("reset");
if (p && strlen(p))
reset_board = (p[0] == '1');
else
reset_board = default_reset();
free(p);
if (reset_board) {
if (gpio_open() != 0)
goto close_handle;
if (gpio_set_dir(1) != 0)
goto close_handle;
if (gpio_set_value(0) != 0)
goto close_handle;
}
if (spi_open() != 0)
goto close_handle;
if (spi_set_speed(speed_hz) != 0)
goto close_handle;
if (spi_set_mode(0x00) != 0)
goto close_handle;
register_shutdown(digilent_spi_shutdown, NULL);
register_spi_master(&spi_master_digilent_spi);
return 0;
close_handle:
libusb_close(handle);
handle = NULL;
return -1;
}