FreeCalypso > hg > freecalypso-tools
view loadtools/flamdsec.c @ 995:74024eb17e04
fc-loadtool help: improve language regarding 16 MiB flash chips
In FC project history, 16 MiB flash originally meant Pirelli DP-L10.
Then we got FCDEV3B with the same flash (our own design), but now we are
discovering more Calypso devices that used such large flash, both late
Calypso era (Sony Ericsson K2x0) as well as much earlier ones (FIC FLUID
devices.txt file with 2004 dates, Leonardo+ rev 5). Hence we need to
migrate to more generic or neutral language in associated documentation,
without giving elevated status to specific examples that drove our
early project history.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 03 Dec 2023 21:11:12 +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); }