blob: d09b0d59b2de6fbc2a99689a7efcab295ed56073 [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{
Angel Pons7e134562021-06-07 13:29:13 +0200156 uint8_t command[16] = { 0 };
Miklós Márton324929c2019-08-01 19:14:10 +0200157 uint8_t answer[12];
158
159 if (bridge_input_clk == NULL)
160 return -1;
161
Miklós Márton324929c2019-08-01 19:14:10 +0200162 command[0] = STLINK_BRIDGE_COMMAND;
163 command[1] = STLINK_BRIDGE_GET_CLOCK;
164 command[2] = STLINK_SPI_COM;
165
166 if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_GET_CLOCK") != 0)
167 return -1;
168
169 *bridge_input_clk = (uint32_t)answer[4]
170 | (uint32_t)answer[5]<<8
171 | (uint32_t)answer[6]<<16
172 | (uint32_t)answer[7]<<24;
173 return 0;
174
175}
176
177static int stlinkv3_spi_calc_prescaler(uint16_t reqested_freq_in_kHz,
178 enum spi_prescaler *prescaler,
179 uint16_t *calculated_freq_in_kHz)
180{
181 uint32_t bridge_clk_in_kHz;
182 uint32_t calculated_prescaler = 1;
183 uint16_t prescaler_value;
184
185 if (stlinkv3_get_clk(&bridge_clk_in_kHz))
186 return -1;
187
188 calculated_prescaler = bridge_clk_in_kHz/reqested_freq_in_kHz;
189 // Apply a smaller frequency if not exact
190 if (calculated_prescaler <= 2) {
191 *prescaler = SPI_BAUDRATEPRESCALER_2;
192 prescaler_value = 2;
193 } else if (calculated_prescaler <= 4) {
194 *prescaler = SPI_BAUDRATEPRESCALER_4;
195 prescaler_value = 4;
196 } else if (calculated_prescaler <= 8) {
197 *prescaler = SPI_BAUDRATEPRESCALER_8;
198 prescaler_value = 8;
199 } else if (calculated_prescaler <= 16) {
200 *prescaler = SPI_BAUDRATEPRESCALER_16;
201 prescaler_value = 16;
202 } else if (calculated_prescaler <= 32) {
203 *prescaler = SPI_BAUDRATEPRESCALER_32;
204 prescaler_value = 32;
205 } else if (calculated_prescaler <= 64) {
206 *prescaler = SPI_BAUDRATEPRESCALER_64;
207 prescaler_value = 64;
208 } else if (calculated_prescaler <= 128) {
209 *prescaler = SPI_BAUDRATEPRESCALER_128;
210 prescaler_value = 128;
211 } else if (calculated_prescaler <= 256) {
212 *prescaler = SPI_BAUDRATEPRESCALER_256;
213 prescaler_value = 256;
214 } else {
215 // smaller frequency not possible
216 *prescaler = SPI_BAUDRATEPRESCALER_256;
217 prescaler_value = 256;
218 }
219
220 *calculated_freq_in_kHz = bridge_clk_in_kHz / prescaler_value;
221
222 return 0;
223}
224
225static int stlinkv3_check_version(enum fw_version_check_result *result)
226{
227 uint8_t answer[12];
Angel Pons7e134562021-06-07 13:29:13 +0200228 uint8_t command[16] = { 0 };
Miklós Márton324929c2019-08-01 19:14:10 +0200229
230 command[0] = ST_GETVERSION_EXT;
231 command[1] = 0x80;
232
233 if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "ST_GETVERSION_EXT") != 0)
234 return -1;
235
236 msg_pinfo("Connected to STLink V3 with bridge FW version: %d\n", answer[4]);
237 *result = answer[4] >= FIRST_COMPATIBLE_BRIDGE_FW_VERSION
238 ? FW_VERSION_OK
239 : FW_VERSION_OLD;
240 return 0;
241}
242
243static int stlinkv3_spi_open(uint16_t reqested_freq_in_kHz)
244{
Angel Pons7e134562021-06-07 13:29:13 +0200245 uint8_t command[16] = { 0 };
Miklós Márton324929c2019-08-01 19:14:10 +0200246 uint8_t answer[2];
247 uint16_t SCK_freq_in_kHz;
248 enum spi_prescaler prescaler;
249 enum fw_version_check_result fw_check_result;
250
251 if (stlinkv3_check_version(&fw_check_result)) {
Miklós Márton5c639b32020-07-26 10:40:46 +0200252 msg_perr("Failed to query FW version\n");
Miklós Márton324929c2019-08-01 19:14:10 +0200253 return -1;
254 }
255
256 if (fw_check_result != FW_VERSION_OK) {
Miklós Márton5c639b32020-07-26 10:40:46 +0200257 msg_pinfo("Your STLink V3 has a too old version of the bridge interface\n"
258 "Please update the firmware to version 2.33.25 or newer of the STSW-LINK007\n"
259 "which can be downloaded from here:\n"
260 "https://www.st.com/en/development-tools/stsw-link007.html\n");
Miklós Márton324929c2019-08-01 19:14:10 +0200261 return -1;
262 }
263
264 if (stlinkv3_spi_calc_prescaler(reqested_freq_in_kHz,
265 &prescaler,
266 &SCK_freq_in_kHz)) {
Miklós Márton5c639b32020-07-26 10:40:46 +0200267 msg_perr("Failed to calculate SPI clock prescaler\n");
Miklós Márton324929c2019-08-01 19:14:10 +0200268 return -1;
269 }
270 msg_pinfo("SCK frequency set to %d kHz\n", SCK_freq_in_kHz);
271
Miklós Márton324929c2019-08-01 19:14:10 +0200272 command[0] = STLINK_BRIDGE_COMMAND;
273 command[1] = STLINK_BRIDGE_INIT_SPI;
274 command[2] = SPI_DIRECTION_2LINES_FULLDUPLEX;
275 command[3] = (SPI_MODE_MASTER
276 | (SPI_CPHA_1EDGE << 1)
277 | (SPI_CPOL_LOW << 2)
278 | (SPI_FIRSTBIT_MSB << 3));
279 command[4] = SPI_DATASIZE_8B;
280 command[5] = SPI_NSS_SOFT;
281 command[6] = (uint8_t)prescaler;
282
283 return stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_INIT_SPI");
284}
285
286static int stlinkv3_get_last_readwrite_status(uint32_t *status)
287{
Angel Pons7e134562021-06-07 13:29:13 +0200288 uint8_t command[16] = { 0 };
Miklós Márton324929c2019-08-01 19:14:10 +0200289 uint16_t answer[4];
290
Miklós Márton324929c2019-08-01 19:14:10 +0200291 command[0] = STLINK_BRIDGE_COMMAND;
292 command[1] = STLINK_BRIDGE_GET_RWCMD_STATUS;
293
294 if (stlinkv3_command(command, sizeof(command),
295 (uint8_t *)answer, sizeof(answer),
296 "STLINK_BRIDGE_GET_RWCMD_STATUS") != 0)
297 return -1;
298
299 *status = (uint32_t)answer[2] | (uint32_t)answer[3]<<16;
300 return 0;
301}
302
303static int stlinkv3_spi_set_SPI_NSS(enum spi_nss_level nss_level)
304{
Angel Pons7e134562021-06-07 13:29:13 +0200305 uint8_t command[16] = { 0 };
Miklós Márton324929c2019-08-01 19:14:10 +0200306 uint8_t answer[2];
307
Miklós Márton324929c2019-08-01 19:14:10 +0200308 command[0] = STLINK_BRIDGE_COMMAND;
309 command[1] = STLINK_BRIDGE_CS_SPI;
310 command[2] = (uint8_t) (nss_level);
311
312 if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_CS_SPI") != 0)
313 return -1;
314 return 0;
315}
316
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000317static int stlinkv3_spi_transmit(const struct flashctx *flash,
Miklós Márton324929c2019-08-01 19:14:10 +0200318 unsigned int write_cnt,
319 unsigned int read_cnt,
320 const unsigned char *write_arr,
321 unsigned char *read_arr)
322{
Angel Pons7e134562021-06-07 13:29:13 +0200323 uint8_t command[16] = { 0 };
Miklós Márton324929c2019-08-01 19:14:10 +0200324 int rc = 0;
325 int actual_length = 0;
326 uint32_t rw_status = 0;
Nico Huber370a9f32019-12-31 18:22:02 +0100327 unsigned int i;
Miklós Márton324929c2019-08-01 19:14:10 +0200328
329 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_LOW)) {
330 msg_perr("Failed to set the NSS pin to low\n");
331 return -1;
332 }
333
Miklós Márton324929c2019-08-01 19:14:10 +0200334 command[0] = STLINK_BRIDGE_COMMAND;
335 command[1] = STLINK_BRIDGE_WRITE_SPI;
336 command[2] = (uint8_t)write_cnt;
337 command[3] = (uint8_t)(write_cnt >> 8);
338
Nico Huber370a9f32019-12-31 18:22:02 +0100339 for (i = 0; (i < 8) && (i < write_cnt); i++)
Miklós Márton324929c2019-08-01 19:14:10 +0200340 command[4+i] = write_arr[i];
341
342 rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT,
343 command, sizeof(command),
344 &actual_length, USB_TIMEOUT_IN_MS);
345 if (rc != LIBUSB_TRANSFER_COMPLETED || actual_length != sizeof(command)) {
346 msg_perr("Failed to issue the STLINK_BRIDGE_WRITE_SPI command: '%s'\n",
347 libusb_error_name(rc));
348 goto transmit_err;
349 }
350
351 if (write_cnt > 8) {
352 rc = libusb_bulk_transfer(stlinkv3_handle,
353 STLINK_EP_OUT,
354 (unsigned char *)&write_arr[8],
355 (unsigned int)(write_cnt - 8),
356 &actual_length,
357 USB_TIMEOUT_IN_MS);
358 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != (write_cnt - 8)) {
359 msg_perr("Failed to send the data after the STLINK_BRIDGE_WRITE_SPI command: '%s'\n",
360 libusb_error_name(rc));
361 goto transmit_err;
362 }
363 }
364
365 if (stlinkv3_get_last_readwrite_status(&rw_status))
366 return -1;
367
368 if (rw_status != 0) {
369 msg_perr("SPI read/write failure: %d\n", rw_status);
370 goto transmit_err;
371 }
372
373 if (read_cnt) {
374 command[1] = STLINK_BRIDGE_READ_SPI;
375 command[2] = (uint8_t)read_cnt;
376 command[3] = (uint8_t)(read_cnt >> 8);
377
378 rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT,
379 command, sizeof(command),
380 &actual_length, USB_TIMEOUT_IN_MS);
381 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != sizeof(command)) {
382 msg_perr("Failed to issue the STLINK_BRIDGE_READ_SPI command: '%s'\n",
383 libusb_error_name(rc));
384 goto transmit_err;
385 }
386
387 rc = libusb_bulk_transfer(stlinkv3_handle,
388 STLINK_EP_IN,
389 (unsigned char *)read_arr,
390 (int)read_cnt,
391 &actual_length,
392 USB_TIMEOUT_IN_MS);
393 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != read_cnt) {
394 msg_perr("Failed to retrive the STLINK_BRIDGE_READ_SPI answer: '%s'\n",
395 libusb_error_name(rc));
396 goto transmit_err;
397 }
398 }
399
400 if (stlinkv3_get_last_readwrite_status(&rw_status))
401 goto transmit_err;
402
403 if (rw_status != 0) {
404 msg_perr("SPI read/write failure: %d\n", rw_status);
405 goto transmit_err;
406 }
407
408 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH)) {
409 msg_perr("Failed to set the NSS pin to high\n");
410 return -1;
411 }
412 return 0;
413
414transmit_err:
415 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH))
416 msg_perr("Failed to set the NSS pin to high\n");
417 return -1;
418}
419
420static int stlinkv3_spi_shutdown(void *data)
421{
Angel Pons7e134562021-06-07 13:29:13 +0200422 uint8_t command[16] = { 0 };
Miklós Márton324929c2019-08-01 19:14:10 +0200423 uint8_t answer[2];
424
Miklós Márton324929c2019-08-01 19:14:10 +0200425 command[0] = STLINK_BRIDGE_COMMAND;
426 command[1] = STLINK_BRIDGE_CLOSE;
427 command[2] = STLINK_SPI_COM;
428
429 stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_CLOSE");
430
431 libusb_close(stlinkv3_handle);
432 libusb_exit(usb_ctx);
433
434 return 0;
435}
436
437static const struct spi_master spi_programmer_stlinkv3 = {
438 .max_data_read = UINT16_MAX,
439 .max_data_write = UINT16_MAX,
440 .command = stlinkv3_spi_transmit,
441 .multicommand = default_spi_send_multicommand,
442 .read = default_spi_read,
443 .write_256 = default_spi_write_256,
444 .write_aai = default_spi_write_aai,
445};
446
447int stlinkv3_spi_init(void)
448{
449 uint16_t sck_freq_kHz = 1000; // selecting 1 MHz SCK is a good bet
450 char *speed_str = NULL;
451 char *serialno = NULL;
452 char *endptr = NULL;
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000453 int ret = 1;
Miklós Márton324929c2019-08-01 19:14:10 +0200454
455 libusb_init(&usb_ctx);
456 if (!usb_ctx) {
457 msg_perr("Could not initialize libusb!\n");
458 return 1;
459 }
460
461 serialno = extract_programmer_param("serial");
462 if (serialno)
463 msg_pdbg("Opening STLINK-V3 with serial: %s\n", serialno);
464 stlinkv3_handle = usb_dev_get_by_vid_pid_serial(usb_ctx,
465 devs_stlinkv3_spi[0].vendor_id,
466 devs_stlinkv3_spi[0].device_id,
467 serialno);
468
469 if (!stlinkv3_handle) {
470 if (serialno)
471 msg_perr("No STLINK-V3 seems to be connected with serial %s\n", serialno);
472 else
473 msg_perr("Could not find any connected STLINK-V3\n");
474 free(serialno);
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000475 goto init_err_exit;
Miklós Márton324929c2019-08-01 19:14:10 +0200476 }
477 free(serialno);
478
479 speed_str = extract_programmer_param("spispeed");
480 if (speed_str) {
481 sck_freq_kHz = strtoul(speed_str, &endptr, 0);
Patrick Georgi355a1df2020-04-23 09:36:12 +0200482 if (*endptr || sck_freq_kHz == 0) {
Miklós Márton324929c2019-08-01 19:14:10 +0200483 msg_perr("The spispeed parameter passed with invalid format: %s\n",
484 speed_str);
Patrick Georgi355a1df2020-04-23 09:36:12 +0200485 msg_perr("Please pass the parameter "
486 "with a simple non-zero number in kHz\n");
Patrick Georgi739899a2020-04-23 09:35:06 +0200487 free(speed_str);
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000488 ret = -1;
489 goto init_err_exit;
Miklós Márton324929c2019-08-01 19:14:10 +0200490 }
491 free(speed_str);
492 }
493
494 if (stlinkv3_spi_open(sck_freq_kHz))
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000495 goto init_err_exit;
Miklós Márton324929c2019-08-01 19:14:10 +0200496
497 if (register_shutdown(stlinkv3_spi_shutdown, NULL))
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000498 goto init_err_cleanup_exit;
Miklós Márton324929c2019-08-01 19:14:10 +0200499
500 if (register_spi_master(&spi_programmer_stlinkv3))
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000501 return 1; /* shutdown function does cleanup */
Miklós Márton324929c2019-08-01 19:14:10 +0200502
503 return 0;
504
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000505init_err_cleanup_exit:
506 stlinkv3_spi_shutdown(NULL);
Miklós Márton324929c2019-08-01 19:14:10 +0200507 return 1;
Anastasia Klimchuk67aaed72021-05-14 14:01:34 +1000508
509init_err_exit:
510 if (stlinkv3_handle)
511 libusb_close(stlinkv3_handle);
512 libusb_exit(usb_ctx);
513 return ret;
Miklós Márton324929c2019-08-01 19:14:10 +0200514}