Skip to content

Commit cc4a117

Browse files
committed
Progress with IFC Flash testing
1 parent 610af12 commit cc4a117

File tree

3 files changed

+156
-43
lines changed

3 files changed

+156
-43
lines changed

config/examples/nxp-t2080.config

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
# NXP T2080 wolfBoot Configuration Template
2-
#
3-
# Stock (default): Compact layout, NOR base 0xEFFE0000
4-
# NAII 68PPC2 (alternate): Larger app partition, NOR base 0xE8000000
5-
# Uncomment the "# NAII 68PPC2:" lines and comment the stock lines to use.
62

73
ARCH=PPC
84
TARGET=nxp_t2080
@@ -59,3 +55,6 @@ WOLFBOOT_DTS_BOOT_ADDRESS?=0xE8040000
5955
WOLFBOOT_DTS_UPDATE_ADDRESS?=0xE8050000
6056
# DTS Load to RAM Address
6157
WOLFBOOT_LOAD_DTS_ADDRESS?=0x200000
58+
59+
# Optional QSPI flash test (erase/write/read on update partition)
60+
#CFLAGS_EXTRA+=-DTEST_FLASH

hal/nxp_t2080.c

Lines changed: 152 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,10 @@ static void hal_flash_init(void)
217217
wolfBoot_printf("IFC CSPR0: 0x%x%s\n", cspr,
218218
(cspr & IFC_CSPR_WP) ? " (WP set)" : "");
219219
#endif
220-
if (cspr & IFC_CSPR_WP) {
221-
set32(IFC_CSPR(0), cspr & ~IFC_CSPR_WP);
222-
}
220+
/* WP clearing is done in hal_flash_clear_wp() from RAMFUNCTION code.
221+
* T2080RM requires V=0 before modifying IFC_CSPR, which is not safe
222+
* during XIP. The RAMFUNCTION code runs from DDR with flash TLB
223+
* guarded, so it can safely toggle V=0 -> modify -> V=1. */
223224

224225
/* Note: hal_flash_getid() is disabled because AMD Autoselect mode
225226
* affects the entire flash bank. Since wolfBoot runs XIP from the same
@@ -676,52 +677,132 @@ static void RAMFUNCTION ram_udelay(uint32_t delay_us)
676677
} while ((tbl_now - tbl_start) < ticks);
677678
}
678679

679-
/* Switch flash TLB to cache-inhibited for direct flash chip access.
680-
* AMD flash commands require writes to reach the chip immediately and
681-
* status reads to come directly from the chip. With MAS2_M (cacheable),
682-
* writes are cached and never reach the flash, reads return stale data.
683-
* Uses direct SPR manipulation to avoid calling .text functions. */
680+
/* Switch flash TLB to cache-inhibited + guarded for direct flash chip access.
681+
* AMD flash commands require writes to reach the chip immediately and status
682+
* reads to come directly from the chip. With MAS2_M (cacheable), stores go
683+
* through the CPC coherency fabric; IFC does not support coherent writes and
684+
* returns a bus error (DSI). tlbre/tlbwe only modifies MAS2 and is unreliable
685+
* when the entry has IPROT=1; use set_tlb() to rewrite all MAS fields.
686+
* Must be called while flash is still in read-array mode (set_tlb lives in
687+
* flash .text, reachable via longcall while TLB is still M/cacheable). */
684688
static void RAMFUNCTION hal_flash_cache_disable(void)
685689
{
686-
uint32_t mas2;
687-
/* Select TLB1, entry 2 (flash) */
688-
mtspr(MAS0, BOOKE_MAS0(1, 2, 0));
689-
__asm__ __volatile__("isync; tlbre; isync");
690-
/* Change WIMGE from M to I|G */
691-
mas2 = mfspr(MAS2);
692-
mas2 &= ~0x1F; /* clear WIMGE bits */
693-
mas2 |= (MAS2_I | MAS2_G);
694-
mtspr(MAS2, mas2);
695-
__asm__ __volatile__("isync; msync; tlbwe; isync");
690+
set_tlb(1, 2,
691+
FLASH_BASE_ADDR, FLASH_BASE_ADDR, FLASH_BASE_PHYS_HIGH,
692+
MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G, 0,
693+
FLASH_TLB_PAGESZ, 1);
696694
}
697695

698696
/* Restore flash TLB to cacheable mode after flash operation.
699-
* Flash is back in read-array mode, safe to cache again. */
697+
* Flash must be back in read-array mode before calling (AMD_CMD_RESET sent).
698+
* Invalidate caches afterward so stale pre-erase data is not served. */
700699
static void RAMFUNCTION hal_flash_cache_enable(void)
701700
{
702-
uint32_t mas2;
703-
/* Select TLB1, entry 2 (flash) */
704-
mtspr(MAS0, BOOKE_MAS0(1, 2, 0));
705-
__asm__ __volatile__("isync; tlbre; isync");
706-
/* Change WIMGE from I|G to M (cacheable) */
707-
mas2 = mfspr(MAS2);
708-
mas2 &= ~0x1F;
709-
mas2 |= MAS2_M;
710-
mtspr(MAS2, mas2);
711-
__asm__ __volatile__("isync; msync; tlbwe; isync");
712-
/* Invalidate D-cache and I-cache — stale entries from before
713-
* the flash operation must be discarded */
701+
set_tlb(1, 2,
702+
FLASH_BASE_ADDR, FLASH_BASE_ADDR, FLASH_BASE_PHYS_HIGH,
703+
MAS3_SX | MAS3_SW | MAS3_SR, MAS2_M, 0,
704+
FLASH_TLB_PAGESZ, 1);
714705
invalidate_dcache();
715706
invalidate_icache();
716707
}
717708

709+
/* Clear IFC write-protect. T2080RM says IFC_CSPR should only be written
710+
* when V=0. Must be called from RAMFUNCTION (DDR) with flash TLB set to
711+
* guarded (MAS2_G) so no speculative access occurs while V is briefly 0. */
712+
static void RAMFUNCTION hal_flash_clear_wp(void)
713+
{
714+
uint32_t cspr = get32(IFC_CSPR(0));
715+
if (cspr & IFC_CSPR_WP) {
716+
/* Clear V first, then modify WP, then re-enable V */
717+
set32(IFC_CSPR(0), cspr & ~(IFC_CSPR_WP | IFC_CSPR_V));
718+
__asm__ __volatile__("sync; isync");
719+
set32(IFC_CSPR(0), (cspr & ~IFC_CSPR_WP) | IFC_CSPR_V);
720+
__asm__ __volatile__("sync; isync");
721+
/* Verify WP cleared */
722+
cspr = get32(IFC_CSPR(0));
723+
wolfBoot_printf("WP clear: CSPR0=0x%x%s\n", cspr,
724+
(cspr & IFC_CSPR_WP) ? " (FAILED)" : " (OK)");
725+
}
726+
}
727+
718728
static void RAMFUNCTION hal_flash_unlock_sector(uint32_t sector)
719729
{
720730
/* AMD unlock sequence */
721731
FLASH_IO8_WRITE(sector, FLASH_UNLOCK_ADDR1, AMD_CMD_UNLOCK_START);
722732
FLASH_IO8_WRITE(sector, FLASH_UNLOCK_ADDR2, AMD_CMD_UNLOCK_ACK);
723733
}
724734

735+
/* Check and clear PPB (Persistent Protection Bits) for a sector.
736+
* S29GL01GS has per-sector non-volatile protection bits. If set, erase/program
737+
* fails with DQ5 error. PPB erase is chip-wide (clears ALL sectors).
738+
* Returns: 0 if unprotected or successfully cleared, -1 on failure. */
739+
static int RAMFUNCTION hal_flash_ppb_unlock(uint32_t sector)
740+
{
741+
uint16_t ppb_status;
742+
uint16_t read1, read2;
743+
uint32_t timeout;
744+
745+
/* Enter PPB ASO (Address Space Overlay) */
746+
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR1, AMD_CMD_UNLOCK_START);
747+
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR2, AMD_CMD_UNLOCK_ACK);
748+
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR1, AMD_CMD_SET_PPB_ENTRY);
749+
750+
/* Read PPB status for target sector: DQ0=0 means protected */
751+
ppb_status = FLASH_IO8_READ(sector, 0);
752+
753+
if ((ppb_status & 0x0101) == 0x0101) {
754+
/* Both chips report unprotected — exit PPB mode and return */
755+
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC1);
756+
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC2);
757+
return 0;
758+
}
759+
760+
/* Exit PPB ASO before calling printf (flash must be in read-array
761+
* mode for I-cache misses to fetch valid instructions) */
762+
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC1);
763+
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC2);
764+
FLASH_IO8_WRITE(0, 0, AMD_CMD_RESET);
765+
ram_udelay(50);
766+
767+
wolfBoot_printf("PPB: sector %d protected (0x%x), erasing all PPBs\n",
768+
sector, ppb_status);
769+
770+
/* Re-enter PPB ASO for erase */
771+
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR1, AMD_CMD_UNLOCK_START);
772+
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR2, AMD_CMD_UNLOCK_ACK);
773+
FLASH_IO8_WRITE(0, FLASH_UNLOCK_ADDR1, AMD_CMD_SET_PPB_ENTRY);
774+
775+
/* PPB Erase All (clears all sectors' PPBs) */
776+
FLASH_IO8_WRITE(0, 0, AMD_CMD_PPB_UNLOCK_BC1); /* 0x80 */
777+
FLASH_IO8_WRITE(0, 0, AMD_CMD_PPB_UNLOCK_BC2); /* 0x30 */
778+
779+
/* Wait for PPB erase completion — poll for toggle stop */
780+
timeout = 0;
781+
do {
782+
read1 = FLASH_IO8_READ(0, 0);
783+
read2 = FLASH_IO8_READ(0, 0);
784+
if (read1 == read2)
785+
break;
786+
ram_udelay(10);
787+
} while (timeout++ < 100000); /* 1 second */
788+
789+
/* Exit PPB ASO */
790+
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC1);
791+
FLASH_IO8_WRITE(0, 0, AMD_CMD_SET_PPB_EXIT_BC2);
792+
793+
/* Reset to read-array mode */
794+
FLASH_IO8_WRITE(0, 0, AMD_CMD_RESET);
795+
ram_udelay(50);
796+
797+
if (timeout >= 100000) {
798+
wolfBoot_printf("PPB: erase timeout\n");
799+
return -1;
800+
}
801+
802+
wolfBoot_printf("PPB: erase complete\n");
803+
return 0;
804+
}
805+
725806
/* wait for toggle to stop and status mask to be met within microsecond timeout.
726807
* RAMFUNCTION: executes from DDR while flash is in program/erase command mode. */
727808
static int RAMFUNCTION hal_flash_status_wait(uint32_t sector, uint16_t mask,
@@ -773,6 +854,12 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
773854

774855
/* Disable flash caching — AMD commands must reach the chip directly */
775856
hal_flash_cache_disable();
857+
hal_flash_clear_wp();
858+
859+
/* Reset flash to read-array mode in case previous operation left it
860+
* in command mode (e.g. after a timeout or incomplete operation) */
861+
FLASH_IO8_WRITE(0, 0, AMD_CMD_RESET);
862+
ram_udelay(50);
776863

777864
pos = 0;
778865
while (len > 0) {
@@ -792,18 +879,15 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
792879

793880
hal_flash_unlock_sector(sector);
794881
FLASH_IO8_WRITE(sector, offset, AMD_CMD_WRITE_TO_BUFFER);
795-
#if FLASH_CFI_WIDTH == 16
796-
FLASH_IO16_WRITE(sector, offset, (nwords-1));
797-
#else
882+
/* Word count (N-1) must be replicated to both chips */
798883
FLASH_IO8_WRITE(sector, offset, (nwords-1));
799-
#endif
800884

801885
for (i=0; i<nwords; i++) {
802886
const uint8_t* ptr = &data[pos];
803887
#if FLASH_CFI_WIDTH == 16
804-
FLASH_IO16_WRITE(sector, i, *((const uint16_t*)ptr));
888+
FLASH_IO16_WRITE(sector, offset + i, *((const uint16_t*)ptr));
805889
#else
806-
FLASH_IO8_WRITE(sector, i, *ptr);
890+
FLASH_IO8_WRITE(sector, offset + i, *ptr);
807891
#endif
808892
pos += (FLASH_CFI_WIDTH/8);
809893
}
@@ -813,6 +897,9 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
813897
/* poll for program completion - max 200ms */
814898
ret = hal_flash_status_wait(sector, 0x44, 200*1000);
815899
if (ret != 0) {
900+
/* Reset flash to read-array mode BEFORE calling printf */
901+
FLASH_IO8_WRITE(sector, 0, AMD_CMD_RESET);
902+
ram_udelay(50);
816903
wolfBoot_printf("Flash Write: Timeout at sector %d\n", sector);
817904
break;
818905
}
@@ -837,6 +924,12 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
837924

838925
/* Disable flash caching — AMD commands must reach the chip directly */
839926
hal_flash_cache_disable();
927+
hal_flash_clear_wp();
928+
929+
/* Reset flash to read-array mode in case previous operation left it
930+
* in command mode (e.g. after a timeout or incomplete operation) */
931+
FLASH_IO8_WRITE(0, 0, AMD_CMD_RESET);
932+
ram_udelay(50);
840933

841934
while (len > 0) {
842935
/* determine sector address */
@@ -847,20 +940,41 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
847940
sector, address, len);
848941
#endif
849942

943+
/* Check and clear PPB protection if set */
944+
if (hal_flash_ppb_unlock(sector) != 0) {
945+
wolfBoot_printf("Flash Erase: PPB unlock failed sector %d\n", sector);
946+
ret = -1;
947+
break;
948+
}
949+
950+
wolfBoot_printf("Erasing sector %d...\n", sector);
951+
850952
hal_flash_unlock_sector(sector);
851953
FLASH_IO8_WRITE(sector, FLASH_UNLOCK_ADDR1, AMD_CMD_ERASE_START);
852954
hal_flash_unlock_sector(sector);
853955
FLASH_IO8_WRITE(sector, 0, AMD_CMD_ERASE_SECTOR);
854956
/* block erase timeout = 50us - for additional sectors */
855957
/* Typical is 200ms (max 1100ms) */
856958

857-
/* poll for erase completion - max 1.1 sec */
959+
/* poll for erase completion - max 1.1 sec
960+
* NOTE: Do NOT call wolfBoot_printf while flash is in erase mode.
961+
* With cache-inhibited TLB, I-cache misses fetch from flash which
962+
* returns status data instead of instructions. */
858963
ret = hal_flash_status_wait(sector, 0x4C, 1100*1000);
859964
if (ret != 0) {
965+
/* Reset flash to read-array mode BEFORE calling printf */
966+
FLASH_IO8_WRITE(sector, 0, AMD_CMD_RESET);
967+
ram_udelay(50);
860968
wolfBoot_printf("Flash Erase: Timeout at sector %d\n", sector);
861969
break;
862970
}
863971

972+
/* Erase succeeded — flash is back in read-array mode.
973+
* Reset to be safe before any printf (I-cache may miss) */
974+
FLASH_IO8_WRITE(sector, 0, AMD_CMD_RESET);
975+
ram_udelay(10);
976+
wolfBoot_printf("Erase sector %d: OK\n", sector);
977+
864978
address += FLASH_SECTOR_SIZE;
865979
len -= FLASH_SECTOR_SIZE;
866980
}

hal/nxp_t2080.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ enum ifc_amask_sizes {
131131

132132
/* ---- NOR Flash ---- */
133133
#define FLASH_BANK_SIZE (128*1024*1024)
134-
#define FLASH_PAGE_SIZE (1024) /* program buffer */
134+
#define FLASH_PAGE_SIZE (512) /* program buffer (256 bytes per chip x 2 chips) */
135135
#define FLASH_SECTOR_SIZE (128*1024)
136136
#define FLASH_SECTORS (FLASH_BANK_SIZE / FLASH_SECTOR_SIZE)
137137
#define FLASH_CFI_WIDTH 16 /* 8 or 16 */

0 commit comments

Comments
 (0)