Skip to content

Commit 6bb2381

Browse files
committed
SFDP: Add support for multiple configurations and sector maps
A Sector Map Parameter Table contains a sequence of the following descriptors: * (Optional) configuration detection command descriptors, one for each command to run to determine the current configuration. This exists only if the flash layout is configurable. * Sector map descriptors, one for each possible configuration. On a flash device with a non-configurable layout, there is only one such descriptor. Previously we only supported the non-configurable case with a single descriptor. This commit adds support for multiple configurations.
1 parent f88bf82 commit 6bb2381

File tree

2 files changed

+464
-12
lines changed

2 files changed

+464
-12
lines changed

storage/blockdevice/source/SFDP.cpp

Lines changed: 127 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,110 @@ int sfdp_parse_headers(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, ui
245245
return 0;
246246
}
247247

248+
static constexpr size_t min_descriptor_size = 8; // two DWORDs
249+
250+
static inline bool is_last_descriptor(const uint8_t *descriptor)
251+
{
252+
// Last descriptor of the current type (detection command/sector map)
253+
MBED_ASSERT(nullptr != descriptor);
254+
return descriptor[0] & 0x01;
255+
}
256+
257+
static inline bool is_sector_map_descriptor(const uint8_t *descriptor)
258+
{
259+
// true - sector map descriptor
260+
// false - configuration detection command descriptor
261+
MBED_ASSERT(nullptr != descriptor);
262+
return descriptor[0] & 0x02;
263+
}
264+
265+
static int sfdp_detect_sector_map_configuration(
266+
Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader,
267+
sfdp_hdr_info &sfdp_info,
268+
uint8_t *&descriptor,
269+
const uint8_t *table_end,
270+
uint8_t &config)
271+
{
272+
config = 0;
273+
274+
// If the table starts with a sector map descriptor instead of a configuration
275+
// detection command descriptor, this device has only one configuration (i.e. is
276+
// not configurable) with ID equal to 0.
277+
if (is_sector_map_descriptor(descriptor)) {
278+
return 0;
279+
}
280+
281+
// Loop through all configuration detection descriptors and run detection commands
282+
while (!is_sector_map_descriptor(descriptor) && (descriptor + min_descriptor_size <= table_end)) {
283+
uint8_t instruction = descriptor[1];
284+
uint8_t dummy_cycles = descriptor[2] & 0x0F;
285+
auto addr_size = static_cast<sfdp_cmd_addr_size_t>(descriptor[2] >> 6);
286+
uint8_t mask = descriptor[3];
287+
uint32_t cmd_addr;
288+
memcpy(&cmd_addr, &descriptor[4], sizeof(cmd_addr)); // last 32 bits of the descriptor
289+
290+
uint8_t rx;
291+
int status = sfdp_reader(cmd_addr, addr_size, instruction, dummy_cycles, &rx, sizeof(rx));
292+
if (status < 0) {
293+
tr_error("Sector Map: Configuration detection command failed");
294+
return -1;
295+
}
296+
297+
// Shift existing bits to the left, so we can add the newly detected bit
298+
config <<= 1;
299+
300+
// The mask may apply to any bit of rx, so we can't directly combine
301+
// (rx & mask) with config. Instead, treat (rx & mask) as a boolean.
302+
if (rx & mask) {
303+
config |= 0x01;
304+
}
305+
306+
if (is_last_descriptor(descriptor)) {
307+
// We've processed the last configuration detection command descriptor
308+
descriptor += min_descriptor_size; // Increment the descriptor for the caller
309+
return 0;
310+
}
311+
descriptor += min_descriptor_size; // next descriptor
312+
}
313+
314+
tr_error("Sector Map: Incomplete configuration detection command descriptors");
315+
return -1;
316+
}
317+
318+
static int sfdp_locate_sector_map_by_config(
319+
const uint8_t config,
320+
sfdp_hdr_info &sfdp_info,
321+
uint8_t *&descriptor,
322+
const uint8_t *table_end)
323+
{
324+
// The size of a sector map descriptor depends on the number of regions. Before
325+
// the number of regions is calculated, use the minimum possible size in the a loop condition.
326+
while (is_sector_map_descriptor(descriptor) && (descriptor + min_descriptor_size <= table_end)) {
327+
size_t regions = descriptor[2] + 1; // Region ID starts at 0
328+
size_t current_descriptor_size = (1 /*header*/ + regions) * 4 /*DWORD size*/;
329+
if (descriptor + current_descriptor_size > table_end) {
330+
tr_error("Sector Map: Incomplete sector map descriptor at the end of the table");
331+
return -1;
332+
}
333+
334+
if (descriptor[1] == config) {
335+
// matching sector map found
336+
return 0;
337+
}
338+
339+
if (is_last_descriptor(descriptor)) {
340+
// We've processed the last sector map descriptor
341+
tr_error("Sector Map: Failed to find a sector map that matches the current configuration");
342+
return -1;
343+
}
344+
345+
descriptor += current_descriptor_size; // next descriptor
346+
}
347+
348+
tr_error("Sector Map: Incomplete sector map descriptors");
349+
return -1;
350+
}
351+
248352
int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info)
249353
{
250354
uint32_t tmp_region_size = 0;
@@ -268,7 +372,7 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, ui
268372
* - sector map configuration detection commands
269373
* - configurations
270374
* - regions in each configuration
271-
* is variable -> the size of this table is variable
375+
* are variable -> the size of this table is variable
272376
*/
273377
auto smptbl_buff = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[sfdp_info.smptbl.size]);
274378
if (!smptbl_buff) {
@@ -291,27 +395,40 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, ui
291395
return -1;
292396
}
293397

294-
// Currently we support only Single Map Descriptor
295-
if (!((smptbl_buff[0] & 0x3) == 0x03) && (smptbl_buff[1] == 0x0)) {
296-
tr_error("Sector Map: Supporting Only Single Map Descriptor (not map commands)");
297-
return -1;
398+
uint8_t *table = smptbl_buff.get();
399+
uint8_t *descriptor = table;
400+
401+
// Detect which configuration is in use
402+
uint8_t active_config_id = 0x00;
403+
status = sfdp_detect_sector_map_configuration(sfdp_reader, sfdp_info, descriptor, table + sfdp_info.smptbl.size, active_config_id);
404+
if (status != 0) {
405+
tr_error("Failed to detect sector map configuration");
406+
return status;
298407
}
299408

300-
sfdp_info.smptbl.region_cnt = smptbl_buff[2] + 1;
409+
// Locate the sector map for the configuration
410+
status = sfdp_locate_sector_map_by_config(active_config_id, sfdp_info, descriptor, table + sfdp_info.smptbl.size);
411+
if (status != 0) {
412+
tr_error("Failed to locate a matching sector map");
413+
return status;
414+
}
415+
416+
// Find the number of regions from the sector map
417+
sfdp_info.smptbl.region_cnt = descriptor[2] + 1;
301418
if (sfdp_info.smptbl.region_cnt > SFDP_SECTOR_MAP_MAX_REGIONS) {
302419
tr_error("Sector Map: Supporting up to %d regions, current setup to %d regions - fail",
303420
SFDP_SECTOR_MAP_MAX_REGIONS,
304421
sfdp_info.smptbl.region_cnt);
305422
return -1;
306423
}
307424

308-
// Loop through Regions and set for each one: size, supported erase types, high boundary offset
309-
// Calculate minimum Common Erase Type for all Regions
425+
// Loop through the regions and set for each one: size, supported erase types, high boundary offset
426+
// Calculate the minimum common erase type for all regions
310427
for (auto idx = 0; idx < sfdp_info.smptbl.region_cnt; idx++) {
311-
tmp_region_size = ((*((uint32_t *)&smptbl_buff[(idx + 1) * 4])) >> 8) & 0x00FFFFFF; // bits 9-32
428+
tmp_region_size = ((*((uint32_t *)&descriptor[(idx + 1) * 4])) >> 8) & 0x00FFFFFF; // bits 9-32
312429
sfdp_info.smptbl.region_size[idx] = (tmp_region_size + 1) * 256; // Region size is 0 based multiple of 256 bytes;
313430

314-
sfdp_info.smptbl.region_erase_types_bitfld[idx] = smptbl_buff[(idx + 1) * 4] & 0x0F; // bits 1-4
431+
sfdp_info.smptbl.region_erase_types_bitfld[idx] = descriptor[(idx + 1) * 4] & 0x0F; // bits 1-4
315432

316433
min_common_erase_type_bits &= sfdp_info.smptbl.region_erase_types_bitfld[idx];
317434

@@ -335,7 +452,6 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, ui
335452
return 0;
336453
}
337454

338-
339455
size_t sfdp_detect_page_size(uint8_t *basic_param_table_ptr, size_t basic_param_table_size)
340456
{
341457
constexpr int SFDP_BASIC_PARAM_TABLE_PAGE_SIZE = 40;

0 commit comments

Comments
 (0)