// SPDX-License-Identifier: GPL-2.0-or-later /*************************************************************************** * Copyright (C) 2019 by Andreas Bolsch > 2, (uint32_t *)&header); if (retval != ERROR_OK) return retval; LOG_DEBUG("header 0x%08" PRIx32 " 0x%08" PRIx32, header.signature, header.revision); if (header.signature != SFDP_MAGIC) { LOG_INFO("no SDFP found"); return ERROR_FLASH_BANK_NOT_PROBED; } if (((header.revision >> 24) & 0xFF) != SFDP_ACCESS_PROT) { LOG_ERROR("access protocol 0x%02x not implemented", (header.revision >> 24) & 0xFFU); return ERROR_FLASH_BANK_NOT_PROBED; } /* retrieve table of parameter headers */ nph = ((header.revision >> 16) & 0xFF) + 1; LOG_DEBUG("parameter headers: %d", nph); pheaders = malloc(sizeof(struct sfdp_phdr) * nph); if (!pheaders) { LOG_ERROR("not enough memory"); return ERROR_FAIL; } memset(pheaders, 0, sizeof(struct sfdp_phdr) * nph); retval = read_sfdp_block(bank, sizeof(header), (sizeof(struct sfdp_phdr) >> 2) * nph, (uint32_t *)pheaders); if (retval != ERROR_OK) goto err; for (k = 0; k < nph; k++) { uint8_t words = (pheaders[k].revision >> 24) & 0xFF; uint16_t id = (((pheaders[k].ptr) >> 16) & 0xFF00) | (pheaders[k].revision & 0xFF); uint32_t ptr = pheaders[k].ptr & 0xFFFFFF; LOG_DEBUG("pheader %d len=0x%02" PRIx8 " id=0x%04" PRIx16 " ptr=0x%06" PRIx32, k, words, id, ptr); /* retrieve parameter table */ ptable = malloc(words << 2); if (!ptable) { LOG_ERROR("not enough memory"); retval = ERROR_FAIL; goto err; } retval = read_sfdp_block(bank, ptr, words, ptable); if (retval != ERROR_OK) goto err; for (j = 0; j < words; j++) LOG_DEBUG("word %02d 0x%08X", j + 1, ptable[j]); if (id == SFDP_BASIC_FLASH) { struct sfdp_basic_flash_param *table = (struct sfdp_basic_flash_param *)ptable; uint16_t erase; if (words < 9) { LOG_ERROR("id=0x%04" PRIx16 " invalid length %d", id, words); retval = ERROR_FLASH_BANK_NOT_PROBED; goto err; } LOG_DEBUG("basic flash parameter table"); /* dummy device name */ dev->name = sfdp_name; /* default instructions */ dev->read_cmd = SPIFLASH_READ; dev->pprog_cmd = SPIFLASH_PAGE_PROGRAM; dev->chip_erase_cmd = SPIFLASH_MASS_ERASE; /* get device size */ if (table->density & (1UL << 31)) dev->size_in_bytes = 1UL << ((table->density & ~(1UL << 31)) - 3); else dev->size_in_bytes = (table->density + 1) >> 3; /* 2-2-2 read instruction, not used */ if (table->fast_444 & (1UL << 0)) dev->qread_cmd = (table->read_222 >> 24) & 0xFF; /* 4-4-4 read instruction */ if (table->fast_444 & (1UL << 4)) dev->qread_cmd = (table->read_444 >> 24) & 0xFF; /* find the largest erase block size and instruction */ erase = (table->erase_t12 >> 0) & 0xFFFF; erase_type = 1; if (((table->erase_t12 >> 16) & 0xFF) > (erase & 0xFF)) { erase = (table->erase_t12 >> 16) & 0xFFFF; erase_type = 2; } if (((table->erase_t34 >> 0) & 0xFF) > (erase & 0xFF)) { erase = (table->erase_t34 >> 0) & 0xFFFF; erase_type = 3; } if (((table->erase_t34 >> 16) & 0xFF) > (erase & 0xFF)) { erase = (table->erase_t34 >> 16) & 0xFFFF; erase_type = 4; } dev->erase_cmd = (erase >> 8) & 0xFF; dev->sectorsize = 1UL << (erase & 0xFF); if ((offsetof(struct sfdp_basic_flash_param, chip_byte) >> 2) < words) { /* get Program Page Size, if chip_byte present, that's optional */ dev->pagesize = 1UL << ((table->chip_byte >> 4) & 0x0F); } else { /* no explicit page size specified ... */ if (table->fast_addr & (1UL << 2)) { /* Write Granularity = 1, use 64 bytes */ dev->pagesize = 1UL << 6; } else { /* Write Granularity = 0, use 16 bytes */ dev->pagesize = 1UL << 4; } } if (dev->size_in_bytes > (1UL << 24)) { if (((table->fast_addr >> 17) & 0x3) == 0x0) LOG_ERROR("device needs paging - not implemented"); /* 4-byte addresses needed if more than 16 MBytes */ if (((offsetof(struct sfdp_basic_flash_param, addr_reset) >> 2) < words) && (table->addr_reset & (1UL << 29))) { /* dedicated 4-byte-address instructions, hopefully these ... * this entry is unfortunately optional as well * a subsequent 4-byte address table may overwrite this */ dev->read_cmd = 0x13; dev->pprog_cmd = 0x12; dev->erase_cmd = 0xDC; if (dev->qread_cmd != 0) dev->qread_cmd = 0xEC; } else if (((table->fast_addr >> 17) & 0x3) == 0x1) LOG_INFO("device has to be switched to 4-byte addresses"); } } else if (id == SFDP_4BYTE_ADDR) { struct sfdp_4byte_addr_param *table = (struct sfdp_4byte_addr_param *)ptable; if (words >= (offsetof(struct sfdp_4byte_addr_param, erase_t1234) + sizeof(table->erase_t1234)) >> 2) { LOG_INFO("4-byte address parameter table"); /* read and page program instructions */ if (table->flags & (1UL << 0)) dev->read_cmd = 0x13; if (table->flags & (1UL << 5)) dev->qread_cmd = 0xEC; if (table->flags & (1UL << 6)) dev->pprog_cmd = 0x12; /* erase instructions */ if ((erase_type == 1) && (table->flags & (1UL << 9))) dev->erase_cmd = (table->erase_t1234 >> 0) & 0xFF; else if ((erase_type == 2) && (table->flags & (1UL << 10))) dev->erase_cmd = (table->erase_t1234 >> 8) & 0xFF; else if ((erase_type == 3) && (table->flags & (1UL << 11))) dev->erase_cmd = (table->erase_t1234 >> 16) & 0xFF; else if ((erase_type == 4) && (table->flags & (1UL << 12))) dev->erase_cmd = (table->erase_t1234 >> 24) & 0xFF; } else LOG_ERROR("parameter table id=0x%04" PRIx16 " invalid length %d", id, words); } else LOG_DEBUG("unimplemented parameter table id=0x%04" PRIx16, id); free(ptable); ptable = NULL; } if (erase_type != 0) { LOG_INFO("valid SFDP detected"); retval = ERROR_OK; } else { LOG_ERROR("incomplete/invalid SFDP"); retval = ERROR_FLASH_BANK_NOT_PROBED; } err: free(pheaders); free(ptable); return retval; }