blob: fbb9b1b841789ea0f1871dfe490620ad61905216 [file] [log] [blame]
Miklós Márton324929c2019-08-01 19:14:10 +02001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2019 Miklós Márton martonmiklosqdev@gmail.com
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/*
19 * Driver for programming SPI flash chips using the SPI port
20 * of the STMicroelectronics's STLINK-V3 programmer/debugger.
21 *
22 * The implementation is inspired by the ST's STLINK-V3-BRIDGE C++ API:
23 * https://www.st.com/en/development-tools/stlink-v3-bridge.html
24 */
25
26#include "flash.h"
27#include "programmer.h"
28#include "spi.h"
29
30#include <libusb.h>
31#include <limits.h>
32#include <stdint.h>
33#include <stdlib.h>
34#include <string.h>
35
36enum fw_version_check_result {
37 FW_VERSION_OK,
38 FW_VERSION_OLD,
39};
40
41enum spi_prescaler {
42 SPI_BAUDRATEPRESCALER_2 = 0,
43 SPI_BAUDRATEPRESCALER_4 = 1,
44 SPI_BAUDRATEPRESCALER_8 = 2,
45 SPI_BAUDRATEPRESCALER_16 = 3,
46 SPI_BAUDRATEPRESCALER_32 = 4,
47 SPI_BAUDRATEPRESCALER_64 = 5,
48 SPI_BAUDRATEPRESCALER_128 = 6,
49 SPI_BAUDRATEPRESCALER_256 = 7
50};
51
52enum spi_dir {
53 SPI_DIRECTION_2LINES_FULLDUPLEX = 0,
54 SPI_DIRECTION_2LINES_RXONLY = 1,
55 SPI_DIRECTION_1LINE_RX = 2,
56 SPI_DIRECTION_1LINE_TX = 3
57};
58
59enum spi_mode {
60 SPI_MODE_SLAVE = 0,
61 SPI_MODE_MASTER = 1
62};
63
64enum spi_datasize {
65 SPI_DATASIZE_16B = 0,
66 SPI_DATASIZE_8B = 1
67};
68
69enum spi_cpol {
70 SPI_CPOL_LOW = 0,
71 SPI_CPOL_HIGH = 1
72};
73
74enum spi_cpha {
75 SPI_CPHA_1EDGE = 0,
76 SPI_CPHA_2EDGE = 1
77};
78
79enum spi_firstbit {
80 SPI_FIRSTBIT_LSB = 0,
81 SPI_FIRSTBIT_MSB = 1
82};
83
84// ST calls the Chip select (CS) NSS == Negated Slave Select
85enum spi_nss {
86 SPI_NSS_SOFT = 0,
87 SPI_NSS_HARD = 1
88};
89
90enum spi_nss_level {
91 SPI_NSS_LOW = 0,
92 SPI_NSS_HIGH = 1
93};
94
95#define ST_GETVERSION_EXT 0xFB
96
97#define STLINK_BRIDGE_COMMAND 0xFC
98#define STLINK_BRIDGE_CLOSE 0x01
99#define STLINK_BRIDGE_GET_RWCMD_STATUS 0x02
100#define STLINK_BRIDGE_GET_CLOCK 0x03
101#define STLINK_BRIDGE_INIT_SPI 0x20
102#define STLINK_BRIDGE_WRITE_SPI 0x21
103#define STLINK_BRIDGE_READ_SPI 0x22
104#define STLINK_BRIDGE_CS_SPI 0x23
105
106#define STLINK_BRIDGE_SPI_ERROR 0x02
107
108#define STLINK_SPI_COM 0x02
109
110#define STLINK_EP_OUT 0x06
111#define STLINK_EP_IN 0x86
112
113#define FIRST_COMPATIBLE_BRIDGE_FW_VERSION 3
114
115#define USB_TIMEOUT_IN_MS 5000
116
117const struct dev_entry devs_stlinkv3_spi[] = {
118 {0x0483, 0x374F, OK, "STMicroelectronics", "STLINK-V3"},
119 {0}
120};
121
122static struct libusb_context *usb_ctx;
123static libusb_device_handle *stlinkv3_handle;
124
125static int stlinkv3_command(uint8_t *command, size_t command_length,
126 uint8_t *answer, size_t answer_length, const char *command_name)
127{
128 int actual_length = 0;
129 int rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT,
130 command, command_length,
131 &actual_length, USB_TIMEOUT_IN_MS);
132 if (rc != LIBUSB_TRANSFER_COMPLETED || (size_t)actual_length != command_length) {
133 msg_perr("Failed to issue the %s command: '%s'\n",
134 command_name,
135 libusb_error_name(rc));
136 return -1;
137 }
138
139 rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_IN,
140 answer, answer_length,
141 &actual_length, USB_TIMEOUT_IN_MS);
142 if (rc != LIBUSB_TRANSFER_COMPLETED || (size_t)actual_length != answer_length) {
143 msg_perr("Failed to get %s answer: '%s'\n",
144 command_name,
145 libusb_error_name(rc));
146 return -1;
147 }
148 return 0;
149}
150
151/**
152 * @param[out] bridge_input_clk Current input frequency in kHz of the given com.
153 */
154static int stlinkv3_get_clk(uint32_t *bridge_input_clk)
155{
156 uint8_t command[16];
157 uint8_t answer[12];
158
159 if (bridge_input_clk == NULL)
160 return -1;
161
162 memset(command, 0, sizeof(command));
163
164 command[0] = STLINK_BRIDGE_COMMAND;
165 command[1] = STLINK_BRIDGE_GET_CLOCK;
166 command[2] = STLINK_SPI_COM;
167
168 if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_GET_CLOCK") != 0)
169 return -1;
170
171 *bridge_input_clk = (uint32_t)answer[4]
172 | (uint32_t)answer[5]<<8
173 | (uint32_t)answer[6]<<16
174 | (uint32_t)answer[7]<<24;
175 return 0;
176
177}
178
179static int stlinkv3_spi_calc_prescaler(uint16_t reqested_freq_in_kHz,
180 enum spi_prescaler *prescaler,
181 uint16_t *calculated_freq_in_kHz)
182{
183 uint32_t bridge_clk_in_kHz;
184 uint32_t calculated_prescaler = 1;
185 uint16_t prescaler_value;
186
187 if (stlinkv3_get_clk(&bridge_clk_in_kHz))
188 return -1;
189
190 calculated_prescaler = bridge_clk_in_kHz/reqested_freq_in_kHz;
191 // Apply a smaller frequency if not exact
192 if (calculated_prescaler <= 2) {
193 *prescaler = SPI_BAUDRATEPRESCALER_2;
194 prescaler_value = 2;
195 } else if (calculated_prescaler <= 4) {
196 *prescaler = SPI_BAUDRATEPRESCALER_4;
197 prescaler_value = 4;
198 } else if (calculated_prescaler <= 8) {
199 *prescaler = SPI_BAUDRATEPRESCALER_8;
200 prescaler_value = 8;
201 } else if (calculated_prescaler <= 16) {
202 *prescaler = SPI_BAUDRATEPRESCALER_16;
203 prescaler_value = 16;
204 } else if (calculated_prescaler <= 32) {
205 *prescaler = SPI_BAUDRATEPRESCALER_32;
206 prescaler_value = 32;
207 } else if (calculated_prescaler <= 64) {
208 *prescaler = SPI_BAUDRATEPRESCALER_64;
209 prescaler_value = 64;
210 } else if (calculated_prescaler <= 128) {
211 *prescaler = SPI_BAUDRATEPRESCALER_128;
212 prescaler_value = 128;
213 } else if (calculated_prescaler <= 256) {
214 *prescaler = SPI_BAUDRATEPRESCALER_256;
215 prescaler_value = 256;
216 } else {
217 // smaller frequency not possible
218 *prescaler = SPI_BAUDRATEPRESCALER_256;
219 prescaler_value = 256;
220 }
221
222 *calculated_freq_in_kHz = bridge_clk_in_kHz / prescaler_value;
223
224 return 0;
225}
226
227static int stlinkv3_check_version(enum fw_version_check_result *result)
228{
229 uint8_t answer[12];
230 uint8_t command[16];
231
232 memset(command, 0, sizeof(command));
233
234 command[0] = ST_GETVERSION_EXT;
235 command[1] = 0x80;
236
237 if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "ST_GETVERSION_EXT") != 0)
238 return -1;
239
240 msg_pinfo("Connected to STLink V3 with bridge FW version: %d\n", answer[4]);
241 *result = answer[4] >= FIRST_COMPATIBLE_BRIDGE_FW_VERSION
242 ? FW_VERSION_OK
243 : FW_VERSION_OLD;
244 return 0;
245}
246
247static int stlinkv3_spi_open(uint16_t reqested_freq_in_kHz)
248{
249 uint8_t command[16];
250 uint8_t answer[2];
251 uint16_t SCK_freq_in_kHz;
252 enum spi_prescaler prescaler;
253 enum fw_version_check_result fw_check_result;
254
255 if (stlinkv3_check_version(&fw_check_result)) {
Miklós Márton5c639b32020-07-26 10:40:46 +0200256 msg_perr("Failed to query FW version\n");
Miklós Márton324929c2019-08-01 19:14:10 +0200257 return -1;
258 }
259
260 if (fw_check_result != FW_VERSION_OK) {
Miklós Márton5c639b32020-07-26 10:40:46 +0200261 msg_pinfo("Your STLink V3 has a too old version of the bridge interface\n"
262 "Please update the firmware to version 2.33.25 or newer of the STSW-LINK007\n"
263 "which can be downloaded from here:\n"
264 "https://www.st.com/en/development-tools/stsw-link007.html\n");
Miklós Márton324929c2019-08-01 19:14:10 +0200265 return -1;
266 }
267
268 if (stlinkv3_spi_calc_prescaler(reqested_freq_in_kHz,
269 &prescaler,
270 &SCK_freq_in_kHz)) {
Miklós Márton5c639b32020-07-26 10:40:46 +0200271 msg_perr("Failed to calculate SPI clock prescaler\n");
Miklós Márton324929c2019-08-01 19:14:10 +0200272 return -1;
273 }
274 msg_pinfo("SCK frequency set to %d kHz\n", SCK_freq_in_kHz);
275
276 memset(command, 0, sizeof(command));
277
278 command[0] = STLINK_BRIDGE_COMMAND;
279 command[1] = STLINK_BRIDGE_INIT_SPI;
280 command[2] = SPI_DIRECTION_2LINES_FULLDUPLEX;
281 command[3] = (SPI_MODE_MASTER
282 | (SPI_CPHA_1EDGE << 1)
283 | (SPI_CPOL_LOW << 2)
284 | (SPI_FIRSTBIT_MSB << 3));
285 command[4] = SPI_DATASIZE_8B;
286 command[5] = SPI_NSS_SOFT;
287 command[6] = (uint8_t)prescaler;
288
289 return stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_INIT_SPI");
290}
291
292static int stlinkv3_get_last_readwrite_status(uint32_t *status)
293{
294 uint8_t command[16];
295 uint16_t answer[4];
296
297 memset(command, 0, sizeof(command));
298
299 command[0] = STLINK_BRIDGE_COMMAND;
300 command[1] = STLINK_BRIDGE_GET_RWCMD_STATUS;
301
302 if (stlinkv3_command(command, sizeof(command),
303 (uint8_t *)answer, sizeof(answer),
304 "STLINK_BRIDGE_GET_RWCMD_STATUS") != 0)
305 return -1;
306
307 *status = (uint32_t)answer[2] | (uint32_t)answer[3]<<16;
308 return 0;
309}
310
311static int stlinkv3_spi_set_SPI_NSS(enum spi_nss_level nss_level)
312{
313 uint8_t command[16];
314 uint8_t answer[2];
315
316 memset(command, 0, sizeof(command));
317
318 command[0] = STLINK_BRIDGE_COMMAND;
319 command[1] = STLINK_BRIDGE_CS_SPI;
320 command[2] = (uint8_t) (nss_level);
321
322 if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_CS_SPI") != 0)
323 return -1;
324 return 0;
325}
326
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000327static int stlinkv3_spi_transmit(const struct flashctx *flash,
Miklós Márton324929c2019-08-01 19:14:10 +0200328 unsigned int write_cnt,
329 unsigned int read_cnt,
330 const unsigned char *write_arr,
331 unsigned char *read_arr)
332{
333 uint8_t command[16];
334 int rc = 0;
335 int actual_length = 0;
336 uint32_t rw_status = 0;
Nico Huber370a9f32019-12-31 18:22:02 +0100337 unsigned int i;
Miklós Márton324929c2019-08-01 19:14:10 +0200338
339 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_LOW)) {
340 msg_perr("Failed to set the NSS pin to low\n");
341 return -1;
342 }
343
344 memset(command, 0, sizeof(command));
345
346 command[0] = STLINK_BRIDGE_COMMAND;
347 command[1] = STLINK_BRIDGE_WRITE_SPI;
348 command[2] = (uint8_t)write_cnt;
349 command[3] = (uint8_t)(write_cnt >> 8);
350
Nico Huber370a9f32019-12-31 18:22:02 +0100351 for (i = 0; (i < 8) && (i < write_cnt); i++)
Miklós Márton324929c2019-08-01 19:14:10 +0200352 command[4+i] = write_arr[i];
353
354 rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT,
355 command, sizeof(command),
356 &actual_length, USB_TIMEOUT_IN_MS);
357 if (rc != LIBUSB_TRANSFER_COMPLETED || actual_length != sizeof(command)) {
358 msg_perr("Failed to issue the STLINK_BRIDGE_WRITE_SPI command: '%s'\n",
359 libusb_error_name(rc));
360 goto transmit_err;
361 }
362
363 if (write_cnt > 8) {
364 rc = libusb_bulk_transfer(stlinkv3_handle,
365 STLINK_EP_OUT,
366 (unsigned char *)&write_arr[8],
367 (unsigned int)(write_cnt - 8),
368 &actual_length,
369 USB_TIMEOUT_IN_MS);
370 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != (write_cnt - 8)) {
371 msg_perr("Failed to send the data after the STLINK_BRIDGE_WRITE_SPI command: '%s'\n",
372 libusb_error_name(rc));
373 goto transmit_err;
374 }
375 }
376
377 if (stlinkv3_get_last_readwrite_status(&rw_status))
378 return -1;
379
380 if (rw_status != 0) {
381 msg_perr("SPI read/write failure: %d\n", rw_status);
382 goto transmit_err;
383 }
384
385 if (read_cnt) {
386 command[1] = STLINK_BRIDGE_READ_SPI;
387 command[2] = (uint8_t)read_cnt;
388 command[3] = (uint8_t)(read_cnt >> 8);
389
390 rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT,
391 command, sizeof(command),
392 &actual_length, USB_TIMEOUT_IN_MS);
393 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != sizeof(command)) {
394 msg_perr("Failed to issue the STLINK_BRIDGE_READ_SPI command: '%s'\n",
395 libusb_error_name(rc));
396 goto transmit_err;
397 }
398
399 rc = libusb_bulk_transfer(stlinkv3_handle,
400 STLINK_EP_IN,
401 (unsigned char *)read_arr,
402 (int)read_cnt,
403 &actual_length,
404 USB_TIMEOUT_IN_MS);
405 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != read_cnt) {
406 msg_perr("Failed to retrive the STLINK_BRIDGE_READ_SPI answer: '%s'\n",
407 libusb_error_name(rc));
408 goto transmit_err;
409 }
410 }
411
412 if (stlinkv3_get_last_readwrite_status(&rw_status))
413 goto transmit_err;
414
415 if (rw_status != 0) {
416 msg_perr("SPI read/write failure: %d\n", rw_status);
417 goto transmit_err;
418 }
419
420 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH)) {
421 msg_perr("Failed to set the NSS pin to high\n");
422 return -1;
423 }
424 return 0;
425
426transmit_err:
427 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH))
428 msg_perr("Failed to set the NSS pin to high\n");
429 return -1;
430}
431
432static int stlinkv3_spi_shutdown(void *data)
433{
434 uint8_t command[16];
435 uint8_t answer[2];
436
437 memset(command, 0, sizeof(command));
438
439 command[0] = STLINK_BRIDGE_COMMAND;
440 command[1] = STLINK_BRIDGE_CLOSE;
441 command[2] = STLINK_SPI_COM;
442
443 stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_CLOSE");
444
445 libusb_close(stlinkv3_handle);
446 libusb_exit(usb_ctx);
447
448 return 0;
449}
450
451static const struct spi_master spi_programmer_stlinkv3 = {
452 .max_data_read = UINT16_MAX,
453 .max_data_write = UINT16_MAX,
454 .command = stlinkv3_spi_transmit,
455 .multicommand = default_spi_send_multicommand,
456 .read = default_spi_read,
457 .write_256 = default_spi_write_256,
458 .write_aai = default_spi_write_aai,
459};
460
461int stlinkv3_spi_init(void)
462{
463 uint16_t sck_freq_kHz = 1000; // selecting 1 MHz SCK is a good bet
464 char *speed_str = NULL;
465 char *serialno = NULL;
466 char *endptr = NULL;
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000467 int ret = 1;
Miklós Márton324929c2019-08-01 19:14:10 +0200468
469 libusb_init(&usb_ctx);
470 if (!usb_ctx) {
471 msg_perr("Could not initialize libusb!\n");
472 return 1;
473 }
474
475 serialno = extract_programmer_param("serial");
476 if (serialno)
477 msg_pdbg("Opening STLINK-V3 with serial: %s\n", serialno);
478 stlinkv3_handle = usb_dev_get_by_vid_pid_serial(usb_ctx,
479 devs_stlinkv3_spi[0].vendor_id,
480 devs_stlinkv3_spi[0].device_id,
481 serialno);
482
483 if (!stlinkv3_handle) {
484 if (serialno)
485 msg_perr("No STLINK-V3 seems to be connected with serial %s\n", serialno);
486 else
487 msg_perr("Could not find any connected STLINK-V3\n");
488 free(serialno);
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000489 goto init_err_exit;
Miklós Márton324929c2019-08-01 19:14:10 +0200490 }
491 free(serialno);
492
493 speed_str = extract_programmer_param("spispeed");
494 if (speed_str) {
495 sck_freq_kHz = strtoul(speed_str, &endptr, 0);
Patrick Georgi355a1df2020-04-23 09:36:12 +0200496 if (*endptr || sck_freq_kHz == 0) {
Miklós Márton324929c2019-08-01 19:14:10 +0200497 msg_perr("The spispeed parameter passed with invalid format: %s\n",
498 speed_str);
Patrick Georgi355a1df2020-04-23 09:36:12 +0200499 msg_perr("Please pass the parameter "
500 "with a simple non-zero number in kHz\n");
Patrick Georgi739899a2020-04-23 09:35:06 +0200501 free(speed_str);
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000502 ret = -1;
503 goto init_err_exit;
Miklós Márton324929c2019-08-01 19:14:10 +0200504 }
505 free(speed_str);
506 }
507
508 if (stlinkv3_spi_open(sck_freq_kHz))
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000509 goto init_err_exit;
Miklós Márton324929c2019-08-01 19:14:10 +0200510
511 if (register_shutdown(stlinkv3_spi_shutdown, NULL))
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000512 goto init_err_cleanup_exit;
Miklós Márton324929c2019-08-01 19:14:10 +0200513
514 if (register_spi_master(&spi_programmer_stlinkv3))
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000515 return 1; /* shutdown function does cleanup */
Miklós Márton324929c2019-08-01 19:14:10 +0200516
517 return 0;
518
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000519init_err_cleanup_exit:
520 stlinkv3_spi_shutdown(NULL);
Miklós Márton324929c2019-08-01 19:14:10 +0200521 return 1;
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000522
523init_err_exit:
524 if (stlinkv3_handle)
525 libusb_close(stlinkv3_handle);
526 libusb_exit(usb_ctx);
527 return ret;
Miklós Márton324929c2019-08-01 19:14:10 +0200528}