56
56
#define MBED_HAL_QSPI_MAX_FREQ 32000000UL
57
57
58
58
// NRF supported R/W opcodes
59
- #define FAST_READ_opcode 0x0B
59
+ #define FASTREAD_opcode 0x0B
60
60
#define READ2O_opcode 0x3B
61
61
#define READ2IO_opcode 0xBB
62
62
#define READ4O_opcode 0x6B
63
63
#define READ4IO_opcode 0xEB
64
+ #define READSFDP_opcode 0x5A
64
65
65
66
#define PP_opcode 0x02
66
67
#define PP2O_opcode 0xA2
70
71
#define SCK_DELAY 0x05
71
72
#define WORD_MASK 0x03
72
73
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
+
73
81
static nrf_drv_qspi_config_t config ;
74
82
75
83
// Private helper function to track initialization
76
84
static ret_code_t _qspi_drv_init (void );
77
85
// Private helper function to set NRF frequency divider
78
86
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 );
79
89
80
90
qspi_status_t qspi_prepare_command (qspi_t * obj , const qspi_command_t * command , bool write )
81
91
{
@@ -93,7 +103,7 @@ qspi_status_t qspi_prepare_command(qspi_t *obj, const qspi_command_t *command, b
93
103
return QSPI_STATUS_INVALID_PARAMETER ;
94
104
}
95
105
} else {
96
- if (command -> instruction .value == FAST_READ_opcode ) {
106
+ if (command -> instruction .value == FASTREAD_opcode ) {
97
107
config .prot_if .readoc = NRF_QSPI_READOC_FASTREAD ;
98
108
} else {
99
109
return QSPI_STATUS_INVALID_PARAMETER ;
@@ -277,9 +287,15 @@ qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data,
277
287
return QSPI_STATUS_INVALID_PARAMETER ;
278
288
}
279
289
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 );
282
293
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
+ }
283
299
}
284
300
285
301
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,
293
309
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 )
294
310
{
295
311
ret_code_t ret_code ;
296
- uint8_t data [8 ];
312
+ uint8_t data [8 ] = { 0 } ;
297
313
uint32_t data_size = tx_size + rx_size ;
298
314
299
- nrf_qspi_cinstr_conf_t qspi_cinstr_config ;
315
+ nrf_qspi_cinstr_conf_t qspi_cinstr_config = { 0 } ;
300
316
qspi_cinstr_config .opcode = command -> instruction .value ;
301
317
qspi_cinstr_config .io2_level = true;
302
318
qspi_cinstr_config .io3_level = true;
303
319
qspi_cinstr_config .wipwait = false;
304
320
qspi_cinstr_config .wren = false;
305
-
321
+
306
322
if (!command -> address .disabled && data_size == 0 ) {
307
323
// erase command with address
308
324
if (command -> address .size == QSPI_CFG_ADDR_SIZE_24 ) {
@@ -400,6 +416,112 @@ nrf_qspi_frequency_t nrf_frequency(int hz)
400
416
return freq ;
401
417
}
402
418
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
+
403
525
404
526
#endif
405
527
0 commit comments