FreeCalypso > hg > freecalypso-tools
view loadtools/flutil.c @ 416:30f6d1c32c6f
doc/Flash-boot-defect article removed (no longer relevant)
This article is no longer relevant because the issue in question
only affected one (1) defective FCDEV3B board which was not
and never will be sold.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 26 Oct 2018 07:11:08 +0000 |
parents | e7502631a0f9 |
children | 7bf0d909c87e |
line wrap: on
line source
/* * Miscellaneous utility functions for flash support */ #include <sys/types.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include "flash.h" extern struct flash_bank_info flash_bank_info[2]; extern struct flash_cmdset flash_cmdset_amd; extern struct flash_cmdset flash_cmdset_intel; static int cfi_read_byte(bi, off, ret16p) struct flash_bank_info *bi; int off; uint16_t *ret16p; { return do_r16(bi->base_addr + (off << 1), ret16p); } static int cfi_read_twobyte(bi, off, retptr) struct flash_bank_info *bi; int off; uint16_t *retptr; { uint16_t lo, hi; if (cfi_read_byte(bi, off, &lo) < 0) return(-1); lo &= 0xFF; if (cfi_read_byte(bi, off + 1, &hi) < 0) return(-1); hi &= 0xFF; *retptr = (hi << 8) | lo; return(0); } flash_get_cfi(bank) { struct flash_bank_info *bi; struct flash_geom *geom; struct flash_region_desc *reg; int nr; uint16_t rdval, cmdset_id; uint32_t size_check; bi = flash_bank_info + bank; if (bi->geom) return(0); printf("Performing CFI query\n"); if (do_w16(bi->base_addr + 0xAA, 0x98)) { fprintf(stderr, "unexpected response to w16 - aborting\n"); return(-1); } /* if do_r16() returns -1, error msg has already been printed */ if (cfi_read_byte(bi, 0x10, &rdval) < 0) return(-1); if (rdval != 'Q') { noqry: fprintf(stderr, "error: no QRY response from flash\n"); amd_reset_cmd(bi); return(-1); } if (cfi_read_byte(bi, 0x11, &rdval) < 0) return(-1); if (rdval != 'R') goto noqry; if (cfi_read_byte(bi, 0x12, &rdval) < 0) return(-1); if (rdval != 'Y') goto noqry; if (cfi_read_twobyte(bi, 0x13, &cmdset_id) < 0) return(-1); if (!bi->ops) { switch (cmdset_id) { case 2: bi->ops = &flash_cmdset_amd; break; case 3: bi->ops = &flash_cmdset_intel; break; default: fprintf(stderr, "error: command set %04X unsupported\n", cmdset_id); amd_reset_cmd(bi); return(-1); } } geom = malloc(sizeof(struct flash_geom)); if (!geom) { fprintf(stderr, "unable to malloc buffer for flash bank %d CFI geometry structure\n", bank); bi->ops->reset_cmd(bi); return(-1); } /* total device size */ if (cfi_read_byte(bi, 0x27, &rdval) < 0) { free_and_immed_out: free(geom); return(-1); } if (rdval < 20 || rdval > 24) { fprintf(stderr, "error: CFI reports unreasonable device size\n"); free_and_clean_out: free(geom); bi->ops->reset_cmd(bi); return(-1); } geom->total_size = 1 << rdval; if (geom->total_size > bi->bank_desc->align_size) { fprintf(stderr, "error: CFI device size 0x%lx exceeds configured maximum 0x%lx\n", (u_long) geom->total_size, bi->bank_desc->align_size); goto free_and_clean_out; } if (cfi_read_byte(bi, 0x2C, &rdval) < 0) goto free_and_immed_out; if (rdval < 1 || rdval > CFI_MAX_REGIONS) { fprintf(stderr, "error: CFI reports unreasonable # of erase regions\n"); goto free_and_clean_out; } geom->nregions = rdval; geom->total_sectors = 0; size_check = 0; for (nr = 0; nr < geom->nregions; nr++) { reg = geom->regions + nr; if (cfi_read_twobyte(bi, 0x2D + nr*4, &rdval) < 0) goto free_and_immed_out; if (rdval > 255) { fprintf(stderr, "error: CFI reports unreasonable # of sectors in region %d\n", nr); goto free_and_clean_out; } reg->nsectors = rdval + 1; geom->total_sectors += reg->nsectors; if (cfi_read_twobyte(bi, 0x2F + nr*4, &rdval) < 0) goto free_and_immed_out; if (rdval < 0x20 || rdval > 0x400) { fprintf(stderr, "error: CFI reports unreasonable sector size in region %d\n", nr); goto free_and_clean_out; } reg->sector_size = rdval << 8; size_check += reg->sector_size * reg->nsectors; } if (bi->ops->reset_cmd(bi) < 0) { /* error msg already printed */ free(geom); return(-1); } if (size_check != geom->total_size) { fprintf(stderr, "CFI error: added size of erase regions (%lx) != reported devive size (%lx)\n", (u_long) size_check, (u_long) geom->total_size); free(geom); return(-1); } /* all checks passed */ bi->geom = geom; printf( "CFI query successful: total size %lx, %u sectors, command set style %04X\n", (u_long) geom->total_size, geom->total_sectors, cmdset_id); return(1); } get_flash_sector_table(bank) { struct flash_bank_info *bi; struct flash_geom *geom; struct flash_region_desc *reg; struct sector_info *sp; uint32_t offset; int nr, i; bi = flash_bank_info + bank; if (bi->sectors) return(0); i = flash_get_cfi(bank); if (i < 0) return(i); geom = bi->geom; sp = (struct sector_info *) malloc(sizeof(struct sector_info) * (geom->total_sectors + 1)); if (!sp) { fprintf(stderr, "unable to malloc buffer for flash bank %d sector table\n", bank); return(-1); } bi->sectors = sp; /* now fill it */ offset = 0; for (nr = 0; nr < geom->nregions; nr++) { reg = geom->regions + nr; for (i = 0; i < reg->nsectors; i++) { sp->start = offset; sp->size = reg->sector_size; sp++; offset += reg->sector_size; } } /* sanity checks */ if (sp - bi->sectors != geom->total_sectors) { fprintf(stderr, "BUG in get_flash_sector_table(): wrong # of sectors at the end\n"); abort(); } if (offset != geom->total_size) { fprintf(stderr, "BUG in get_flash_sector_table(): wrong offset at the end\n"); abort(); } /* finish */ sp->start = 0; sp->size = 0; return(0); } flashcmd_sectors(argc, argv, bank) char **argv; { struct flash_bank_info *bi; struct sector_info *sp; if (argc > 2) { fprintf(stderr, "error: too many arguments\n"); return(-1); } if (get_flash_sector_table(bank) < 0) return(-1); bi = flash_bank_info + bank; printf("%u sectors in flash bank %d:\n", bi->geom->total_sectors, bank); printf("Offset Size\n"); for (sp = bi->sectors; sp->size; sp++) printf("%08lX %lx\n", (u_long) sp->start, (u_long) sp->size); return(0); } get_flash_sector_range(bi, useroff, userlen, startp, endp) struct flash_bank_info *bi; u_long useroff, userlen; struct sector_info **startp, **endp; { struct sector_info *sp; uint32_t remlen; for (sp = bi->sectors; sp->size; sp++) if (sp->start == useroff) break; if (!sp->size) { fprintf(stderr, "error: specified offset not aligned to a flash sector boundary\n"); return(-1); } *startp = sp; for (remlen = userlen; remlen; ) { if (remlen < sp->size) { fprintf(stderr, "error: specified length not aligned to a flash sector boundary\n"); return(-1); } remlen -= sp->size; sp++; } *endp = sp; return(0); } build_flashw_hex_string(bin, strbuf, nwords, m0src) u_char *bin; char *strbuf; int nwords, m0src; { int i; u_char *dp; char *s; for (dp = bin, s = strbuf, i = 0; i < nwords; dp += 2, s += 4, i++) { if (m0src) sprintf(s, "%02X%02X", dp[0], dp[1]); else sprintf(s, "%02X%02X", dp[1], dp[0]); } *s = '\0'; } flash_id_check(bank, repeat) { struct flash_bank_info *bi; struct flash_bank_desc *bd; struct flash_idcheck *id; int stat, fail; uint16_t rdval; unsigned cnt; bi = flash_bank_info + bank; if (bi->idcheck_done && !repeat) return(0); bd = bi->bank_desc; if (!bd->idcheck_table || !bd->idcheck_num) return(0); printf("Performing flash ID check\n"); stat = do_w16(bi->base_addr + 0xAAA, 0xAA); if (stat) { bad_w16: fprintf(stderr, "unexpected response to w16 in read ID cmd sequence - aborting\n"); return(-1); } stat = do_w16(bi->base_addr + 0x554, 0x55); if (stat) goto bad_w16; stat = do_w16(bi->base_addr + 0xAAA, 0x90); if (stat) goto bad_w16; id = bd->idcheck_table; fail = 0; for (cnt = 0; cnt < bd->idcheck_num; cnt++) { stat = do_r16(bi->base_addr + id->offset, &rdval); if (stat) return(stat); /* error msg already printed */ printf("offset %02X: %04X -- ", (int)id->offset, (int)rdval); if (rdval == id->expect_val) printf("PASS\n"); else { printf("FAIL: expected %04X\n", (int)id->expect_val); fail = 1; break; } id++; } /* reset flash to read mode */ stat = do_w16(bi->base_addr + 0xAAA, 0xF0); if (stat) { fprintf(stderr, "unexpected response to w16 when resetting flash to read mode!\n"); return(-1); } if (fail) return(-1); bi->idcheck_done = 1; return(0); }