diff options
Diffstat (limited to 'jlink_spi.c')
-rw-r--r-- | jlink_spi.c | 237 |
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", +}; |