Skip to content

Commit 2a72bb6

Browse files
committed
NRF5x: QSPI SFDP read implementation
1 parent e9f08cb commit 2a72bb6

File tree

5 files changed

+142
-11
lines changed

5 files changed

+142
-11
lines changed

components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@
3434
"DISCO_F769NI": {
3535
"QSPI_FREQ": "8000000"
3636
},
37-
"NRF52840_DK": {
38-
"QSPI_FREQ": "32000000"
37+
"MCU_NRF52840": {
38+
"QSPI_FREQ": "32000000",
39+
"QSPI_MIN_READ_SIZE": "4",
40+
"QSPI_MIN_PROG_SIZE": "4"
3941
}
4042
}
4143
}

targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/drivers_nrf/hal/nrf_qspi.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ typedef struct
230230
bool io3_level; /**< I/O line level during transmission. */
231231
bool wipwait; /**< Wait if a Wait in Progress bit is set in the memory status byte. */
232232
bool wren; /**< Send write enable before instruction. */
233+
bool lfen; /**< Enable long frame mode. */
234+
bool lfstop; /**< Stop long frame mode. */
233235
} nrf_qspi_cinstr_conf_t;
234236

235237
/**
@@ -751,7 +753,9 @@ __STATIC_INLINE void nrf_qspi_cinstr_transfer_start(NRF_QSPI_Type *
751753
((uint32_t)p_config->io2_level << QSPI_CINSTRCONF_LIO2_Pos) |
752754
((uint32_t)p_config->io3_level << QSPI_CINSTRCONF_LIO3_Pos) |
753755
((uint32_t)p_config->wipwait << QSPI_CINSTRCONF_WIPWAIT_Pos) |
754-
((uint32_t)p_config->wren << QSPI_CINSTRCONF_WREN_Pos));
756+
((uint32_t)p_config->wren << QSPI_CINSTRCONF_WREN_Pos) |
757+
((uint32_t)p_config->lfen << QSPI_CINSTRCONF_LFEN_Pos) |
758+
((uint32_t)p_config->lfstop << QSPI_CINSTRCONF_LFSTOP_Pos) );
755759
}
756760

757761
#endif // SUPPRESS_INLINE_IMPLEMENTATION

targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/drivers_nrf/qspi/nrf_drv_qspi.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ typedef struct
135135
.io2_level = false, \
136136
.io3_level = false, \
137137
.wipwait = false, \
138-
.wren = false \
138+
.wren = false, \
139+
.lfen = false, \
140+
.lfstop = false \
139141
}
140142

141143
/**

targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c

Lines changed: 129 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ TODO
5656
#define MBED_HAL_QSPI_MAX_FREQ 32000000UL
5757

5858
// NRF supported R/W opcodes
59-
#define FAST_READ_opcode 0x0B
59+
#define FASTREAD_opcode 0x0B
6060
#define READ2O_opcode 0x3B
6161
#define READ2IO_opcode 0xBB
6262
#define READ4O_opcode 0x6B
6363
#define READ4IO_opcode 0xEB
64+
#define READSFDP_opcode 0x5A
6465

6566
#define PP_opcode 0x02
6667
#define PP2O_opcode 0xA2
@@ -70,12 +71,21 @@ TODO
7071
#define SCK_DELAY 0x05
7172
#define WORD_MASK 0x03
7273

74+
// NRF SFDP defines
75+
#define DWORD_LEN 4
76+
#define SFDP_CMD_LEN DWORD_LEN
77+
#define SFDP_DATA_LEN 128 // SFPD data buffer length in bytes, may need to be increased for other flash parts
78+
#define SFDP_READ_LEN 8 // 8 SFDP bytes can be read at a time
79+
#define SFDP_READ_MAX (SFDP_DATA_LEN / SFDP_READ_LEN)
80+
7381
static nrf_drv_qspi_config_t config;
7482

7583
// Private helper function to track initialization
7684
static ret_code_t _qspi_drv_init(void);
7785
// Private helper function to set NRF frequency divider
7886
nrf_qspi_frequency_t nrf_frequency(int hz);
87+
// Private helper function to read SFDP data
88+
qspi_status_t sfdp_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length);
7989

8090
qspi_status_t qspi_prepare_command(qspi_t *obj, const qspi_command_t *command, bool write)
8191
{
@@ -93,7 +103,7 @@ qspi_status_t qspi_prepare_command(qspi_t *obj, const qspi_command_t *command, b
93103
return QSPI_STATUS_INVALID_PARAMETER;
94104
}
95105
} else {
96-
if (command->instruction.value == FAST_READ_opcode) {
106+
if (command->instruction.value == FASTREAD_opcode) {
97107
config.prot_if.readoc = NRF_QSPI_READOC_FASTREAD;
98108
} else {
99109
return QSPI_STATUS_INVALID_PARAMETER;
@@ -277,9 +287,15 @@ qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data,
277287
return QSPI_STATUS_INVALID_PARAMETER;
278288
}
279289

280-
qspi_status_t status = qspi_prepare_command(obj, command, false);
281-
if (status != QSPI_STATUS_OK) {
290+
// check to see if this is an SFDP read
291+
if (command->instruction.value == READSFDP_opcode) {
292+
qspi_status_t status = sfdp_read(obj, command, data, length );
282293
return status;
294+
} else {
295+
qspi_status_t status = qspi_prepare_command(obj, command, false);
296+
if (status != QSPI_STATUS_OK) {
297+
return status;
298+
}
283299
}
284300

285301
ret_code_t ret = nrf_drv_qspi_read(data, *length, command->address.value);
@@ -293,16 +309,16 @@ qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data,
293309
qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_command_t *command, const void *tx_data, size_t tx_size, void *rx_data, size_t rx_size)
294310
{
295311
ret_code_t ret_code;
296-
uint8_t data[8];
312+
uint8_t data[8] = { 0 };
297313
uint32_t data_size = tx_size + rx_size;
298314

299-
nrf_qspi_cinstr_conf_t qspi_cinstr_config;
315+
nrf_qspi_cinstr_conf_t qspi_cinstr_config = { 0 };
300316
qspi_cinstr_config.opcode = command->instruction.value;
301317
qspi_cinstr_config.io2_level = true;
302318
qspi_cinstr_config.io3_level = true;
303319
qspi_cinstr_config.wipwait = false;
304320
qspi_cinstr_config.wren = false;
305-
321+
306322
if(!command->address.disabled && data_size == 0) {
307323
// erase command with address
308324
if (command->address.size == QSPI_CFG_ADDR_SIZE_24) {
@@ -400,6 +416,112 @@ nrf_qspi_frequency_t nrf_frequency(int hz)
400416
return freq;
401417
}
402418

419+
// Private helper to read nRF5x SFDP data using QSPI
420+
qspi_status_t sfdp_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length)
421+
{
422+
ret_code_t ret_code;
423+
static bool b_init = false;
424+
static uint8_t sfdp_rx[SFDP_DATA_LEN] = { 0 };
425+
426+
if (b_init == false) {
427+
// get the SFDP data usig nRF5x QSPI custom instuctions and long frame mode (lfen)
428+
nrf_qspi_cinstr_conf_t qspi_cinstr_config = { 0 };
429+
qspi_cinstr_config.opcode = command->instruction.value;
430+
qspi_cinstr_config.io2_level = true;
431+
qspi_cinstr_config.io3_level = true;
432+
qspi_cinstr_config.wipwait = false;
433+
qspi_cinstr_config.wren = false;
434+
qspi_cinstr_config.lfen = true;
435+
qspi_cinstr_config.lfstop = false;
436+
437+
// read the SFDP data, cmd + 8 bytes at a time
438+
uint8_t sfdp_data[SFDP_READ_LEN];
439+
qspi_cinstr_config.length = NRF_QSPI_CINSTR_LEN_9B;
440+
441+
for (uint32_t i = 0; i < SFDP_READ_MAX; ++i) {
442+
443+
static uint32_t rx_i = 0;
444+
memset(sfdp_data, 0, SFDP_READ_LEN);
445+
446+
if (i == (SFDP_READ_MAX - 1) ){
447+
qspi_cinstr_config.lfstop = true;
448+
}
449+
450+
ret_code = nrf_drv_qspi_cinstr_xfer(&qspi_cinstr_config, sfdp_data, sfdp_data);
451+
if (ret_code != NRF_SUCCESS) {
452+
return QSPI_STATUS_ERROR;
453+
}
454+
455+
// copy the second DWORD from the command data, the first DWORD is 0's
456+
for (uint32_t c = DWORD_LEN; c < SFDP_READ_LEN; ++c) {
457+
((uint8_t *)sfdp_rx)[rx_i] = sfdp_data[c];
458+
++rx_i;
459+
}
460+
rx_i += DWORD_LEN;
461+
}
462+
463+
// re-send just the SFDP CMD to offset the next read by DWORD
464+
uint8_t sfdp_cmd[SFDP_CMD_LEN] = { 0 };
465+
qspi_cinstr_config.lfstop = false;
466+
qspi_cinstr_config.length = NRF_QSPI_CINSTR_LEN_5B;
467+
468+
ret_code = nrf_drv_qspi_cinstr_xfer(&qspi_cinstr_config, sfdp_cmd, sfdp_cmd);
469+
if (ret_code != NRF_SUCCESS) {
470+
return QSPI_STATUS_ERROR;
471+
}
472+
473+
// read the offset SFDP data, cmd + 8 bytes at a time
474+
qspi_cinstr_config.length = NRF_QSPI_CINSTR_LEN_9B;
475+
for (uint32_t i = 0; i < SFDP_READ_MAX; ++i) {
476+
477+
static uint32_t rx_i = DWORD_LEN; // offset sfdp_rx data start
478+
memset(sfdp_data, 0, SFDP_READ_LEN);
479+
480+
if (i == (SFDP_READ_MAX - 1) ){
481+
qspi_cinstr_config.lfstop = true;
482+
}
483+
484+
ret_code = nrf_drv_qspi_cinstr_xfer(&qspi_cinstr_config, sfdp_data, sfdp_data);
485+
if (ret_code != NRF_SUCCESS) {
486+
return QSPI_STATUS_ERROR;
487+
}
488+
489+
// copy the second DWORD from the command data, the first DWORD is 0's
490+
for (uint32_t c = DWORD_LEN; c < SFDP_READ_LEN; ++c) {
491+
((uint8_t *)sfdp_rx)[rx_i] = sfdp_data[c];
492+
++rx_i;
493+
}
494+
rx_i += DWORD_LEN;
495+
}
496+
497+
b_init = true;
498+
}
499+
500+
if ( b_init == true) {
501+
// check for valid SFDP data, last basic header byte is always 0xFF
502+
// NOTE: "nRF52840-Preview-DK" boards do not support SFDP read
503+
if (sfdp_rx[7] != 0xFF) {
504+
return QSPI_STATUS_ERROR;
505+
}
506+
507+
// calculate the SFDP data length based on the parameter table offset
508+
// provided at index 12, plus the table length in DWORDS at index 11
509+
uint32_t sfdp_length = sfdp_rx[12] + (sfdp_rx[11] * DWORD_LEN);
510+
511+
// check if the data request is within the SFDP data array
512+
// increase SFDP_DATA_LEN to match sfdp_length, if necessary
513+
if ( sfdp_length <= SFDP_DATA_LEN &&
514+
sfdp_length >= (command->address.value + *length) ) {
515+
memcpy(data, (sfdp_rx + command->address.value), *length);
516+
return QSPI_STATUS_OK;
517+
} else {
518+
return QSPI_STATUS_INVALID_PARAMETER;
519+
}
520+
} else {
521+
return QSPI_STATUS_ERROR;
522+
}
523+
}
524+
403525

404526
#endif
405527

targets/targets.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6798,6 +6798,7 @@
67986798
},
67996799
"MCU_NRF52840": {
68006800
"inherits": ["Target"],
6801+
"components_add": ["QSPIF"],
68016802
"core": "Cortex-M4F",
68026803
"static_memory_defines": false,
68036804
"macros": [

0 commit comments

Comments
 (0)