#ifndef _SPI_FLASH_H #define _SPI_FLASH_H /* * Generic SPI NOR flash layer (Phase 3). * * Transport-agnostic: it issues SPI commands through an `xfer` callback * (one CS-framed transaction), so it works over the BSCAN proxy, the * EXTEST bit-bang, or anything else that can clock bytes in and out. * * Standard JEDEC command set (RDID/READ/WREN/RDSR/PP/SE). Chips are * matched by JEDEC ID against a small built-in database; 3- and 4-byte * addressing are both supported (parts over 16 MB use the 4-byte * command opcodes, which are stateless regardless of the part's * address-mode latch). */ #include #include /* One CS-framed SPI transaction: clock out `txlen` MOSI bytes, then * read `rxlen` MISO bytes into `rx`. Either length may be 0. Returns 0 * on success, <0 on error. */ typedef int (*spi_flash_xfer_fn)(void *ctx, const uint8_t *tx, size_t txlen, uint8_t *rx, size_t rxlen); typedef struct { const char *name; uint8_t mfg_id; /* JEDEC byte 0 (manufacturer) */ uint16_t dev_id; /* JEDEC bytes 1..2 ((b1 << 8) | b2) */ uint32_t size_bytes; /* total capacity */ uint32_t page_size; /* program granularity (typically 256) */ uint32_t sector_size; /* erase granularity (typically 4096) */ uint8_t addr_bytes; /* address width: 3 or 4 */ uint8_t read_cmd; /* READ opcode (no dummy) */ uint8_t pp_cmd; /* PAGE PROGRAM opcode */ uint8_t erase_cmd; /* sector/subsector ERASE opcode */ } spi_flash_chip; typedef struct { spi_flash_xfer_fn xfer; /* transport */ void *ctx; /* opaque, passed to xfer */ const spi_flash_chip *chip; /* matched part, set by detect */ uint8_t jedec[3]; /* raw JEDEC ID from last detect */ } spi_flash; /* Read the JEDEC ID (0x9F) and match the database. * Returns 0 if a known chip matched (sf->chip set), 1 if the ID was * read but is not in the database (sf->chip left NULL, sf->jedec set), * <0 on transport error. */ int spi_flash_detect(spi_flash *sf); /* Read `len` bytes starting at `addr` into `buf`. Returns 0 / <0. */ int spi_flash_read(spi_flash *sf, uint32_t addr, uint8_t *buf, size_t len); /* Erase the sector containing `addr` (WREN + ERASE + poll WIP). */ int spi_flash_erase_sector(spi_flash *sf, uint32_t addr); /* Program at most one page at `addr` (`len` <= page_size and must not * cross a page boundary): WREN + PP + poll WIP. */ int spi_flash_program_page(spi_flash *sf, uint32_t addr, const uint8_t *data, size_t len); /* Program `len` bytes from `addr`, splitting on page boundaries. The * target range must already be erased. */ int spi_flash_program(spi_flash *sf, uint32_t addr, const uint8_t *data, size_t len); /* Read back and compare. Returns 0 if identical, 1 on first mismatch * (and reports its offset via *mismatch_off if non-NULL), <0 on error. */ int spi_flash_verify(spi_flash *sf, uint32_t addr, const uint8_t *data, size_t len, uint32_t *mismatch_off); /* Built-in database access. */ int spi_flash_chip_count(void); const spi_flash_chip *spi_flash_chip_by_index(int i); #endif