blob: d56f3059135406b82213511a70b4842629cf6350 [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)) {
256 msg_perr("Failed to query FW version");
257 return -1;
258 }
259
260 if (fw_check_result != FW_VERSION_OK) {
261 msg_pinfo("Your STLink V3 has too old version of the bridge interface\n"
262 "Please update the firmware with the STSW-LINK007 which can be downloaded from here:\n"
263 "https://www.st.com/en/development-tools/stsw-link007.html");
264 return -1;
265 }
266
267 if (stlinkv3_spi_calc_prescaler(reqested_freq_in_kHz,
268 &prescaler,
269 &SCK_freq_in_kHz)) {
270 msg_perr("Failed to calculate SPI clock prescaler");
271 return -1;
272 }
273 msg_pinfo("SCK frequency set to %d kHz\n", SCK_freq_in_kHz);
274
275 memset(command, 0, sizeof(command));
276
277 command[0] = STLINK_BRIDGE_COMMAND;
278 command[1] = STLINK_BRIDGE_INIT_SPI;
279 command[2] = SPI_DIRECTION_2LINES_FULLDUPLEX;
280 command[3] = (SPI_MODE_MASTER
281 | (SPI_CPHA_1EDGE << 1)
282 | (SPI_CPOL_LOW << 2)
283 | (SPI_FIRSTBIT_MSB << 3));
284 command[4] = SPI_DATASIZE_8B;
285 command[5] = SPI_NSS_SOFT;
286 command[6] = (uint8_t)prescaler;
287
288 return stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_INIT_SPI");
289}
290
291static int stlinkv3_get_last_readwrite_status(uint32_t *status)
292{
293 uint8_t command[16];
294 uint16_t answer[4];
295
296 memset(command, 0, sizeof(command));
297
298 command[0] = STLINK_BRIDGE_COMMAND;
299 command[1] = STLINK_BRIDGE_GET_RWCMD_STATUS;
300
301 if (stlinkv3_command(command, sizeof(command),
302 (uint8_t *)answer, sizeof(answer),
303 "STLINK_BRIDGE_GET_RWCMD_STATUS") != 0)
304 return -1;
305
306 *status = (uint32_t)answer[2] | (uint32_t)answer[3]<<16;
307 return 0;
308}
309
310static int stlinkv3_spi_set_SPI_NSS(enum spi_nss_level nss_level)
311{
312 uint8_t command[16];
313 uint8_t answer[2];
314
315 memset(command, 0, sizeof(command));
316
317 command[0] = STLINK_BRIDGE_COMMAND;
318 command[1] = STLINK_BRIDGE_CS_SPI;
319 command[2] = (uint8_t) (nss_level);
320
321 if (stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_CS_SPI") != 0)
322 return -1;
323 return 0;
324}
325
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000326static int stlinkv3_spi_transmit(const struct flashctx *flash,
Miklós Márton324929c2019-08-01 19:14:10 +0200327 unsigned int write_cnt,
328 unsigned int read_cnt,
329 const unsigned char *write_arr,
330 unsigned char *read_arr)
331{
332 uint8_t command[16];
333 int rc = 0;
334 int actual_length = 0;
335 uint32_t rw_status = 0;
Nico Huber370a9f32019-12-31 18:22:02 +0100336 unsigned int i;
Miklós Márton324929c2019-08-01 19:14:10 +0200337
338 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_LOW)) {
339 msg_perr("Failed to set the NSS pin to low\n");
340 return -1;
341 }
342
343 memset(command, 0, sizeof(command));
344
345 command[0] = STLINK_BRIDGE_COMMAND;
346 command[1] = STLINK_BRIDGE_WRITE_SPI;
347 command[2] = (uint8_t)write_cnt;
348 command[3] = (uint8_t)(write_cnt >> 8);
349
Nico Huber370a9f32019-12-31 18:22:02 +0100350 for (i = 0; (i < 8) && (i < write_cnt); i++)
Miklós Márton324929c2019-08-01 19:14:10 +0200351 command[4+i] = write_arr[i];
352
353 rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT,
354 command, sizeof(command),
355 &actual_length, USB_TIMEOUT_IN_MS);
356 if (rc != LIBUSB_TRANSFER_COMPLETED || actual_length != sizeof(command)) {
357 msg_perr("Failed to issue the STLINK_BRIDGE_WRITE_SPI command: '%s'\n",
358 libusb_error_name(rc));
359 goto transmit_err;
360 }
361
362 if (write_cnt > 8) {
363 rc = libusb_bulk_transfer(stlinkv3_handle,
364 STLINK_EP_OUT,
365 (unsigned char *)&write_arr[8],
366 (unsigned int)(write_cnt - 8),
367 &actual_length,
368 USB_TIMEOUT_IN_MS);
369 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != (write_cnt - 8)) {
370 msg_perr("Failed to send the data after the STLINK_BRIDGE_WRITE_SPI command: '%s'\n",
371 libusb_error_name(rc));
372 goto transmit_err;
373 }
374 }
375
376 if (stlinkv3_get_last_readwrite_status(&rw_status))
377 return -1;
378
379 if (rw_status != 0) {
380 msg_perr("SPI read/write failure: %d\n", rw_status);
381 goto transmit_err;
382 }
383
384 if (read_cnt) {
385 command[1] = STLINK_BRIDGE_READ_SPI;
386 command[2] = (uint8_t)read_cnt;
387 command[3] = (uint8_t)(read_cnt >> 8);
388
389 rc = libusb_bulk_transfer(stlinkv3_handle, STLINK_EP_OUT,
390 command, sizeof(command),
391 &actual_length, USB_TIMEOUT_IN_MS);
392 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != sizeof(command)) {
393 msg_perr("Failed to issue the STLINK_BRIDGE_READ_SPI command: '%s'\n",
394 libusb_error_name(rc));
395 goto transmit_err;
396 }
397
398 rc = libusb_bulk_transfer(stlinkv3_handle,
399 STLINK_EP_IN,
400 (unsigned char *)read_arr,
401 (int)read_cnt,
402 &actual_length,
403 USB_TIMEOUT_IN_MS);
404 if (rc != LIBUSB_TRANSFER_COMPLETED || (unsigned int)actual_length != read_cnt) {
405 msg_perr("Failed to retrive the STLINK_BRIDGE_READ_SPI answer: '%s'\n",
406 libusb_error_name(rc));
407 goto transmit_err;
408 }
409 }
410
411 if (stlinkv3_get_last_readwrite_status(&rw_status))
412 goto transmit_err;
413
414 if (rw_status != 0) {
415 msg_perr("SPI read/write failure: %d\n", rw_status);
416 goto transmit_err;
417 }
418
419 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH)) {
420 msg_perr("Failed to set the NSS pin to high\n");
421 return -1;
422 }
423 return 0;
424
425transmit_err:
426 if (stlinkv3_spi_set_SPI_NSS(SPI_NSS_HIGH))
427 msg_perr("Failed to set the NSS pin to high\n");
428 return -1;
429}
430
431static int stlinkv3_spi_shutdown(void *data)
432{
433 uint8_t command[16];
434 uint8_t answer[2];
435
436 memset(command, 0, sizeof(command));
437
438 command[0] = STLINK_BRIDGE_COMMAND;
439 command[1] = STLINK_BRIDGE_CLOSE;
440 command[2] = STLINK_SPI_COM;
441
442 stlinkv3_command(command, sizeof(command), answer, sizeof(answer), "STLINK_BRIDGE_CLOSE");
443
444 libusb_close(stlinkv3_handle);
445 libusb_exit(usb_ctx);
446
447 return 0;
448}
449
450static const struct spi_master spi_programmer_stlinkv3 = {
451 .max_data_read = UINT16_MAX,
452 .max_data_write = UINT16_MAX,
453 .command = stlinkv3_spi_transmit,
454 .multicommand = default_spi_send_multicommand,
455 .read = default_spi_read,
456 .write_256 = default_spi_write_256,
457 .write_aai = default_spi_write_aai,
458};
459
460int stlinkv3_spi_init(void)
461{
462 uint16_t sck_freq_kHz = 1000; // selecting 1 MHz SCK is a good bet
463 char *speed_str = NULL;
464 char *serialno = NULL;
465 char *endptr = NULL;
466
467 libusb_init(&usb_ctx);
468 if (!usb_ctx) {
469 msg_perr("Could not initialize libusb!\n");
470 return 1;
471 }
472
473 serialno = extract_programmer_param("serial");
474 if (serialno)
475 msg_pdbg("Opening STLINK-V3 with serial: %s\n", serialno);
476 stlinkv3_handle = usb_dev_get_by_vid_pid_serial(usb_ctx,
477 devs_stlinkv3_spi[0].vendor_id,
478 devs_stlinkv3_spi[0].device_id,
479 serialno);
480
481 if (!stlinkv3_handle) {
482 if (serialno)
483 msg_perr("No STLINK-V3 seems to be connected with serial %s\n", serialno);
484 else
485 msg_perr("Could not find any connected STLINK-V3\n");
486 free(serialno);
487 goto err_exit;
488 }
489 free(serialno);
490
491 speed_str = extract_programmer_param("spispeed");
492 if (speed_str) {
493 sck_freq_kHz = strtoul(speed_str, &endptr, 0);
Patrick Georgi355a1df2020-04-23 09:36:12 +0200494 if (*endptr || sck_freq_kHz == 0) {
Miklós Márton324929c2019-08-01 19:14:10 +0200495 msg_perr("The spispeed parameter passed with invalid format: %s\n",
496 speed_str);
Patrick Georgi355a1df2020-04-23 09:36:12 +0200497 msg_perr("Please pass the parameter "
498 "with a simple non-zero number in kHz\n");
Miklós Márton324929c2019-08-01 19:14:10 +0200499 return -1;
500 }
501 free(speed_str);
502 }
503
504 if (stlinkv3_spi_open(sck_freq_kHz))
505 goto err_exit;
506
507 if (register_shutdown(stlinkv3_spi_shutdown, NULL))
508 goto err_exit;
509
510 if (register_spi_master(&spi_programmer_stlinkv3))
511 goto err_exit;
512
513 return 0;
514
515err_exit:
516 libusb_exit(usb_ctx);
517 return 1;
518}