changeset 979:c5133c3c11b1

fc-loadtool flash: implement PL-J PPB programming functions
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 02 Dec 2023 04:21:59 +0000
parents a400bb4a1620
children 0a4d19aab608
files loadtools/flamdsec.c loadtools/flash.h loadtools/fldevs.c
diffstat 3 files changed, 165 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/loadtools/flamdsec.c	Fri Dec 01 08:05:31 2023 +0000
+++ b/loadtools/flamdsec.c	Sat Dec 02 04:21:59 2023 +0000
@@ -1,12 +1,14 @@
 /*
- * This module is a place to implement commands that are specific to
- * security features (OTP, sector locking) of AMD-style flash chips.
+ * This module is a place to implement commands and functions for
+ * sector write-protection (locking and unlocking, checking current
+ * lock state) on AMD-style flash chips.
  */
 
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include "flash.h"
 
 extern struct flash_bank_info flash_bank_info[2];
@@ -168,3 +170,140 @@
 	}
 	return(0);
 }
+
+static
+plj_ppb_write_op(base_addr, is_erase, retp)
+	uint32_t base_addr;
+	uint16_t *retp;
+{
+	if (do_w16(base_addr + 0xAAA, 0xAA)) {
+bad_w16:	fprintf(stderr,
+	"unexpected response to w16 in PPB command sequence - aborting\n");
+		return(-1);
+	}
+	if (do_w16(base_addr + 0x554, 0x55))
+		goto bad_w16;
+	if (do_w16(base_addr + 0xAAA, 0x60))
+		goto bad_w16;
+	if (do_w16(base_addr + 4, is_erase ? 0x60 : 0x68))
+		goto bad_w16;
+	usleep(1200);			/* per S29PL-J datasheet */
+	if (do_w16(base_addr + 4, is_erase ? 0x40 : 0x48))
+		goto bad_w16;
+	if (do_r16(base_addr + 4, retp) < 0)
+		return(-1);
+	return(0);
+}
+
+plj_ppb_program_one(bi, sector_addr)
+	struct flash_bank_info *bi;
+	uint32_t sector_addr;
+{
+	uint16_t stat;
+	unsigned pulsecnt;
+	int rc;
+
+	for (pulsecnt = 0; pulsecnt < 25; ) {
+		rc = plj_ppb_write_op(bi->base_addr + sector_addr, 0, &stat);
+		if (rc < 0)
+			return(rc);
+		pulsecnt++;
+		if (!(stat & 1))
+			continue;
+		printf("PPB 0x%X programmed with %u pulse%s\n", sector_addr,
+			pulsecnt, pulsecnt > 1 ? "s" : "");
+		return(0);
+	}
+	printf("PPB 0x%X programming FAILED, tried %u pulses\n", sector_addr,
+		pulsecnt);
+	return(-1);
+}
+
+plj_ppb_program_all_single(bank)
+{
+	struct flash_bank_info *bi = flash_bank_info + bank;
+	struct amd_lock_info *li = bi->amd_lock;
+	struct lock_group_desc *grp;
+	uint32_t offset;
+	unsigned ng, nb;
+	int rc;
+
+	offset = 0;
+	for (ng = 0; ng < li->ngroups; ng++) {
+		grp = li->groups + ng;
+		for (nb = 0; nb < grp->nblocks; nb++) {
+			rc = plj_ppb_program_one(bi, offset);
+			if (rc < 0)
+				return(rc);
+			offset += grp->block_size;
+		}
+	}
+	return(0);
+}
+
+plj_ppb_program_all_dualbank(reqbank)
+{
+	int altbank = !reqbank;
+	int rc;
+
+	if (flash_detect(altbank, 0) < 0)
+		return(-1);
+	if (flash_bank_info[0].device != flash_bank_info[1].device) {
+		fprintf(stderr, "error: mismatch between two flash banks\n");
+		return(-1);
+	}
+	printf("Programming all PPBs in flash bank 0\n");
+	rc = plj_ppb_program_all_single(0);
+	if (rc < 0)
+		return(-1);
+	printf("Programming all PPBs in flash bank 1\n");
+	rc = plj_ppb_program_all_single(1);
+	if (rc < 0)
+		return(-1);
+	return(0);
+}
+
+static
+plj_ppb_erase_cycle(bank)
+{
+	struct flash_bank_info *bi = flash_bank_info + bank;
+	uint16_t stat;
+	unsigned pulsecnt;
+	int rc;
+
+	printf("Performing PPB erase cycle\n");
+	for (pulsecnt = 0; pulsecnt < 1000; ) {
+		rc = plj_ppb_write_op(bi->base_addr, 1, &stat);
+		if (rc < 0)
+			return(rc);
+		pulsecnt++;
+		if (stat & 1)
+			continue;
+		printf("PPB erase cycle succeeded after %u pulse%s\n",
+			pulsecnt, pulsecnt > 1 ? "s" : "");
+		return(0);
+	}
+	printf("PPB erase cycle FAILED, tried %u pulses\n", pulsecnt);
+	return(-1);
+}
+
+plj_ppb_erase_all_single(bank)
+{
+	int rc;
+
+	printf("Programming all PPBs before erase cycle\n");
+	rc = plj_ppb_program_all_single(bank);
+	if (rc < 0)
+		return(-1);
+	return plj_ppb_erase_cycle(bank);
+}
+
+plj_ppb_erase_all_dualbank(reqbank)
+{
+	int rc;
+
+	rc = plj_ppb_program_all_dualbank(reqbank);
+	if (rc < 0)
+		return(-1);
+	return plj_ppb_erase_cycle(reqbank);
+}
--- a/loadtools/flash.h	Fri Dec 01 08:05:31 2023 +0000
+++ b/loadtools/flash.h	Sat Dec 02 04:21:59 2023 +0000
@@ -61,6 +61,9 @@
 	int			have_status_word_7;
 	int			have_mode_lock_bits;
 	int			have_pln_lock_reg;
+	int			(*ppb_program_one)();
+	int			(*ppb_program_all)();
+	int			(*ppb_erase_all)();
 };
 
 struct flash_device {
--- a/loadtools/fldevs.c	Fri Dec 01 08:05:31 2023 +0000
+++ b/loadtools/fldevs.c	Sat Dec 02 04:21:59 2023 +0000
@@ -9,6 +9,12 @@
 extern struct flash_cmdset flash_cmdset_intel;
 extern struct flash_cmdset flash_cmdset_intel_w30;
 
+extern int plj_ppb_program_one();
+extern int plj_ppb_program_all_single();
+extern int plj_ppb_program_all_dualbank();
+extern int plj_ppb_erase_all_single();
+extern int plj_ppb_erase_all_dualbank();
+
 /* flash bank geometries */
 
 static struct flash_geom geom_2M_topboot = {
@@ -434,6 +440,9 @@
 		{0x2000, 8, 0, 0, 1}},
 	.have_status_word_3	= 1,
 	.have_mode_lock_bits	= 1,
+	.ppb_program_one	= plj_ppb_program_one,
+	.ppb_program_all	= plj_ppb_program_all_single,
+	.ppb_erase_all		= plj_ppb_erase_all_single,
 };
 
 struct flash_device flashdev_PL064J = {
@@ -484,6 +493,9 @@
 		{0x40000, 24, 1, 1, 1}},
 	.have_status_word_3	= 1,
 	.have_mode_lock_bits	= 1,
+	.ppb_program_one	= plj_ppb_program_one,
+	.ppb_program_all	= plj_ppb_program_all_dualbank,
+	.ppb_erase_all		= plj_ppb_erase_all_dualbank,
 };
 
 static struct amd_lock_info PL129J_lock_info_1 = {
@@ -494,6 +506,9 @@
 		{0x10000, 3, 0, 0, 0},
 		{0x2000, 8, 0, 0, 1}},
 	.have_status_word_3	= 1,
+	.ppb_program_one	= plj_ppb_program_one,
+	.ppb_program_all	= plj_ppb_program_all_dualbank,
+	.ppb_erase_all		= plj_ppb_erase_all_dualbank,
 };
 
 struct flash_device flashdev_PL129J = {
@@ -685,6 +700,9 @@
 	.have_status_word_3	= 1,
 	.have_status_word_7	= 1,
 	.have_mode_lock_bits	= 1,
+	.ppb_program_one	= plj_ppb_program_one,
+	.ppb_program_all	= plj_ppb_program_all_dualbank,
+	.ppb_erase_all		= plj_ppb_erase_all_dualbank,
 };
 
 static struct amd_lock_info K5L29_lock_info_1 = {
@@ -696,6 +714,9 @@
 		{0x2000, 8, 0, 0, 1}},
 	.have_status_word_3	= 1,
 	.have_status_word_7	= 1,
+	.ppb_program_one	= plj_ppb_program_one,
+	.ppb_program_all	= plj_ppb_program_all_dualbank,
+	.ppb_erase_all		= plj_ppb_erase_all_dualbank,
 };
 
 struct flash_device flashdev_K5L29xx_A = {