changeset 976:ff4ce8d5ece4

fc-loadtool flash: definitions for AMD sector lock architecture
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 01 Dec 2023 06:43:21 +0000
parents 343552c6c621
children 511e2b85c115
files loadtools/flash.h loadtools/flashid.c loadtools/fldevs.c
diffstat 3 files changed, 213 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/loadtools/flash.h	Tue Nov 28 19:08:53 2023 +0000
+++ b/loadtools/flash.h	Fri Dec 01 06:43:21 2023 +0000
@@ -36,12 +36,40 @@
 	uint8_t	expect_val;
 };
 
+/*
+ * In order to examine non-volatile sector lock state of AMD-style flash
+ * chips, we need to know how their sectors are grouped for the purpose
+ * of locking: aggregation of sectors into groups that can only be locked
+ * or unlocked as a unit, and also grouping into independent-read partitions
+ * where each partition needs its own Autoselect sequence.
+ */
+
+struct lock_group_desc {
+	uint32_t	block_size;
+	unsigned	nblocks;
+	int		is_group;
+	int		part_begin;
+	int		part_end;
+};
+
+#define	MAX_AMD_LOCK_GROUPS	8
+
+struct amd_lock_info {
+	unsigned		ngroups;
+	struct lock_group_desc	groups[MAX_AMD_LOCK_GROUPS];
+	int			have_status_word_3;
+	int			have_status_word_7;
+	int			have_mode_lock_bits;
+	int			have_pln_lock_reg;
+};
+
 struct flash_device {
 	char			*name;
 	struct cfi_check	*cfi_table;
 	int			required_global_config;
 	struct flash_geom	*bank_geom[2];
 	struct flash_cmdset	*cmdset;
+	struct amd_lock_info	*lock_info[2];
 };
 
 /* the following structures describe flash banks as accessible to us */
@@ -70,6 +98,7 @@
 	struct flash_device	*device;
 	struct flash_geom	*geom;
 	struct flash_cmdset	*ops;
+	struct amd_lock_info	*amd_lock;
 	struct sector_info	*sectors;
 	int			detect_done;
 };
--- a/loadtools/flashid.c	Tue Nov 28 19:08:53 2023 +0000
+++ b/loadtools/flashid.c	Fri Dec 01 06:43:21 2023 +0000
@@ -21,6 +21,7 @@
 
 extern struct flash_device flashdev_Am29DL640G;
 extern struct flash_device flashdev_PL032J;
+extern struct flash_device flashdev_PL064J;
 extern struct flash_device flashdev_PL129J;
 extern struct flash_device flashdev_PL129N;
 extern struct flash_device flashdev_K5A32xx_T;
@@ -133,6 +134,37 @@
 }
 
 static
+spansion_pl064j_or_oldamd(bi)
+	struct flash_bank_info *bi;
+{
+	int rc;
+
+	printf("Am29DL640G or PL064J, looking at CFI\n");
+	if (do_w16(bi->base_addr + 0xAA, 0x98)) {
+		fprintf(stderr, "unexpected response to w16 - aborting\n");
+		return(-1);
+	}
+	rc = run_cfi_check(bi, flashdev_PL064J.cfi_table);
+	if (rc < 0)
+		return(rc);
+	if (rc) {
+		printf("Found PL064J\n");
+		bi->device = &flashdev_PL064J;
+		return(0);
+	}
+	rc = run_cfi_check(bi, flashdev_Am29DL640G.cfi_table);
+	if (rc < 0)
+		return(rc);
+	if (rc) {
+		printf("Found Am29DL640G\n");
+		bi->device = &flashdev_Am29DL640G;
+		return(0);
+	}
+	fprintf(stderr, "Error: no matching CFI found\n");
+	return(-1);
+}
+
+static
 amd_extended_id(bi)
 	struct flash_bank_info *bi;
 {
@@ -147,7 +179,7 @@
 	if (ext1 == 0x2221 && ext2 == 0x2200)
 		return spansion_pl129j_or_n(bi);
 	if (ext1 == 0x2202 && ext2 == 0x2201)
-		return try_device(bi, &flashdev_Am29DL640G);
+		return spansion_pl064j_or_oldamd(bi);
 	if (ext1 == 0x220A && ext2 == 0x2201)
 		return try_device(bi, &flashdev_PL032J);
 	fprintf(stderr, "Error: unknown device ID\n");
@@ -251,10 +283,13 @@
 		return(-1);
 	}
 	/* good to go */
-	if (bi->device->bank_geom[1] && bank)
+	if (bi->device->bank_geom[1] && bank) {
 		bi->geom = bi->device->bank_geom[1];
-	else
+		bi->amd_lock = bi->device->lock_info[1];
+	} else {
 		bi->geom = bi->device->bank_geom[0];
+		bi->amd_lock = bi->device->lock_info[0];
+	}
 	bi->ops = bi->device->cmdset;
 	bi->detect_done = 1;
 	/* return device to read array mode */
--- a/loadtools/fldevs.c	Tue Nov 28 19:08:53 2023 +0000
+++ b/loadtools/fldevs.c	Fri Dec 01 06:43:21 2023 +0000
@@ -316,15 +316,31 @@
 	{0x42, 'I'},
 	{0x43, '1'},
 	{0x44, '3'},
+	{0x49, 0x04},
 	{-1,   0}
 };
 
+static struct amd_lock_info Am29DL640G_lock_info = {
+	.ngroups		= 8,
+	.groups			= {
+		{0x2000, 8, 0, 1, 0},
+		{0x10000, 3, 0, 0, 0},
+		{0x40000, 3, 1, 0, 1},
+		{0x40000, 12, 1, 1, 1},
+		{0x40000, 12, 1, 1, 1},
+		{0x40000, 3, 1, 1, 0},
+		{0x10000, 3, 0, 0, 0},
+		{0x2000, 8, 0, 0, 1}},
+	.have_status_word_3	= 1,
+};
+
 struct flash_device flashdev_Am29DL640G = {
 	.name			= "Am29DL640G",
 	.cfi_table		= Am29DL640G_cfi,
 	.required_global_config	= FLASH_GLOBAL_CFG_SINGLE_8M,
 	.bank_geom		= {&geom_8M_bothends, 0},
 	.cmdset			= &flash_cmdset_amd,
+	.lock_info		= {&Am29DL640G_lock_info, 0},
 };
 
 /* Spansion S71PL-J and S71PL-N flash */
@@ -367,7 +383,67 @@
 	.cmdset			= &flash_cmdset_amd,
 };
 
-/* S29PL064J/S71PL064J is identical to Am29DL640G covered above */
+/*
+ * For our purposes, S29PL064J/S71PL064J differs from Am29DL640G only
+ * in terms of lock status retrieval and manipulation: the older chip
+ * lacks persistent/password mode lock bits and in-system lock/unlock
+ * operations.  We distinguish them by one byte in CFI.
+ */
+
+static struct cfi_check spansion_PL064J_cfi[] = {
+	{0x10, 'Q'},
+	{0x11, 'R'},
+	{0x12, 'Y'},
+	{0x13, 0x02},
+	{0x14, 0x00},
+	{0x15, 0x40},
+	{0x16, 0x00},
+	{0x27, 0x17},
+	{0x2C, 0x03},
+	{0x2D, 0x07},
+	{0x2E, 0x00},
+	{0x2F, 0x20},
+	{0x30, 0x00},
+	{0x31, 0x7D},
+	{0x32, 0x00},
+	{0x33, 0x00},
+	{0x34, 0x01},
+	{0x35, 0x07},
+	{0x36, 0x00},
+	{0x37, 0x20},
+	{0x38, 0x00},
+	{0x40, 'P'},
+	{0x41, 'R'},
+	{0x42, 'I'},
+	{0x43, '1'},
+	{0x44, '3'},
+	{0x49, 0x07},
+	{-1,   0}
+};
+
+static struct amd_lock_info PL064J_lock_info = {
+	.ngroups		= 8,
+	.groups			= {
+		{0x2000, 8, 0, 1, 0},
+		{0x10000, 3, 0, 0, 0},
+		{0x40000, 3, 1, 0, 1},
+		{0x40000, 12, 1, 1, 1},
+		{0x40000, 12, 1, 1, 1},
+		{0x40000, 3, 1, 1, 0},
+		{0x10000, 3, 0, 0, 0},
+		{0x2000, 8, 0, 0, 1}},
+	.have_status_word_3	= 1,
+	.have_mode_lock_bits	= 1,
+};
+
+struct flash_device flashdev_PL064J = {
+	.name			= "Spansion S29PL064J",
+	.cfi_table		= spansion_PL064J_cfi,
+	.required_global_config	= FLASH_GLOBAL_CFG_SINGLE_8M,
+	.bank_geom		= {&geom_8M_bothends, 0},
+	.cmdset			= &flash_cmdset_amd,
+	.lock_info		= {&PL064J_lock_info, 0},
+};
 
 static struct cfi_check spansion_PL129J_cfi[] = {
 	{0x10, 'Q'},
@@ -399,12 +475,35 @@
 	{-1,   0}
 };
 
+static struct amd_lock_info PL129J_lock_info_0 = {
+	.ngroups		= 4,
+	.groups			= {
+		{0x2000, 8, 0, 1, 0},
+		{0x10000, 3, 0, 0, 0},
+		{0x40000, 7, 1, 0, 1},
+		{0x40000, 24, 1, 1, 1}},
+	.have_status_word_3	= 1,
+	.have_mode_lock_bits	= 1,
+};
+
+static struct amd_lock_info PL129J_lock_info_1 = {
+	.ngroups		= 4,
+	.groups			= {
+		{0x40000, 24, 1, 1, 1},
+		{0x40000, 7, 1, 1, 0},
+		{0x10000, 3, 0, 0, 0},
+		{0x2000, 8, 0, 0, 1}},
+	.have_status_word_3	= 1,
+	.have_mode_lock_bits	= 1,
+};
+
 struct flash_device flashdev_PL129J = {
 	.name			= "Spansion S29PL129J",
 	.cfi_table		= spansion_PL129J_cfi,
 	.required_global_config	= FLASH_GLOBAL_CFG_DUAL_8M,
 	.bank_geom		= {&geom_8M_bottomboot, &geom_8M_topboot},
 	.cmdset			= &flash_cmdset_amd,
+	.lock_info		= {&PL129J_lock_info_0, &PL129J_lock_info_1},
 };
 
 static struct cfi_check spansion_PL129N_cfi[] = {
@@ -437,6 +536,26 @@
 	{-1,   0}
 };
 
+static struct amd_lock_info PL129N_lock_info_0 = {
+	.ngroups		= 3,
+	.groups			= {
+		{0x10000, 4, 0, 1, 0},
+		{0x40000, 7, 1, 0, 1},
+		{0x40000, 24, 1, 1, 1}},
+	.have_status_word_3	= 1,
+	.have_pln_lock_reg	= 1,
+};
+
+static struct amd_lock_info PL129N_lock_info_1 = {
+	.ngroups		= 3,
+	.groups			= {
+		{0x40000, 24, 1, 1, 1},
+		{0x40000, 7, 1, 1, 0},
+		{0x10000, 4, 0, 0, 1}},
+	.have_status_word_3	= 1,
+	.have_pln_lock_reg	= 1,
+};
+
 struct flash_device flashdev_PL129N = {
 	.name			= "Spansion S29PL129N",
 	.cfi_table		= spansion_PL129N_cfi,
@@ -444,6 +563,7 @@
 	.bank_geom		= {&geom_8M_bottomboot_big,
 				   &geom_8M_topboot_big},
 	.cmdset			= &flash_cmdset_amd,
+	.lock_info		= {&PL129N_lock_info_0, &PL129N_lock_info_1},
 };
 
 /* Samsung K5A32xxCTM introduced onto the scene by Openmoko */
@@ -557,10 +677,35 @@
 	{-1,   0}
 };
 
+static struct amd_lock_info K5L29_lock_info_0 = {
+	.ngroups		= 4,
+	.groups			= {
+		{0x2000, 8, 0, 1, 0},
+		{0x10000, 3, 0, 0, 0},
+		{0x40000, 7, 1, 0, 1},
+		{0x40000, 24, 1, 1, 1}},
+	.have_status_word_3	= 1,
+	.have_status_word_7	= 1,
+	.have_mode_lock_bits	= 1,
+};
+
+static struct amd_lock_info K5L29_lock_info_1 = {
+	.ngroups		= 4,
+	.groups			= {
+		{0x40000, 24, 1, 1, 1},
+		{0x40000, 7, 1, 1, 0},
+		{0x10000, 3, 0, 0, 0},
+		{0x2000, 8, 0, 0, 1}},
+	.have_status_word_3	= 1,
+	.have_status_word_7	= 1,
+	.have_mode_lock_bits	= 1,
+};
+
 struct flash_device flashdev_K5L29xx_A = {
 	.name			= "Samsung K5L29xx_A",
 	.cfi_table		= samsung_PL129J_equiv_cfi,
 	.required_global_config	= FLASH_GLOBAL_CFG_DUAL_8M,
 	.bank_geom		= {&geom_8M_bottomboot, &geom_8M_topboot},
 	.cmdset			= &flash_cmdset_amd,
+	.lock_info		= {&K5L29_lock_info_0, &K5L29_lock_info_1},
 };