blob: 61338b6bcfc55fc9173f56e78db1bf12dbbc97a9 [file] [log] [blame]
Marc Schink3578ec62016-03-17 16:23:03 +01001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2016 Marc Schink <flashrom-dev@marcschink.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17/*
18 * Driver for the J-Link hardware by SEGGER.
19 * See https://www.segger.com/ for more info.
20 */
21
22#include <stdlib.h>
23#include <stdbool.h>
24#include <stdint.h>
25#include <string.h>
26#include <errno.h>
27#include <libjaylink/libjaylink.h>
28
29#include "flash.h"
30#include "programmer.h"
31#include "spi.h"
32
33/*
34 * Maximum number of bytes that can be transferred at once via the JTAG
35 * interface, see jaylink_jtag_io().
36 */
Marc Schinka8c0b682020-08-22 11:29:22 +020037#define JTAG_MAX_TRANSFER_SIZE (32768 / 8)
Marc Schink3578ec62016-03-17 16:23:03 +010038
39/*
40 * Default base frequency in Hz. Used when the base frequency can not be
41 * retrieved from the device.
42 */
43#define DEFAULT_FREQ 16000000
44
45/*
46 * Default frequency divider. Used when the frequency divider can not be
47 * retrieved from the device.
48 */
49#define DEFAULT_FREQ_DIV 4
50
51/* Minimum target voltage required for operation in mV. */
52#define MIN_TARGET_VOLTAGE 1200
53
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +100054struct jlink_spi_data {
Anastasia Klimchuke4a97912021-04-26 10:58:16 +100055 struct jaylink_context *ctx;
56 struct jaylink_device_handle *devh;
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +100057 bool reset_cs;
58 bool enable_target_power;
59};
Marc Schink3578ec62016-03-17 16:23:03 +010060
Anastasia Klimchuke4a97912021-04-26 10:58:16 +100061static bool assert_cs(struct jlink_spi_data *jlink_data)
Marc Schink3578ec62016-03-17 16:23:03 +010062{
63 int ret;
64
Anastasia Klimchuke4a97912021-04-26 10:58:16 +100065 if (jlink_data->reset_cs) {
66 ret = jaylink_clear_reset(jlink_data->devh);
Marc Schink3578ec62016-03-17 16:23:03 +010067
68 if (ret != JAYLINK_OK) {
69 msg_perr("jaylink_clear_reset() failed: %s.\n", jaylink_strerror(ret));
70 return false;
71 }
72 } else {
Anastasia Klimchuke4a97912021-04-26 10:58:16 +100073 ret = jaylink_jtag_clear_trst(jlink_data->devh);
Marc Schink3578ec62016-03-17 16:23:03 +010074
75 if (ret != JAYLINK_OK) {
76 msg_perr("jaylink_jtag_clear_trst() failed: %s.\n", jaylink_strerror(ret));
77 return false;
78 }
79 }
80
81 return true;
82}
83
Anastasia Klimchuke4a97912021-04-26 10:58:16 +100084static bool deassert_cs(struct jlink_spi_data *jlink_data)
Marc Schink3578ec62016-03-17 16:23:03 +010085{
86 int ret;
87
Anastasia Klimchuke4a97912021-04-26 10:58:16 +100088 if (jlink_data->reset_cs) {
89 ret = jaylink_set_reset(jlink_data->devh);
Marc Schink3578ec62016-03-17 16:23:03 +010090
91 if (ret != JAYLINK_OK) {
92 msg_perr("jaylink_set_reset() failed: %s.\n", jaylink_strerror(ret));
93 return false;
94 }
95 } else {
Anastasia Klimchuke4a97912021-04-26 10:58:16 +100096 ret = jaylink_jtag_set_trst(jlink_data->devh);
Marc Schink3578ec62016-03-17 16:23:03 +010097
98 if (ret != JAYLINK_OK) {
99 msg_perr("jaylink_jtag_set_trst() failed: %s.\n", jaylink_strerror(ret));
100 return false;
101 }
102 }
103
104 return true;
105}
106
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000107static int jlink_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
Marc Schink3578ec62016-03-17 16:23:03 +0100108 const unsigned char *writearr, unsigned char *readarr)
109{
110 uint32_t length;
111 uint8_t *buffer;
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000112 struct jlink_spi_data *jlink_data = flash->mst->spi.data;
Marc Schink3578ec62016-03-17 16:23:03 +0100113
114 length = writecnt + readcnt;
115
116 if (length > JTAG_MAX_TRANSFER_SIZE)
117 return SPI_INVALID_LENGTH;
118
119 buffer = malloc(length);
120
121 if (!buffer) {
122 msg_perr("Memory allocation failed.\n");
123 return SPI_GENERIC_ERROR;
124 }
125
126 /* Reverse all bytes because the device transfers data LSB first. */
127 reverse_bytes(buffer, writearr, writecnt);
128
129 memset(buffer + writecnt, 0x00, readcnt);
130
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000131 if (!assert_cs(jlink_data)) {
Marc Schink3578ec62016-03-17 16:23:03 +0100132 free(buffer);
133 return SPI_PROGRAMMER_ERROR;
134 }
135
136 int ret;
137
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000138 ret = jaylink_jtag_io(jlink_data->devh,
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +1000139 buffer, buffer, buffer, length * 8, JAYLINK_JTAG_VERSION_2);
Marc Schink3578ec62016-03-17 16:23:03 +0100140
141 if (ret != JAYLINK_OK) {
Angel Pons281ed262021-04-16 10:55:03 +0200142 msg_perr("jaylink_jtag_io() failed: %s.\n", jaylink_strerror(ret));
Marc Schink3578ec62016-03-17 16:23:03 +0100143 free(buffer);
144 return SPI_PROGRAMMER_ERROR;
145 }
146
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000147 if (!deassert_cs(jlink_data)) {
Marc Schink3578ec62016-03-17 16:23:03 +0100148 free(buffer);
149 return SPI_PROGRAMMER_ERROR;
150 }
151
152 /* Reverse all bytes because the device transfers data LSB first. */
153 reverse_bytes(readarr, buffer + writecnt, readcnt);
154 free(buffer);
155
156 return 0;
157}
158
Anastasia Klimchukc63d9182021-07-06 16:18:44 +1000159static int jlink_spi_shutdown(void *data);
160
Nico Huber03f3a6d2021-05-11 17:53:34 +0200161static const struct spi_master spi_master_jlink_spi = {
Marc Schink3578ec62016-03-17 16:23:03 +0100162 /* Maximum data read size in one go (excluding opcode+address). */
163 .max_data_read = JTAG_MAX_TRANSFER_SIZE - 5,
164 /* Maximum data write size in one go (excluding opcode+address). */
165 .max_data_write = JTAG_MAX_TRANSFER_SIZE - 5,
166 .command = jlink_spi_send_command,
167 .multicommand = default_spi_send_multicommand,
168 .read = default_spi_read,
169 .write_256 = default_spi_write_256,
Marc Schink3578ec62016-03-17 16:23:03 +0100170 .features = SPI_MASTER_4BA,
Anastasia Klimchukc63d9182021-07-06 16:18:44 +1000171 .shutdown = jlink_spi_shutdown,
Aarya Chaumal0cea7532022-07-04 18:21:50 +0530172 .probe_opcode = default_spi_probe_opcode,
Marc Schink3578ec62016-03-17 16:23:03 +0100173};
174
175static int jlink_spi_shutdown(void *data)
176{
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000177 struct jlink_spi_data *jlink_data = data;
178 if (jlink_data->devh) {
179 if (jlink_data->enable_target_power) {
180 int ret = jaylink_set_target_power(jlink_data->devh, false);
Marc Schink137f02f2020-08-23 16:19:44 +0200181
182 if (ret != JAYLINK_OK) {
183 msg_perr("jaylink_set_target_power() failed: %s.\n",
184 jaylink_strerror(ret));
185 }
186 }
187
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000188 jaylink_close(jlink_data->devh);
Marc Schink137f02f2020-08-23 16:19:44 +0200189 }
Marc Schink3578ec62016-03-17 16:23:03 +0100190
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000191 jaylink_exit(jlink_data->ctx);
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +1000192
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000193 /* jlink_data->ctx, jlink_data->devh are freed by jaylink_close and jaylink_exit */
194 free(jlink_data);
Marc Schink3578ec62016-03-17 16:23:03 +0100195 return 0;
196}
197
Nico Hubere3a26882023-01-11 21:45:51 +0100198static int jlink_spi_init(struct flashprog_programmer *const prog)
Marc Schink3578ec62016-03-17 16:23:03 +0100199{
200 char *arg;
201 unsigned long speed = 0;
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +1000202 struct jaylink_context *jaylink_ctx = NULL;
203 struct jaylink_device_handle *jaylink_devh = NULL;
204 bool reset_cs;
205 bool enable_target_power;
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000206 struct jlink_spi_data *jlink_data = NULL;
Marc Schink3578ec62016-03-17 16:23:03 +0100207
Marc Schink3578ec62016-03-17 16:23:03 +0100208 arg = extract_programmer_param("spispeed");
209
210 if (arg) {
211 char *endptr;
212
213 errno = 0;
214 speed = strtoul(arg, &endptr, 10);
215
216 if (*endptr != '\0' || errno != 0) {
217 msg_perr("Invalid SPI speed specified: %s.\n", arg);
218 free(arg);
219 return 1;
220 }
221
222 if (speed < 1) {
223 msg_perr("SPI speed must be at least 1 kHz.\n");
224 free(arg);
225 return 1;
226 }
227 }
228
229 free(arg);
230
231 int ret;
232 bool use_serial_number;
233 uint32_t serial_number;
234
235 arg = extract_programmer_param("serial");
236
237 if (arg) {
238 if (!strlen(arg)) {
Angel Pons281ed262021-04-16 10:55:03 +0200239 msg_perr("Empty serial number specified.\n");
Marc Schink3578ec62016-03-17 16:23:03 +0100240 free(arg);
241 return 1;
242 }
243
244 ret = jaylink_parse_serial_number(arg, &serial_number);
245
246 if (ret == JAYLINK_ERR) {
247 msg_perr("Invalid serial number specified: %s.\n", arg);
248 free(arg);
249 return 1;
250 } if (ret != JAYLINK_OK) {
251 msg_perr("jaylink_parse_serial_number() failed: %s.\n", jaylink_strerror(ret));
252 free(arg);
253 return 1;
254 }
255
256 use_serial_number = true;
257 } else {
258 use_serial_number = false;
259 }
260
261 free(arg);
262
263 reset_cs = true;
264 arg = extract_programmer_param("cs");
265
266 if (arg) {
267 if (!strcasecmp(arg, "reset")) {
268 reset_cs = true;
269 } else if (!strcasecmp(arg, "trst")) {
270 reset_cs = false;
271 } else {
272 msg_perr("Invalid chip select pin specified: '%s'.\n", arg);
273 free(arg);
274 return 1;
275 }
276 }
277
278 free(arg);
279
280 if (reset_cs)
281 msg_pdbg("Using RESET as chip select signal.\n");
282 else
283 msg_pdbg("Using TRST as chip select signal.\n");
284
Marc Schink137f02f2020-08-23 16:19:44 +0200285 enable_target_power = false;
286 arg = extract_programmer_param("power");
287
288 if (arg) {
289 if (!strcasecmp(arg, "on")) {
290 enable_target_power = true;
291 } else {
292 msg_perr("Invalid value for 'power' argument: '%s'.\n", arg);
293 free(arg);
294 return 1;
295 }
296 }
297
298 free(arg);
299
Marc Schink3578ec62016-03-17 16:23:03 +0100300 ret = jaylink_init(&jaylink_ctx);
301
302 if (ret != JAYLINK_OK) {
303 msg_perr("jaylink_init() failed: %s.\n", jaylink_strerror(ret));
304 return 1;
305 }
306
307 ret = jaylink_discovery_scan(jaylink_ctx, 0);
308
309 if (ret != JAYLINK_OK) {
Angel Pons281ed262021-04-16 10:55:03 +0200310 msg_perr("jaylink_discovery_scan() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000311 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100312 }
313
314 struct jaylink_device **devs;
315
316 ret = jaylink_get_devices(jaylink_ctx, &devs, NULL);
317
318 if (ret != JAYLINK_OK) {
319 msg_perr("jaylink_get_devices() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000320 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100321 }
322
323 if (!use_serial_number)
324 msg_pdbg("No device selected, using first device.\n");
325
326 size_t i;
327 struct jaylink_device *dev;
328 bool device_found = false;
329
330 for (i = 0; devs[i]; i++) {
331 if (use_serial_number) {
332 uint32_t tmp;
333
334 ret = jaylink_device_get_serial_number(devs[i], &tmp);
335
336 if (ret == JAYLINK_ERR_NOT_AVAILABLE) {
337 continue;
338 } else if (ret != JAYLINK_OK) {
339 msg_pwarn("jaylink_device_get_serial_number() failed: %s.\n",
340 jaylink_strerror(ret));
341 continue;
342 }
343
344 if (serial_number != tmp)
345 continue;
346 }
347
348 ret = jaylink_open(devs[i], &jaylink_devh);
349
350 if (ret == JAYLINK_OK) {
351 dev = devs[i];
352 device_found = true;
353 break;
354 }
355
356 jaylink_devh = NULL;
357 }
358
359 jaylink_free_devices(devs, true);
360
361 if (!device_found) {
362 msg_perr("No J-Link device found.\n");
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000363 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100364 }
365
366 size_t length;
367 char *firmware_version;
368
369 ret = jaylink_get_firmware_version(jaylink_devh, &firmware_version,
370 &length);
371
372 if (ret != JAYLINK_OK) {
373 msg_perr("jaylink_get_firmware_version() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000374 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100375 } else if (length > 0) {
376 msg_pdbg("Firmware: %s\n", firmware_version);
377 free(firmware_version);
378 }
379
380 ret = jaylink_device_get_serial_number(dev, &serial_number);
381
382 if (ret == JAYLINK_OK) {
383 msg_pdbg("S/N: %" PRIu32 "\n", serial_number);
384 } else if (ret == JAYLINK_ERR_NOT_AVAILABLE) {
385 msg_pdbg("S/N: N/A\n");
386 } else {
387 msg_perr("jaylink_device_get_serial_number() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000388 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100389 }
390
Angel Pons7e134562021-06-07 13:29:13 +0200391 uint8_t caps[JAYLINK_DEV_EXT_CAPS_SIZE] = { 0 };
Marc Schink3578ec62016-03-17 16:23:03 +0100392
Marc Schink3578ec62016-03-17 16:23:03 +0100393 ret = jaylink_get_caps(jaylink_devh, caps);
394
395 if (ret != JAYLINK_OK) {
396 msg_perr("jaylink_get_caps() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000397 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100398 }
399
400 if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_EXT_CAPS)) {
401 ret = jaylink_get_extended_caps(jaylink_devh, caps);
402
403 if (ret != JAYLINK_OK) {
Angel Pons281ed262021-04-16 10:55:03 +0200404 msg_perr("jaylink_get_extended_caps() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000405 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100406 }
407 }
408
Marc Schink137f02f2020-08-23 16:19:44 +0200409 if (enable_target_power) {
410 if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SET_TARGET_POWER)) {
411 msg_perr("Device does not support target power.\n");
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000412 goto init_err;
Marc Schink137f02f2020-08-23 16:19:44 +0200413 }
414 }
415
Marc Schink3578ec62016-03-17 16:23:03 +0100416 uint32_t ifaces;
417
418 ret = jaylink_get_available_interfaces(jaylink_devh, &ifaces);
419
420 if (ret != JAYLINK_OK) {
421 msg_perr("jaylink_get_available_interfaces() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000422 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100423 }
424
425 if (!(ifaces & (1 << JAYLINK_TIF_JTAG))) {
426 msg_perr("Device does not support JTAG interface.\n");
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000427 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100428 }
429
430 ret = jaylink_select_interface(jaylink_devh, JAYLINK_TIF_JTAG, NULL);
431
432 if (ret != JAYLINK_OK) {
433 msg_perr("jaylink_select_interface() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000434 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100435 }
436
Marc Schink137f02f2020-08-23 16:19:44 +0200437 if (enable_target_power) {
438 ret = jaylink_set_target_power(jaylink_devh, true);
439
440 if (ret != JAYLINK_OK) {
441 msg_perr("jaylink_set_target_power() failed: %s.\n", jaylink_strerror(ret));
442 return 1;
443 }
444
445 /* Wait some time until the target is powered up. */
446 internal_sleep(10000);
447 }
448
Marc Schink3578ec62016-03-17 16:23:03 +0100449 struct jaylink_hardware_status hwstat;
450
451 ret = jaylink_get_hardware_status(jaylink_devh, &hwstat);
452
453 if (ret != JAYLINK_OK) {
454 msg_perr("jaylink_get_hardware_status() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000455 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100456 }
457
458 msg_pdbg("VTarget: %u.%03u V\n", hwstat.target_voltage / 1000,
459 hwstat.target_voltage % 1000);
460
461 if (hwstat.target_voltage < MIN_TARGET_VOLTAGE) {
462 msg_perr("Target voltage is below %u.%03u V. You need to attach VTref to the I/O voltage of "
463 "the chip.\n", MIN_TARGET_VOLTAGE / 1000, MIN_TARGET_VOLTAGE % 1000);
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000464 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100465 }
466
467 struct jaylink_speed device_speeds;
468
469 device_speeds.freq = DEFAULT_FREQ;
470 device_speeds.div = DEFAULT_FREQ_DIV;
471
472 if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_GET_SPEEDS)) {
473 ret = jaylink_get_speeds(jaylink_devh, &device_speeds);
474
475 if (ret != JAYLINK_OK) {
476 msg_perr("jaylink_get_speeds() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000477 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100478 }
479 }
480
481 device_speeds.freq /= 1000;
482
483 msg_pdbg("Maximum SPI speed: %" PRIu32 " kHz\n", device_speeds.freq / device_speeds.div);
484
485 if (!speed) {
486 speed = device_speeds.freq / device_speeds.div;
487 msg_pdbg("SPI speed not specified, using %lu kHz.\n", speed);
488 }
489
490 if (speed > (device_speeds.freq / device_speeds.div)) {
491 msg_perr("Specified SPI speed of %lu kHz is too high. Maximum is %" PRIu32 " kHz.\n", speed,
492 device_speeds.freq / device_speeds.div);
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000493 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100494 }
495
496 ret = jaylink_set_speed(jaylink_devh, speed);
497
498 if (ret != JAYLINK_OK) {
499 msg_perr("jaylink_set_speed() failed: %s.\n", jaylink_strerror(ret));
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000500 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100501 }
502
503 msg_pdbg("SPI speed: %lu kHz\n", speed);
504
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000505 jlink_data = calloc(1, sizeof(*jlink_data));
506 if (!jlink_data) {
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +1000507 msg_perr("Unable to allocate space for SPI master data\n");
508 goto init_err;
509 }
510
511 /* jaylink_ctx, jaylink_devh are allocated by jaylink_init and jaylink_open */
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000512 jlink_data->ctx = jaylink_ctx;
513 jlink_data->devh = jaylink_devh;
514 jlink_data->reset_cs = reset_cs;
515 jlink_data->enable_target_power = enable_target_power;
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +1000516
Marc Schink3578ec62016-03-17 16:23:03 +0100517 /* Ensure that the CS signal is not active initially. */
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000518 if (!deassert_cs(jlink_data))
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000519 goto init_err;
Marc Schink3578ec62016-03-17 16:23:03 +0100520
Nico Huber89569d62023-01-12 23:31:40 +0100521 return register_spi_master(&spi_master_jlink_spi, 0, jlink_data);
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000522
523init_err:
524 if (jaylink_devh)
525 jaylink_close(jaylink_devh);
526
527 jaylink_exit(jaylink_ctx);
528
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +1000529 /* jaylink_ctx, jaylink_devh are freed by jaylink_close and jaylink_exit */
Anastasia Klimchuke4a97912021-04-26 10:58:16 +1000530 if (jlink_data)
531 free(jlink_data);
Anastasia Klimchuk9e698f92021-04-21 12:21:49 +1000532
Anastasia Klimchuk00c2e802021-04-14 09:52:36 +1000533 return 1;
Marc Schink3578ec62016-03-17 16:23:03 +0100534}
Thomas Heijligencc853d82021-05-04 15:32:17 +0200535
536const struct programmer_entry programmer_jlink_spi = {
537 .name = "jlink_spi",
538 .type = OTHER,
539 .init = jlink_spi_init,
540 .devs.note = "SEGGER J-Link and compatible devices\n",
Thomas Heijligencc853d82021-05-04 15:32:17 +0200541};