1
0
mirror of https://review.coreboot.org/flashrom.git synced 2025-04-27 23:22:37 +02:00

Add initial J-Link SPI programmer

Tested with SEGGER J-Link EDU, Flasher ARM and flash chip W25Q16.V.

Change-Id: Ie03a054a75457ec9e1cab36ea124bb53b10e8d7e
Signed-off-by: Marc Schink <flashrom-dev@marcschink.de>
Reviewed-on: https://review.coreboot.org/c/28087
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
This commit is contained in:
Marc Schink 2016-03-17 16:23:03 +01:00 committed by Nico Huber
parent 9cecc7e25d
commit 3578ec6a3d
6 changed files with 592 additions and 1 deletions

View File

@ -656,6 +656,9 @@ CONFIG_CH341A_SPI ?= yes
# Digilent Development board JTAG # Digilent Development board JTAG
CONFIG_DIGILENT_SPI ?= yes CONFIG_DIGILENT_SPI ?= yes
# Disable J-Link for now.
CONFIG_JLINK_SPI ?= no
# Disable wiki printing by default. It is only useful if you have wiki access. # Disable wiki printing by default. It is only useful if you have wiki access.
CONFIG_PRINT_WIKI ?= no CONFIG_PRINT_WIKI ?= no
@ -964,6 +967,12 @@ PROGRAMMER_OBJS += digilent_spi.o
NEED_LIBUSB1 += CONFIG_DIGILENT_SPI NEED_LIBUSB1 += CONFIG_DIGILENT_SPI
endif endif
ifeq ($(CONFIG_JLINK_SPI), yes)
NEED_LIBJAYLINK += CONFIG_JLINK_SPI
FEATURE_CFLAGS += -D'CONFIG_JLINK_SPI=1'
PROGRAMMER_OBJS += jlink_spi.o
endif
ifneq ($(NEED_SERIAL), ) ifneq ($(NEED_SERIAL), )
LIB_OBJS += serial.o custom_baud.o LIB_OBJS += serial.o custom_baud.o
endif endif
@ -1038,6 +1047,12 @@ endif
endif endif
endif endif
ifneq ($(NEED_LIBJAYLINK), )
CHECK_LIBJAYLINK = yes
JAYLINKLIBS += $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)"; $(PKG_CONFIG) --libs libjaylink)
override CPPFLAGS += $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)"; $(PKG_CONFIG) --cflags-only-I libjaylink)
endif
ifeq ($(CONFIG_PRINT_WIKI), yes) ifeq ($(CONFIG_PRINT_WIKI), yes)
FEATURE_CFLAGS += -D'CONFIG_PRINT_WIKI=1' FEATURE_CFLAGS += -D'CONFIG_PRINT_WIKI=1'
CLI_OBJS += print_wiki.o CLI_OBJS += print_wiki.o
@ -1060,7 +1075,7 @@ ifeq ($(ARCH), x86)
endif endif
$(PROGRAM)$(EXEC_SUFFIX): $(OBJS) $(PROGRAM)$(EXEC_SUFFIX): $(OBJS)
$(CC) $(LDFLAGS) -o $(PROGRAM)$(EXEC_SUFFIX) $(OBJS) $(LIBS) $(PCILIBS) $(FEATURE_LIBS) $(USBLIBS) $(USB1LIBS) $(CC) $(LDFLAGS) -o $(PROGRAM)$(EXEC_SUFFIX) $(OBJS) $(LIBS) $(PCILIBS) $(FEATURE_LIBS) $(USBLIBS) $(USB1LIBS) $(JAYLINKLIBS)
libflashrom.a: $(LIBFLASHROM_OBJS) libflashrom.a: $(LIBFLASHROM_OBJS)
$(AR) rcs $@ $^ $(AR) rcs $@ $^
@ -1194,6 +1209,24 @@ int main(int argc, char **argv)
endef endef
export LIBUSB1_TEST export LIBUSB1_TEST
define LIBJAYLINK_TEST
#include <stddef.h>
#include <libjaylink/libjaylink.h>
int main(int argc, char **argv)
{
struct jaylink_context *ctx;
(void)argc;
(void)argv;
jaylink_init(&ctx);
jaylink_exit(ctx);
return 0;
}
endef
export LIBJAYLINK_TEST
hwlibs: compiler hwlibs: compiler
@printf "" > .libdeps @printf "" > .libdeps
ifeq ($(CHECK_LIBPCI), yes) ifeq ($(CHECK_LIBPCI), yes)
@ -1272,6 +1305,28 @@ ifeq ($(CHECK_LIBUSB1), yes)
rm -f .test.c .test.o .test$(EXEC_SUFFIX); exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1 rm -f .test.c .test.o .test$(EXEC_SUFFIX); exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
@rm -f .test.c .test.o .test$(EXEC_SUFFIX) @rm -f .test.c .test.o .test$(EXEC_SUFFIX)
endif endif
ifeq ($(CHECK_LIBJAYLINK), yes)
@printf "Checking for libjaylink headers... " | tee -a $(BUILD_DETAILS_FILE)
@echo "$$LIBJAYLINK_TEST" > .test.c
@printf "\nexec: %s\n" "$(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o" >>$(BUILD_DETAILS_FILE)
@{ { { { { $(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o >&2 && \
echo "found." || { echo "not found."; echo; \
echo "The following feature requires libjaylink: $(NEED_LIBJAYLINK)."; \
echo "Please install libjaylink headers or disable the feature"; \
echo "mentioned above by specifying make CONFIG_JLINK_SPI=no"; \
echo "See README for more information."; echo; \
rm -f .test.c .test.o; exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
@printf "Checking if libjaylink is usable... " | tee -a $(BUILD_DETAILS_FILE)
@printf "\nexec: %s\n" "$(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(JAYLINKLIBS)" >>$(BUILD_DETAILS_FILE)
@{ { { { { $(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(JAYLINKLIBS) >&2 && \
echo "yes." || { echo "no."; \
echo "The following feature requires libjaylink: $(NEED_LIBJAYLINK)."; \
echo "Please install libjaylink or disable the feature"; \
echo "mentioned above by specifying make CONFIG_JLINK_SPI=no"; \
echo "See README for more information."; echo; \
rm -f .test.c .test.o .test$(EXEC_SUFFIX); exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
@rm -f .test.c .test.o .test$(EXEC_SUFFIX)
endif
.features: features .features: features

1
README
View File

@ -50,6 +50,7 @@ To build flashrom you need to install the following software:
* pciutils+libpci (if you want support for mainboard or PCI device flashing) * pciutils+libpci (if you want support for mainboard or PCI device flashing)
* libusb (if you want FT2232, Dediprog or USB-Blaster support) * libusb (if you want FT2232, Dediprog or USB-Blaster support)
* libftdi (if you want FT2232 or USB-Blaster support) * libftdi (if you want FT2232 or USB-Blaster support)
* libjaylink (if you want support for SEGGER J-Link and compatible devices)
Linux et al: Linux et al:

View File

@ -331,6 +331,8 @@ bitbanging adapter)
.sp .sp
.BR "* digilent_spi" " (for SPI flash ROMs attached to iCEblink40 development boards)" .BR "* digilent_spi" " (for SPI flash ROMs attached to iCEblink40 development boards)"
.sp .sp
.BR "* jlink_spi" " (for SPI flash ROMs attached to SEGGER J-Link and compatible devices)"
.sp
Some programmers have optional or mandatory parameters which are described Some programmers have optional or mandatory parameters which are described
in detail in the in detail in the
.B PROGRAMMER-SPECIFIC INFORMATION .B PROGRAMMER-SPECIFIC INFORMATION
@ -1131,6 +1133,60 @@ can be
(in Hz). The default is a frequency of 4 MHz. (in Hz). The default is a frequency of 4 MHz.
.sp .sp
.SS .SS
.BR "jlink_spi " programmer
.IP
This module supports SEGGER J-Link and compatible devices.
The \fBMOSI\fP signal of the flash chip must be attached to \fBTDI\fP pin of
the programmer, \fBMISO\fP to \fBTDO\fP and \fBSCK\fP to \fBTCK\fP.
The chip select (\fBCS\fP) signal of the flash chip can be attached to
different pins of the programmer which can be selected with the
.sp
.B " flashrom \-p jlink_spi:cs=pin"
.sp
syntax where \fBpin\fP can be either \fBTRST\fP or \fBRESET\fP.
The default pin for chip select is \fBRESET\fP.
Note that, when using \fBRESET\fP, it is normal that the indicator LED blinks
orange or red.
.br
Additionally, the \fBVTref\fP pin of the programmer must be attached to the
logic level of the flash chip.
The programmer measures the voltage on this pin and generates the reference
voltage for its input comparators and adapts its output voltages to it.
.sp
Pinout for devices with 20-pin JTAG connector:
.sp
+-------+
| 1 2 | 1: VTref 2:
| 3 4 | 3: TRST 4: GND
| 5 6 | 5: TDI 6: GND
+-+ 7 8 | 7: 8: GND
| 9 10 | 9: TCK 10: GND
| 11 12 | 11: 12: GND
+-+ 13 14 | 13: TDO 14:
| 15 16 | 15: RESET 16:
| 17 18 | 17: 18:
| 19 20 | 19: PWR_5V 20:
+-------+
.sp
If there is more than one compatible device connected, you can select which one
should be used by specifying its serial number with the
.sp
.B " flashrom \-p jlink_spi:serial=number"
.sp
syntax where
.B number
is the serial number of the device (which can be found for example in the
output of lsusb -v).
.sp
The SPI speed can be selected by using the
.sp
.B " flashrom \-p jlink_spi:spispeed=frequency"
.sp
syntax where \fBfrequency\fP is the SPI clock frequency in kHz.
The maximum speed depends on the device in use.
.SS
.SH EXAMPLES .SH EXAMPLES
To back up and update your BIOS, run To back up and update your BIOS, run
.sp .sp

View File

@ -437,6 +437,18 @@ const struct programmer_entry programmer_table[] = {
}, },
#endif #endif
#if CONFIG_JLINK_SPI == 1
{
.name = "jlink_spi",
.type = OTHER,
.init = jlink_spi_init,
.devs.note = "SEGGER J-Link and compatible devices\n",
.map_flash_region = fallback_map,
.unmap_flash_region = fallback_unmap,
.delay = internal_delay,
},
#endif
{0}, /* This entry corresponds to PROGRAMMER_INVALID. */ {0}, /* This entry corresponds to PROGRAMMER_INVALID. */
}; };

456
jlink_spi.c Normal file
View File

@ -0,0 +1,456 @@
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2016 Marc Schink <flashrom-dev@marcschink.de>
*
* 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.
*/
/*
* Driver for the J-Link hardware by SEGGER.
* See https://www.segger.com/ for more info.
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <libjaylink/libjaylink.h>
#include "flash.h"
#include "programmer.h"
#include "spi.h"
/*
* Maximum number of bytes that can be transferred at once via the JTAG
* interface, see jaylink_jtag_io().
*/
#define JTAG_MAX_TRANSFER_SIZE (UINT16_MAX / 8)
/*
* Default base frequency in Hz. Used when the base frequency can not be
* retrieved from the device.
*/
#define DEFAULT_FREQ 16000000
/*
* Default frequency divider. Used when the frequency divider can not be
* retrieved from the device.
*/
#define DEFAULT_FREQ_DIV 4
/* Minimum target voltage required for operation in mV. */
#define MIN_TARGET_VOLTAGE 1200
static struct jaylink_context *jaylink_ctx;
static struct jaylink_device_handle *jaylink_devh;
static bool reset_cs;
static bool assert_cs(void)
{
int ret;
if (reset_cs) {
ret = jaylink_clear_reset(jaylink_devh);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_clear_reset() failed: %s.\n", jaylink_strerror(ret));
return false;
}
} else {
ret = jaylink_jtag_clear_trst(jaylink_devh);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_jtag_clear_trst() failed: %s.\n", jaylink_strerror(ret));
return false;
}
}
return true;
}
static bool deassert_cs(void)
{
int ret;
if (reset_cs) {
ret = jaylink_set_reset(jaylink_devh);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_set_reset() failed: %s.\n", jaylink_strerror(ret));
return false;
}
} else {
ret = jaylink_jtag_set_trst(jaylink_devh);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_jtag_set_trst() failed: %s.\n", jaylink_strerror(ret));
return false;
}
}
return true;
}
static int jlink_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
const unsigned char *writearr, unsigned char *readarr)
{
uint32_t length;
uint8_t *buffer;
length = writecnt + readcnt;
if (length > JTAG_MAX_TRANSFER_SIZE)
return SPI_INVALID_LENGTH;
buffer = malloc(length);
if (!buffer) {
msg_perr("Memory allocation failed.\n");
return SPI_GENERIC_ERROR;
}
/* Reverse all bytes because the device transfers data LSB first. */
reverse_bytes(buffer, writearr, writecnt);
memset(buffer + writecnt, 0x00, readcnt);
if (!assert_cs()) {
free(buffer);
return SPI_PROGRAMMER_ERROR;
}
int ret;
ret = jaylink_jtag_io(jaylink_devh, buffer, buffer, buffer, length * 8, JAYLINK_JTAG_VERSION_2);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_jag_io() failed: %s.\n", jaylink_strerror(ret));
free(buffer);
return SPI_PROGRAMMER_ERROR;
}
if (!deassert_cs()) {
free(buffer);
return SPI_PROGRAMMER_ERROR;
}
/* Reverse all bytes because the device transfers data LSB first. */
reverse_bytes(readarr, buffer + writecnt, readcnt);
free(buffer);
return 0;
}
static const struct spi_master spi_master_jlink_spi = {
.type = SPI_CONTROLLER_JLINK_SPI,
/* Maximum data read size in one go (excluding opcode+address). */
.max_data_read = JTAG_MAX_TRANSFER_SIZE - 5,
/* Maximum data write size in one go (excluding opcode+address). */
.max_data_write = JTAG_MAX_TRANSFER_SIZE - 5,
.command = jlink_spi_send_command,
.multicommand = default_spi_send_multicommand,
.read = default_spi_read,
.write_256 = default_spi_write_256,
.write_aai = default_spi_write_aai,
.features = SPI_MASTER_4BA,
};
static int jlink_spi_shutdown(void *data)
{
if (jaylink_devh)
jaylink_close(jaylink_devh);
jaylink_exit(jaylink_ctx);
return 0;
}
int jlink_spi_init(void)
{
char *arg;
unsigned long speed = 0;
register_shutdown(jlink_spi_shutdown, NULL);
arg = extract_programmer_param("spispeed");
if (arg) {
char *endptr;
errno = 0;
speed = strtoul(arg, &endptr, 10);
if (*endptr != '\0' || errno != 0) {
msg_perr("Invalid SPI speed specified: %s.\n", arg);
free(arg);
return 1;
}
if (speed < 1) {
msg_perr("SPI speed must be at least 1 kHz.\n");
free(arg);
return 1;
}
}
free(arg);
int ret;
bool use_serial_number;
uint32_t serial_number;
arg = extract_programmer_param("serial");
if (arg) {
if (!strlen(arg)) {
msg_perr("Emptpy serial number specified.\n");
free(arg);
return 1;
}
ret = jaylink_parse_serial_number(arg, &serial_number);
if (ret == JAYLINK_ERR) {
msg_perr("Invalid serial number specified: %s.\n", arg);
free(arg);
return 1;
} if (ret != JAYLINK_OK) {
msg_perr("jaylink_parse_serial_number() failed: %s.\n", jaylink_strerror(ret));
free(arg);
return 1;
}
use_serial_number = true;
} else {
use_serial_number = false;
}
free(arg);
reset_cs = true;
arg = extract_programmer_param("cs");
if (arg) {
if (!strcasecmp(arg, "reset")) {
reset_cs = true;
} else if (!strcasecmp(arg, "trst")) {
reset_cs = false;
} else {
msg_perr("Invalid chip select pin specified: '%s'.\n", arg);
free(arg);
return 1;
}
}
free(arg);
if (reset_cs)
msg_pdbg("Using RESET as chip select signal.\n");
else
msg_pdbg("Using TRST as chip select signal.\n");
ret = jaylink_init(&jaylink_ctx);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_init() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
ret = jaylink_discovery_scan(jaylink_ctx, 0);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_discover_scan() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
struct jaylink_device **devs;
ret = jaylink_get_devices(jaylink_ctx, &devs, NULL);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_devices() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
if (!use_serial_number)
msg_pdbg("No device selected, using first device.\n");
size_t i;
struct jaylink_device *dev;
bool device_found = false;
for (i = 0; devs[i]; i++) {
if (use_serial_number) {
uint32_t tmp;
ret = jaylink_device_get_serial_number(devs[i], &tmp);
if (ret == JAYLINK_ERR_NOT_AVAILABLE) {
continue;
} else if (ret != JAYLINK_OK) {
msg_pwarn("jaylink_device_get_serial_number() failed: %s.\n",
jaylink_strerror(ret));
continue;
}
if (serial_number != tmp)
continue;
}
ret = jaylink_open(devs[i], &jaylink_devh);
if (ret == JAYLINK_OK) {
dev = devs[i];
device_found = true;
break;
}
jaylink_devh = NULL;
}
jaylink_free_devices(devs, true);
if (!device_found) {
msg_perr("No J-Link device found.\n");
return 1;
}
size_t length;
char *firmware_version;
ret = jaylink_get_firmware_version(jaylink_devh, &firmware_version,
&length);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_firmware_version() failed: %s.\n", jaylink_strerror(ret));
return 1;
} else if (length > 0) {
msg_pdbg("Firmware: %s\n", firmware_version);
free(firmware_version);
}
ret = jaylink_device_get_serial_number(dev, &serial_number);
if (ret == JAYLINK_OK) {
msg_pdbg("S/N: %" PRIu32 "\n", serial_number);
} else if (ret == JAYLINK_ERR_NOT_AVAILABLE) {
msg_pdbg("S/N: N/A\n");
} else {
msg_perr("jaylink_device_get_serial_number() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
uint8_t caps[JAYLINK_DEV_EXT_CAPS_SIZE];
memset(caps, 0, sizeof(caps));
ret = jaylink_get_caps(jaylink_devh, caps);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_caps() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_EXT_CAPS)) {
ret = jaylink_get_extended_caps(jaylink_devh, caps);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_available_interfaces() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
}
uint32_t ifaces;
ret = jaylink_get_available_interfaces(jaylink_devh, &ifaces);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_available_interfaces() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
if (!(ifaces & (1 << JAYLINK_TIF_JTAG))) {
msg_perr("Device does not support JTAG interface.\n");
return 1;
}
ret = jaylink_select_interface(jaylink_devh, JAYLINK_TIF_JTAG, NULL);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_select_interface() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
struct jaylink_hardware_status hwstat;
ret = jaylink_get_hardware_status(jaylink_devh, &hwstat);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_hardware_status() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
msg_pdbg("VTarget: %u.%03u V\n", hwstat.target_voltage / 1000,
hwstat.target_voltage % 1000);
if (hwstat.target_voltage < MIN_TARGET_VOLTAGE) {
msg_perr("Target voltage is below %u.%03u V. You need to attach VTref to the I/O voltage of "
"the chip.\n", MIN_TARGET_VOLTAGE / 1000, MIN_TARGET_VOLTAGE % 1000);
return 1;
}
struct jaylink_speed device_speeds;
device_speeds.freq = DEFAULT_FREQ;
device_speeds.div = DEFAULT_FREQ_DIV;
if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_SPEEDS)) {
ret = jaylink_get_speeds(jaylink_devh, &device_speeds);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_speeds() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
}
device_speeds.freq /= 1000;
msg_pdbg("Maximum SPI speed: %" PRIu32 " kHz\n", device_speeds.freq / device_speeds.div);
if (!speed) {
speed = device_speeds.freq / device_speeds.div;
msg_pdbg("SPI speed not specified, using %lu kHz.\n", speed);
}
if (speed > (device_speeds.freq / device_speeds.div)) {
msg_perr("Specified SPI speed of %lu kHz is too high. Maximum is %" PRIu32 " kHz.\n", speed,
device_speeds.freq / device_speeds.div);
return 1;
}
ret = jaylink_set_speed(jaylink_devh, speed);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_set_speed() failed: %s.\n", jaylink_strerror(ret));
return 1;
}
msg_pdbg("SPI speed: %lu kHz\n", speed);
/* Ensure that the CS signal is not active initially. */
if (!deassert_cs())
return 1;
register_spi_master(&spi_master_jlink_spi);
return 0;
}

View File

@ -117,6 +117,9 @@ enum programmer {
#endif #endif
#if CONFIG_DIGILENT_SPI == 1 #if CONFIG_DIGILENT_SPI == 1
PROGRAMMER_DIGILENT_SPI, PROGRAMMER_DIGILENT_SPI,
#endif
#if CONFIG_JLINK_SPI == 1
PROGRAMMER_JLINK_SPI,
#endif #endif
PROGRAMMER_INVALID /* This must always be the last entry. */ PROGRAMMER_INVALID /* This must always be the last entry. */
}; };
@ -573,6 +576,11 @@ int digilent_spi_init(void);
extern const struct dev_entry devs_digilent_spi[]; extern const struct dev_entry devs_digilent_spi[];
#endif #endif
/* jlink_spi.c */
#if CONFIG_JLINK_SPI == 1
int jlink_spi_init(void);
#endif
/* flashrom.c */ /* flashrom.c */
struct decode_sizes { struct decode_sizes {
uint32_t parallel; uint32_t parallel;
@ -641,6 +649,9 @@ enum spi_controller {
#if CONFIG_DIGILENT_SPI == 1 #if CONFIG_DIGILENT_SPI == 1
SPI_CONTROLLER_DIGILENT_SPI, SPI_CONTROLLER_DIGILENT_SPI,
#endif #endif
#if CONFIG_JLINK_SPI == 1
SPI_CONTROLLER_JLINK_SPI,
#endif
}; };
#define MAX_DATA_UNSPECIFIED 0 #define MAX_DATA_UNSPECIFIED 0