summaryrefslogtreecommitdiffstats
path: root/jlink_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'jlink_spi.c')
-rw-r--r--jlink_spi.c237
1 files changed, 173 insertions, 64 deletions
diff --git a/jlink_spi.c b/jlink_spi.c
index 3a73c01b1..447629cce 100644
--- a/jlink_spi.c
+++ b/jlink_spi.c
@@ -51,51 +51,74 @@
/* 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;
+enum cs_wiring {
+ CS_RESET, /* nCS is wired to nRESET(pin 15) */
+ CS_TRST, /* nCS is wired to nTRST(pin 3) */
+ CS_TMS, /* nCS is wired to TMS/nCS(pin 7) */
+};
+
+struct jlink_spi_data {
+ struct jaylink_context *ctx;
+ struct jaylink_device_handle *devh;
+ enum cs_wiring cs;
+ bool enable_target_power;
+};
-static bool assert_cs(void)
+static bool assert_cs(struct jlink_spi_data *jlink_data)
{
int ret;
- if (reset_cs) {
- ret = jaylink_clear_reset(jaylink_devh);
+ if (jlink_data->cs == CS_RESET) {
+ ret = jaylink_clear_reset(jlink_data->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);
+ } else if (jlink_data->cs == CS_TRST) {
+ ret = jaylink_jtag_clear_trst(jlink_data->devh);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_jtag_clear_trst() failed: %s.\n", jaylink_strerror(ret));
return false;
}
+ } else {
+ ret = jaylink_jtag_clear_tms(jlink_data->devh);
+
+ if (ret != JAYLINK_OK) {
+ msg_perr("jaylink_jtag_clear_tms() failed: %s.\n", jaylink_strerror(ret));
+ return false;
+ }
}
return true;
}
-static bool deassert_cs(void)
+static bool deassert_cs(struct jlink_spi_data *jlink_data)
{
int ret;
- if (reset_cs) {
- ret = jaylink_set_reset(jaylink_devh);
+ if (jlink_data->cs == CS_RESET) {
+ ret = jaylink_set_reset(jlink_data->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);
+ } else if (jlink_data->cs == CS_TRST) {
+ ret = jaylink_jtag_set_trst(jlink_data->devh);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_jtag_set_trst() failed: %s.\n", jaylink_strerror(ret));
return false;
}
+ } else {
+ ret = jaylink_jtag_set_tms(jlink_data->devh);
+
+ if (ret != JAYLINK_OK) {
+ msg_perr("jaylink_jtag_set_tms() failed: %s.\n", jaylink_strerror(ret));
+ return false;
+ }
}
return true;
@@ -106,6 +129,8 @@ static int jlink_spi_send_command(const struct flashctx *flash, unsigned int wri
{
uint32_t length;
uint8_t *buffer;
+ static const uint8_t zeros[JTAG_MAX_TRANSFER_SIZE] = {0};
+ struct jlink_spi_data *jlink_data = flash->mst->spi.data;
length = writecnt + readcnt;
@@ -124,22 +149,26 @@ static int jlink_spi_send_command(const struct flashctx *flash, unsigned int wri
memset(buffer + writecnt, 0x00, readcnt);
- if (!assert_cs()) {
+ if (!assert_cs(jlink_data)) {
free(buffer);
return SPI_PROGRAMMER_ERROR;
}
+ /* If CS is wired to TMS, TMS should remain zero. */
+ const uint8_t *tms_buffer = jlink_data->cs == CS_TMS ? zeros : buffer;
+
int ret;
- ret = jaylink_jtag_io(jaylink_devh, buffer, buffer, buffer, length * 8, JAYLINK_JTAG_VERSION_2);
+ ret = jaylink_jtag_io(jlink_data->devh,
+ tms_buffer, buffer, buffer, length * 8, JAYLINK_JTAG_VERSION_2);
if (ret != JAYLINK_OK) {
- msg_perr("jaylink_jag_io() failed: %s.\n", jaylink_strerror(ret));
+ msg_perr("jaylink_jtag_io() failed: %s.\n", jaylink_strerror(ret));
free(buffer);
return SPI_PROGRAMMER_ERROR;
}
- if (!deassert_cs()) {
+ if (!deassert_cs(jlink_data)) {
free(buffer);
return SPI_PROGRAMMER_ERROR;
}
@@ -151,37 +180,51 @@ static int jlink_spi_send_command(const struct flashctx *flash, unsigned int wri
return 0;
}
+static int jlink_spi_shutdown(void *data)
+{
+ struct jlink_spi_data *jlink_data = data;
+
+ if (jlink_data->enable_target_power) {
+ int ret = jaylink_set_target_power(jlink_data->devh, false);
+
+ if (ret != JAYLINK_OK) {
+ msg_perr("jaylink_set_target_power() failed: %s.\n",
+ jaylink_strerror(ret));
+ }
+ }
+
+ if (jlink_data->devh)
+ jaylink_close(jlink_data->devh);
+
+ jaylink_exit(jlink_data->ctx);
+ /* jlink_data->ctx, jlink_data->devh are freed by jaylink_close and jaylink_exit */
+ free(jlink_data);
+ return 0;
+}
+
static const struct spi_master spi_master_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,
+ .shutdown = jlink_spi_shutdown,
};
-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)
+static int jlink_spi_init(const struct programmer_cfg *cfg)
{
char *arg;
unsigned long speed = 0;
+ struct jaylink_context *jaylink_ctx = NULL;
+ struct jaylink_device_handle *jaylink_devh = NULL;
+ enum cs_wiring cs;
+ struct jlink_spi_data *jlink_data = NULL;
+ bool enable_target_power;
- register_shutdown(jlink_spi_shutdown, NULL);
-
- arg = extract_programmer_param("spispeed");
+ arg = extract_programmer_param_str(cfg, "spispeed");
if (arg) {
char *endptr;
@@ -208,11 +251,11 @@ int jlink_spi_init(void)
bool use_serial_number;
uint32_t serial_number;
- arg = extract_programmer_param("serial");
+ arg = extract_programmer_param_str(cfg, "serial");
if (arg) {
if (!strlen(arg)) {
- msg_perr("Emptpy serial number specified.\n");
+ msg_perr("Empty serial number specified.\n");
free(arg);
return 1;
}
@@ -236,14 +279,16 @@ int jlink_spi_init(void)
free(arg);
- reset_cs = true;
- arg = extract_programmer_param("cs");
+ cs = CS_RESET;
+ arg = extract_programmer_param_str(cfg, "cs");
if (arg) {
if (!strcasecmp(arg, "reset")) {
- reset_cs = true;
+ cs = CS_RESET;
} else if (!strcasecmp(arg, "trst")) {
- reset_cs = false;
+ cs = CS_TRST;
+ } else if (!strcasecmp(arg, "tms")) {
+ cs = CS_TMS;
} else {
msg_perr("Invalid chip select pin specified: '%s'.\n", arg);
free(arg);
@@ -253,10 +298,27 @@ int jlink_spi_init(void)
free(arg);
- if (reset_cs)
+ if (cs == CS_RESET)
msg_pdbg("Using RESET as chip select signal.\n");
- else
+ else if (cs == CS_TRST)
msg_pdbg("Using TRST as chip select signal.\n");
+ else
+ msg_pdbg("Using TMS/CS as chip select signal.\n");
+
+ enable_target_power = false;
+ arg = extract_programmer_param_str(cfg, "power");
+
+ if (arg) {
+ if (!strcasecmp(arg, "on")) {
+ enable_target_power = true;
+ } else {
+ msg_perr("Invalid value for 'power' argument: '%s'.\n", arg);
+ free(arg);
+ return 1;
+ }
+ }
+
+ free(arg);
ret = jaylink_init(&jaylink_ctx);
@@ -268,8 +330,8 @@ int jlink_spi_init(void)
ret = jaylink_discovery_scan(jaylink_ctx, 0);
if (ret != JAYLINK_OK) {
- msg_perr("jaylink_discover_scan() failed: %s.\n", jaylink_strerror(ret));
- return 1;
+ msg_perr("jaylink_discovery_scan() failed: %s.\n", jaylink_strerror(ret));
+ goto init_err;
}
struct jaylink_device **devs;
@@ -278,7 +340,7 @@ int jlink_spi_init(void)
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_devices() failed: %s.\n", jaylink_strerror(ret));
- return 1;
+ goto init_err;
}
if (!use_serial_number)
@@ -321,7 +383,7 @@ int jlink_spi_init(void)
if (!device_found) {
msg_perr("No J-Link device found.\n");
- return 1;
+ goto init_err;
}
size_t length;
@@ -332,7 +394,7 @@ int jlink_spi_init(void)
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_firmware_version() failed: %s.\n", jaylink_strerror(ret));
- return 1;
+ goto init_err;
} else if (length > 0) {
msg_pdbg("Firmware: %s\n", firmware_version);
free(firmware_version);
@@ -346,25 +408,31 @@ int jlink_spi_init(void)
msg_pdbg("S/N: N/A\n");
} else {
msg_perr("jaylink_device_get_serial_number() failed: %s.\n", jaylink_strerror(ret));
- return 1;
+ goto init_err;
}
- uint8_t caps[JAYLINK_DEV_EXT_CAPS_SIZE];
+ uint8_t caps[JAYLINK_DEV_EXT_CAPS_SIZE] = { 0 };
- 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;
+ goto init_err;
}
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;
+ msg_perr("jaylink_get_extended_caps() failed: %s.\n", jaylink_strerror(ret));
+ goto init_err;
+ }
+ }
+
+ if (enable_target_power) {
+ if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SET_TARGET_POWER)) {
+ msg_perr("Device does not support target power.\n");
+ goto init_err;
}
}
@@ -374,19 +442,31 @@ int jlink_spi_init(void)
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_available_interfaces() failed: %s.\n", jaylink_strerror(ret));
- return 1;
+ goto init_err;
}
if (!(ifaces & (1 << JAYLINK_TIF_JTAG))) {
msg_perr("Device does not support JTAG interface.\n");
- return 1;
+ goto init_err;
}
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;
+ goto init_err;
+ }
+
+ if (enable_target_power) {
+ ret = jaylink_set_target_power(jaylink_devh, true);
+
+ if (ret != JAYLINK_OK) {
+ msg_perr("jaylink_set_target_power() failed: %s.\n", jaylink_strerror(ret));
+ goto init_err;
+ }
+
+ /* Wait some time until the target is powered up. */
+ internal_sleep(10000);
}
struct jaylink_hardware_status hwstat;
@@ -395,7 +475,7 @@ int jlink_spi_init(void)
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_hardware_status() failed: %s.\n", jaylink_strerror(ret));
- return 1;
+ goto init_err;
}
msg_pdbg("VTarget: %u.%03u V\n", hwstat.target_voltage / 1000,
@@ -404,7 +484,7 @@ int jlink_spi_init(void)
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;
+ goto init_err;
}
struct jaylink_speed device_speeds;
@@ -417,7 +497,7 @@ int jlink_spi_init(void)
if (ret != JAYLINK_OK) {
msg_perr("jaylink_get_speeds() failed: %s.\n", jaylink_strerror(ret));
- return 1;
+ goto init_err;
}
}
@@ -433,23 +513,52 @@ int jlink_spi_init(void)
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;
+ goto init_err;
}
ret = jaylink_set_speed(jaylink_devh, speed);
if (ret != JAYLINK_OK) {
msg_perr("jaylink_set_speed() failed: %s.\n", jaylink_strerror(ret));
- return 1;
+ goto init_err;
}
msg_pdbg("SPI speed: %lu kHz\n", speed);
+ jlink_data = calloc(1, sizeof(*jlink_data));
+ if (!jlink_data) {
+ msg_perr("Unable to allocate space for SPI master data\n");
+ goto init_err;
+ }
+
+ /* jaylink_ctx, jaylink_devh are allocated by jaylink_init and jaylink_open */
+ jlink_data->ctx = jaylink_ctx;
+ jlink_data->devh = jaylink_devh;
+ jlink_data->cs = cs;
+ jlink_data->enable_target_power = enable_target_power;
+
/* Ensure that the CS signal is not active initially. */
- if (!deassert_cs())
- return 1;
+ if (!deassert_cs(jlink_data))
+ goto init_err;
- register_spi_master(&spi_master_jlink_spi);
+ return register_spi_master(&spi_master_jlink_spi, jlink_data);
- return 0;
+init_err:
+ if (jaylink_devh)
+ jaylink_close(jaylink_devh);
+
+ jaylink_exit(jaylink_ctx);
+
+ /* jaylink_ctx, jaylink_devh are freed by jaylink_close and jaylink_exit */
+ if (jlink_data)
+ free(jlink_data);
+
+ return 1;
}
+
+const struct programmer_entry programmer_jlink_spi = {
+ .name = "jlink_spi",
+ .type = OTHER,
+ .init = jlink_spi_init,
+ .devs.note = "SEGGER J-Link and compatible devices\n",
+};