view loadtools/flamdsec.c @ 1014:961efadd530a default tip

fc-shell TCH DL handler: add support for CSD modes TCH DL capture mechanism in FC Tourmaline firmware has been extended to support CSD modes in addition to speech - add the necessary support on the host tools side. It needs to be noted that this mechanism in its present state does NOT provide the debug utility value that was sought: as we learned only after the code was implemented, TI's DSP has a misfeature in that the buffer we are reading (a_dd_0[]) is zeroed out when the IDS block is enabled, i.e., we are reading all zeros and not the real DL bits we were after. But since the code has already been written, we are keeping it - perhaps we can do some tests with IDS disabled.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 26 Nov 2024 06:27:43 +0000
parents ad3041e19884
children
line wrap: on
line source

/*
 * 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 <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include "flash.h"

extern struct flash_bank_info flash_bank_info[2];

/*
 * Some common functions for Spansion PL-N flash: common between
 * flash lock-state retrieval and active PPB programming.
 */

static
pln_special_mode_entry(base_addr, mode_opc)
	uint32_t base_addr;
	uint16_t mode_opc;
{
	if (do_w16(base_addr + 0xAAA, 0xAA)) {
bad_w16:	fprintf(stderr,
"unexpected response to w16 in PL-N special mode entry sequence - aborting\n");
		return(-1);
	}
	if (do_w16(base_addr + 0x554, 0x55))
		goto bad_w16;
	if (do_w16(base_addr + 0xAAA, mode_opc))
		goto bad_w16;
	return(0);
}

static
pln_special_mode_exit(base_addr)
	uint32_t base_addr;
{
	if (do_w16(base_addr, 0x90)) {
bad_w16:	fprintf(stderr,
"unexpected response to w16 in PL-N special mode exit sequence - aborting\n");
		return(-1);
	}
	if (do_w16(base_addr, 0x00))
		goto bad_w16;
	return(0);
}

/*
 * flash lock-state implementation with its helper functions.
 */

static
issue_read_id(base_addr)
	uint32_t base_addr;
{
	if (do_w16(base_addr + 0xAAA, 0xAA)) {
bad_w16:	fprintf(stderr,
	"unexpected response to w16 in read ID cmd sequence - aborting\n");
		return(-1);
	}
	if (do_w16(base_addr + 0x554, 0x55))
		goto bad_w16;
	if (do_w16(base_addr + 0xAAA, 0x90))
		goto bad_w16;
	return(0);
}

static
issue_reset_cmd(base_addr)
	uint32_t base_addr;
{
	if (do_w16(base_addr + 0xAAA, 0xF0)) {
		fprintf(stderr,
	"unexpected response to w16 when resetting flash to read mode!\n");
		return(-1);
	}
	return(0);
}

static
read_mode_lock_word(base_addr, word_offset, retp)
	uint32_t base_addr, word_offset;
	uint16_t *retp;
{
	if (do_w16(base_addr + 0xAAA, 0xAA)) {
bad_w16:	fprintf(stderr,
	"unexpected response to w16 in mode lock query 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 + word_offset, 0x48))
		goto bad_w16;
	if (do_r16(base_addr + word_offset, retp) < 0)
		return(-1);
	return(0);
}

flashcmd_lock_state(argc, argv, bank)
	char **argv;
{
	struct flash_bank_info *bi;
	struct amd_lock_info *li;
	struct lock_group_desc *grp;
	uint32_t offset, part_addr;
	uint16_t word;
	unsigned ng, nb;

	if (argc > 2) {
		fprintf(stderr, "error: too many arguments\n");
		return(-1);
	}
	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	li = bi->amd_lock;
	if (!li) {
		fprintf(stderr,
			"Operation not supported for this flash chip type\n");
		return(-1);
	}
	offset = 0;
	for (ng = 0; ng < li->ngroups; ng++) {
		grp = li->groups + ng;
		if (grp->part_begin) {
			part_addr = bi->base_addr + offset;
			if (issue_read_id(part_addr) < 0)
				return(-1);
		}
		if (offset == 0 && li->have_status_word_3) {
			if (do_r16(bi->base_addr + 6, &word) < 0)
				return(-1);
			printf("Global status word 3: %04X\n", word);
		}
		if (offset == 0 && li->have_status_word_7) {
			if (do_r16(bi->base_addr + 0xE, &word) < 0)
				return(-1);
			printf("Global status word 7: %04X\n", word);
		}
		for (nb = 0; nb < grp->nblocks; nb++) {
			if (do_r16(bi->base_addr + offset + 4, &word) < 0)
				return(-1);
			printf("Sector%s at 0x%X: %s\n",
				grp->is_group ? " group" : "", offset,
				(word & 1) ? "locked" : "unlocked");
			offset += grp->block_size;
		}
		if (grp->part_end) {
			if (issue_reset_cmd(part_addr) < 0)
				return(-1);
		}
	}
	if (li->have_mode_lock_bits) {
		if (read_mode_lock_word(bi->base_addr, 0x14, &word) < 0)
			return(-1);
		printf("Password Protection Mode lock: %04X\n", word);
		if (issue_reset_cmd(bi->base_addr) < 0)
			return(-1);
		if (read_mode_lock_word(bi->base_addr, 0x24, &word) < 0)
			return(-1);
		printf("Persistent Protection Mode lock: %04X\n", word);
		if (issue_reset_cmd(bi->base_addr) < 0)
			return(-1);
	}
	if (li->have_pln_lock_reg) {
		if (pln_special_mode_entry(bi->base_addr, 0x40) < 0)
			return(-1);
		if (do_r16(bi->base_addr, &word) < 0)
			return(-1);
		printf("PL-N Lock Register: %04X\n", word);
		if (pln_special_mode_exit(bi->base_addr) < 0)
			return(-1);
	}
	return(0);
}

/*
 * Here comes a version of the above lock-state checking code,
 * modified for use from within ppb-program-all and ppb-erase-all
 * functions for chips that don't do this work internally.
 */

static
int_lock_state_check(bi, sought_state)
	struct flash_bank_info *bi;
{
	struct amd_lock_info *li = bi->amd_lock;
	struct lock_group_desc *grp;
	uint32_t offset, part_addr;
	uint16_t word;
	unsigned ng, nb;
	int lock_state;

	offset = 0;
	for (ng = 0; ng < li->ngroups; ng++) {
		grp = li->groups + ng;
		if (grp->part_begin) {
			part_addr = bi->base_addr + offset;
			if (issue_read_id(part_addr) < 0)
				return(-1);
		}
		for (nb = 0; nb < grp->nblocks; nb++) {
			if (do_r16(bi->base_addr + offset + 4, &word) < 0)
				return(-1);
			lock_state = word & 1;
			if (lock_state != sought_state)
				break;
			offset += grp->block_size;
		}
		if (lock_state != sought_state) {
			if (issue_reset_cmd(part_addr) < 0)
				return(-1);
			return(0);
		}
		if (grp->part_end) {
			if (issue_reset_cmd(part_addr) < 0)
				return(-1);
		}
	}
	return(1);
}

/*
 * Spansion PL-J PPB write functions, referenced from lock_info structures
 * in fldevs.c device descriptions.
 */

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 amd_reset_cmd(bi);
	}
	fprintf(stderr, "PPB 0x%X programming FAILED, tried %u pulses\n",
		sector_addr, pulsecnt);
	return(-1);
}

plj_ppb_program_all(bi)
	struct flash_bank_info *bi;
{
	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;
		}
	}
	printf("Verifying PPB programming\n");
	rc = int_lock_state_check(bi, 1);
	if (rc < 0)
		return(rc);
	if (rc)
		return(0);
	fprintf(stderr, "flash error: one or more PPBs failed to program\n");
	return(-1);
}

static
plj_ppb_erase_cycle(bi)
	struct flash_bank_info *bi;
{
	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 amd_reset_cmd(bi);
	}
	fprintf(stderr, "PPB erase cycle FAILED, tried %u pulses\n", pulsecnt);
	return(-1);
}

plj_ppb_erase_all_single(bi, raw_mode)
	struct flash_bank_info *bi;
{
	unsigned pulsecnt;
	uint16_t stat;
	int rc;

	if (raw_mode)
		return plj_ppb_erase_cycle(bi);
	printf("Programming all PPBs before erase cycle\n");
	rc = plj_ppb_program_all(bi);
	if (rc < 0)
		return(-1);
	printf("Entering PPB erase and verify loop\n");
	for (pulsecnt = 0; ; ) {
		if (pulsecnt >= 1000) {
			fprintf(stderr,
		"flash error: unable to complete PPB erase after %u pulses\n",
				pulsecnt);
			return(-1);
		}
		rc = plj_ppb_write_op(bi->base_addr, 1, &stat);
		if (rc < 0)
			return(rc);
		pulsecnt++;
		if (stat & 1)
			continue;
		putchar('.');
		fflush(stdout);
		rc = amd_reset_cmd(bi);
		if (rc < 0)
			return(rc);
		rc = int_lock_state_check(bi, 0);
		if (rc < 0)
			return(rc);
		if (rc)
			break;
	}
	printf("\nPPB erase complete, total pulses: %u\n", pulsecnt);
	return(0);
}

plj_ppb_erase_all_dualbank(dummy_bi, raw_mode)
	void *dummy_bi;
{
	unsigned pulsecnt;
	uint16_t stat;
	int rc;

	if (flash_detect(0, 0) < 0)
		return(-1);
	if (flash_detect(1, 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);
	}
	if (raw_mode)
		return plj_ppb_erase_cycle(&flash_bank_info[0]);
	printf("Programming all PPBs in flash bank 0\n");
	rc = plj_ppb_program_all(&flash_bank_info[0]);
	if (rc < 0)
		return(-1);
	printf("Programming all PPBs in flash bank 1\n");
	rc = plj_ppb_program_all(&flash_bank_info[1]);
	if (rc < 0)
		return(-1);
	printf("Entering PPB erase and verify loop\n");
	for (pulsecnt = 0; ; ) {
		if (pulsecnt >= 1000) {
			fprintf(stderr,
		"flash error: unable to complete PPB erase after %u pulses\n",
				pulsecnt);
			return(-1);
		}
		rc = plj_ppb_write_op(flash_bank_info[0].base_addr, 1, &stat);
		if (rc < 0)
			return(rc);
		pulsecnt++;
		if (stat & 1)
			continue;
		putchar('.');
		fflush(stdout);
		rc = amd_reset_cmd(&flash_bank_info[0]);
		if (rc < 0)
			return(rc);
		rc = int_lock_state_check(&flash_bank_info[0], 0);
		if (rc < 0)
			return(rc);
		if (!rc)
			continue;
		rc = int_lock_state_check(&flash_bank_info[1], 0);
		if (rc < 0)
			return(rc);
		if (rc)
			break;
	}
	printf("\nPPB erase complete, total pulses: %u\n", pulsecnt);
	return(0);
}

/*
 * Spansion PL-N PPB write functions, referenced from lock_info structures
 * in fldevs.c device descriptions.
 */

static
pln_ppb_write_op(oper_addr, write1, write2, expect_stat)
	uint32_t oper_addr;
	uint16_t write1, write2, expect_stat;
{
	int rc;
	uint16_t read_stat, prev_stat;
	time_t start_time, curtime;

	rc = pln_special_mode_entry(oper_addr, 0xC0);
	if (rc < 0)
		return(rc);
	if (do_w16(oper_addr, write1)) {
bad_w16:	fprintf(stderr,
	"unexpected response to w16 in PPB command sequence - aborting\n");
		return(-1);
	}
	if (do_w16(oper_addr, write2))
		goto bad_w16;
	printf("Polling for completion status\n");
	usleep(10000);	/* make sure we don't get state before op starts */
	start_time = time(0);
	rc = do_r16(oper_addr, &read_stat);
	if (rc < 0)
		return(rc);
	for (;;) {
		prev_stat = read_stat;
		rc = do_r16(oper_addr, &read_stat);
		if (rc < 0)
			return(rc);
		if (read_stat == expect_stat && prev_stat == expect_stat)
			break;
		curtime = time(0);
		if (curtime >= start_time + 10) {
			fprintf(stderr, "operation timeout, aborting\n");
			return(-1);
		}
	}
	printf("Operation completed successfully\n");
	return pln_special_mode_exit(oper_addr);
}

pln_ppb_program_one(bi, sector_addr)
	struct flash_bank_info *bi;
	uint32_t sector_addr;
{
	printf("Issuing PPB Program command\n");
	return pln_ppb_write_op(bi->base_addr + sector_addr, 0xA0, 0, 0);
}

pln_ppb_erase_all(bi, raw_mode)
	struct flash_bank_info *bi;
{
	printf("Issuing All PPB Erase command\n");
	return pln_ppb_write_op(bi->base_addr, 0x80, 0x30, 1);
}

/*
 * Front end functions for PPB operation commands.
 */

flashcmd_ppb_program(argc, argv, bank)
	char **argv;
{
	struct flash_bank_info *bi;
	struct amd_lock_info *li;
	u_long offset_arg;
	struct sector_info *sp;
	char *strtoul_endp;

	if (argc != 3) {
inv:		fprintf(stderr, "usage: %s %s sector-offset\n",
			argv[0], argv[1]);
		return(-1);
	}
	offset_arg = strtoul(argv[2], &strtoul_endp, 16);
	if (*strtoul_endp)
		goto inv;
	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	li = bi->amd_lock;
	if (!li || !li->ppb_program_one) {
		fprintf(stderr,
			"Operation not supported for this flash chip type\n");
		return(-1);
	}
	if (offset_arg >= bi->geom->total_size) {
		fprintf(stderr,
		"error: specified offset exceeds flash bank size (0x%lx)\n",
			(u_long) bi->geom->total_size);
		return(-1);
	}
	if (get_flash_sector_table(bi) < 0)
		return(-1);
	for (sp = bi->sectors; sp->size; sp++)
		if (sp->start == offset_arg)
			break;
	if (!sp->size) {
		fprintf(stderr,
	"error: specified offset not aligned to a flash sector boundary\n");
		return(-1);
	}
	return li->ppb_program_one(bi, sp->start);
}

flashcmd_ppb_program_all(argc, argv, bank)
	char **argv;
{
	struct flash_bank_info *bi;
	struct amd_lock_info *li;

	if (argc > 2) {
		fprintf(stderr, "error: too many arguments\n");
		return(-1);
	}
	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	li = bi->amd_lock;
	if (!li || !li->ppb_program_all) {
		fprintf(stderr,
			"Operation not supported for this flash chip type\n");
		return(-1);
	}
	return li->ppb_program_all(bi);
}

flashcmd_ppb_erase_all(argc, argv, bank)
	char **argv;
{
	struct flash_bank_info *bi;
	struct amd_lock_info *li;
	int raw_mode;

	switch (argc) {
	case 2:
		raw_mode = 0;
		break;
	case 3:
		if (!strcmp(argv[2], "raw")) {
			raw_mode = 1;
			break;
		}
		/* FALL THRU */
	default:
		fprintf(stderr, "usage: %s %s [raw]\n", argv[0], argv[1]);
		return(-1);
	}
	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	li = bi->amd_lock;
	if (!li || !li->ppb_erase_all) {
		fprintf(stderr,
			"Operation not supported for this flash chip type\n");
		return(-1);
	}
	return li->ppb_erase_all(bi, raw_mode);
}