Hello,
This set of patches should be applied on top of the previous patches (https://www.flashrom.org/pipermail/flashrom/2016-July/014717.html).
Continuing with the major points from the previous mail (linked above), this patch should bring support for new infrastructure to quite a few more chips. OTP models for Eon, GigaDevice and Winbond are supported now. AMIC, Macronix and Spansion OTP models are scheduled for the next revision. I admit that the CLI may not be worded in the most optimum way, but that was a conscious decision so that it could be sorted out while review.
Thank you for your time. I am looking forward to your feedback and having a discussion.
Hatim Kanchwala (4): WIP: Add support for status register and access protection infrastructure to more existing chips OTP/Security Register infrastructure WIP: Add support for OTP/Security Register infrastructure to existing chips Integrate OTP infrastructure and add CLI
Makefile | 5 +- chipdrivers.h | 23 +++ cli_classic.c | 263 ++++++++++++++++++++++++++++++++-- cli_common.c | 54 +++---- flash.h | 25 +++- flashchips.c | 296 ++++++++++++++++++++++++-------------- flashrom.8.tmpl | 52 ++++++- otp.c | 381 +++++++++++++++++++++++++++++++++++++++++++++++++ otp.h | 39 +++++ otp_layouts.c | 229 +++++++++++++++++++++++++++++ spi.h | 20 +++ spi25.c | 73 ++++++++++ spi25_statusreg.h | 6 + statusreg_layouts.c | 58 +++++++- writeprotect.h | 13 +- writeprotect_layouts.c | 269 +++++++++++++++++++++++++++++++++- 16 files changed, 1645 insertions(+), 161 deletions(-) create mode 100644 otp.c create mode 100644 otp.h create mode 100644 otp_layouts.c
- Infrastructure support in flashchips.c added for (28 in total) - - Eon(13) : EN25QH128, EN25Q128, EN25QH64, EN25Q64, EN25QH32, EN25QH16, EN25Q16, EN25Q32(A/B), EN25Q80(A), EN25Q40 - GigaDevice(13) : GD25Q80(B), GD25Q32(B), GD25Q64(B), GD25Q128B, GD25Q128C, GD25VQ21B, GD25VQ40C, GD25VQ41B, GD25VQ80C, GD25VQ16C - Winbond(2) : W25Q40BL, W25Q64FV
Signed-off-by: Hatim Kanchwala hatim@hatimak.me --- flashchips.c | 187 +++++++++++++++++++++++----------- spi25_statusreg.h | 6 ++ statusreg_layouts.c | 58 ++++++++++- writeprotect.h | 13 ++- writeprotect_layouts.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 471 insertions(+), 62 deletions(-)
diff --git a/flashchips.c b/flashchips.c index 725a9e2..43545a4 100644 --- a/flashchips.c +++ b/flashchips.c @@ -1161,27 +1161,27 @@ const struct flashchip flashchips[] = { .block_erase = spi_block_erase_20, }, { .eraseblocks = { { 64 * 1024, 16 } }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { { 1024 * 1024, 1 } }, .block_erase = spi_block_erase_c7, } }, .status_register = &a25l080_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, - .wp = &gd25_a25l080_q16_32a_wp, + .wp = &gd_w_wp, },
{ .vendor = "AMIC", .name = "A25L016", .bustype = BUS_SPI, .manufacture_id = AMIC_ID_NOPREFIX, .model_id = AMIC_A25L016, .total_size = 2048, .page_size = 256, .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, @@ -1271,27 +1271,27 @@ const struct flashchip flashchips[] = { .block_erase = spi_block_erase_d8, }, { .eraseblocks = { { 2048 * 1024, 1 } }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { { 2048 * 1024, 1 } }, .block_erase = spi_block_erase_c7, } }, .status_register = &a25lq16_32a_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, - .wp = &gd25_a25l080_q16_32a_wp, + .wp = &gd_w_wp, },
{ .vendor = "AMIC", .name = "A25LQ032", .bustype = BUS_SPI, .manufacture_id = AMIC_ID_NOPREFIX, .model_id = AMIC_A25LQ032, .total_size = 4096, .page_size = 256, /* A25LQ32A supports SFDP */ /* OTP: 64B total; read 0x4B, 0x48; write 0x42 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, @@ -4528,31 +4528,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {4 * 1024, 128} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 8} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &en25q80a_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25q40_wp, },
{ .vendor = "Eon", .name = "EN25Q80(A)", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q80, .total_size = 1024, .page_size = 256, /* OTP: 256B total; enter 0x3A */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_UNTESTED, @@ -4564,31 +4564,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {4 * 1024, 256} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 16} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &en25q80a_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25q80a_wp, },
{ /* Note: EN25D16 is an evil twin which shares the model ID but has different write protection capabilities */ .vendor = "Eon", .name = "EN25Q16", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q16, .total_size = 2048, .page_size = 256, /* OTP: D16 512B/Q16 128B total; enter 0x3A */ @@ -4606,67 +4606,103 @@ const struct flashchip flashchips[] = { .block_erase = spi_block_erase_d8, }, { /* not supported by Q16 version */ .eraseblocks = { {64 * 1024, 32} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &en25q16_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25q16_wp, },
{ .vendor = "Eon", - .name = "EN25Q32(A/B)", + .name = "EN25Q32", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q32, .total_size = 4096, .page_size = 256, /* OTP: 512B total; enter 0x3A */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 1024} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 64} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &en25q16_sr, + .write = spi_chip_write_256, + .read = spi_chip_read, + .voltage = {2700, 3600}, + .wp = &en25q32_wp, + }, + + { + .vendor = "Eon", + .name = "EN25Q32A/EN25Q32B", + .bustype = BUS_SPI, + .manufacture_id = EON_ID_NOPREFIX, + .model_id = EON_EN25Q32, + .total_size = 4096, + .page_size = 256, + /* OTP: 512B total; enter 0x3A */ + .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .tested = TEST_OK_PREW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 1024} }, + .block_erase = spi_block_erase_20, + }, { + .eraseblocks = { {64 * 1024, 64} }, + .block_erase = spi_block_erase_d8, + }, { + .eraseblocks = { {4 * 1024 * 1024, 1} }, + .block_erase = spi_block_erase_60, + }, { + .eraseblocks = { {4 * 1024 * 1024, 1} }, + .block_erase = spi_block_erase_c7, + } + }, + .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25q32ab_wp, },
{ .vendor = "Eon", .name = "EN25Q64", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q64, .total_size = 8192, .page_size = 256, /* OTP: 512B total; enter 0x3A */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, @@ -4678,31 +4714,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {4 * 1024, 2048} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 128} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25q64_wp, },
{ .vendor = "Eon", .name = "EN25Q128", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q128, .total_size = 16384, .page_size = 256, /* OTP: 512B total; enter 0x3A */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, @@ -4714,30 +4750,30 @@ const struct flashchip flashchips[] = { .eraseblocks = { {4 * 1024, 4096} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 256} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, + .wp = &en25q128_wp, },
{ .vendor = "Eon", .name = "EN25QH16", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25QH16, .total_size = 2048, .page_size = 256, /* supports SFDP */ /* OTP: 512B total; enter 0x3A */ /* QPI enable 0x38, disable 0xFF */ @@ -4751,31 +4787,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {4 * 1024, 512} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 32} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1024 * 2048, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 2048, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */ - .unlock = spi_disable_blockprotect_bp3_srwd, + .status_register = &en25qh16_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25qh16_wp, },
{ .vendor = "Eon", .name = "EN25QH32", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25QH32, .total_size = 4096, .page_size = 256, /* supports SFDP */ /* OTP: 512B total; enter 0x3A */ /* QPI enable 0x38, disable 0xFF */ @@ -4789,31 +4825,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {4 * 1024, 1024} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 64} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1024 * 4096, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 4096, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */ - .unlock = spi_disable_blockprotect_bp3_srwd, + .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25qh32_wp, },
{ .vendor = "Eon", .name = "EN25QH64", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25QH64, .total_size = 8192, .page_size = 256, /* supports SFDP */ /* OTP: 512B total; enter 0x3A */ /* QPI enable 0x38, disable 0xFF */ @@ -4826,31 +4862,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {4 * 1024, 2048} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 128} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { { 8192 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { { 8192 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */ - .unlock = spi_disable_blockprotect_bp3_srwd, + .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25qh64_wp, },
{ .vendor = "Eon", .name = "EN25QH128", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25QH128, .total_size = 16384, .page_size = 256, /* supports SFDP */ /* OTP: 512B total; enter 0x3A */ /* QPI enable 0x38, disable 0xFF */ @@ -4863,31 +4899,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {4 * 1024, 4096} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 256} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { { 16384 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { { 16384 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */ - .unlock = spi_disable_blockprotect_bp3_srwd, + .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &en25qh128_wp, },
{ .vendor = "Eon", .name = "EN25S10", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25S10, .total_size = 128, .page_size = 256, /* OTP: 256B total; enter 0x3A */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_UNTESTED, @@ -5706,27 +5742,27 @@ const struct flashchip flashchips[] = { .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25lq_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {1695, 1950}, - .wp = &gd25_a25l080_q16_32a_wp, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25LQ80", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25LQ80, .total_size = 1024, .page_size = 256, /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_UNTESTED, @@ -5745,27 +5781,27 @@ const struct flashchip flashchips[] = { .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25lq_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {1695, 1950}, - .wp = &gd25_a25l080_q16_32a_wp, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25LQ16", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25LQ16, .total_size = 2048, .page_size = 256, /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_UNTESTED, @@ -5784,27 +5820,27 @@ const struct flashchip flashchips[] = { .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25lq_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {1695, 1950}, - .wp = &gd25_a25l080_q16_32a_wp, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25LQ32", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25LQ32, .total_size = 4096, .page_size = 256, /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, @@ -6053,61 +6089,99 @@ const struct flashchip flashchips[] = { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .printlock = spi_prettyprint_status_register_bp4_srwd, .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, },
{ .vendor = "GigaDevice", - .name = "GD25Q80(B)", + .name = "GD25Q80", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q80, .total_size = 1024, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 (B version only) */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 256} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 32} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 16} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25q10_20_40_80_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, + .wp = &gd_w_wp, + }, + + { + .vendor = "GigaDevice", + .name = "GD25Q80B", + .bustype = BUS_SPI, + .manufacture_id = GIGADEVICE_ID, + .model_id = GIGADEVICE_GD25Q80, + .total_size = 1024, + .page_size = 256, + /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ + .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .tested = TEST_OK_PREW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 256} }, + .block_erase = spi_block_erase_20, + }, { + .eraseblocks = { {32 * 1024, 32} }, + .block_erase = spi_block_erase_52, + }, { + .eraseblocks = { {64 * 1024, 16} }, + .block_erase = spi_block_erase_d8, + }, { + .eraseblocks = { {1024 * 1024, 1} }, + .block_erase = spi_block_erase_60, + }, { + .eraseblocks = { {1024 * 1024, 1} }, + .block_erase = spi_block_erase_c7, + } + }, + .status_register = &gd25q80b_128_sr, + .write = spi_chip_write_256, + .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ + .voltage = {2700, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25Q16", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q16, .total_size = 2048, .page_size = 256, .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, @@ -6125,27 +6199,27 @@ const struct flashchip flashchips[] = { .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25q10_20_40_80_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, - .wp = &gd25_a25l080_q16_32a_wp, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25Q16B", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q16, .total_size = 2048, .page_size = 256, /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 (B version only) */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, @@ -6164,27 +6238,27 @@ const struct flashchip flashchips[] = { .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25q16_32_64b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, - .wp = &gd25_a25l080_q16_32a_wp, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25Q32(B)", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q32, .total_size = 4096, .page_size = 256, /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, @@ -6199,31 +6273,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {32 * 1024, 128} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 64} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25q16_32_64b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25Q64(B)", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q64, .total_size = 8192, .page_size = 256, /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, @@ -6238,31 +6312,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {32 * 1024, 256} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 128} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25q16_32_64b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25Q128B", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q128, .total_size = 16384, .page_size = 256, /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_UNTESTED, @@ -6277,31 +6351,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {32 * 1024, 512} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 256} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25q16_32_64b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25Q128C", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q128, .total_size = 16384, .page_size = 256, /* OTP: 1536B total; read 0x48; write 0x42, erase 0x44 */ /* QPI: enable 0x38, disable 0xFF */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, @@ -6317,32 +6391,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {32 * 1024, 512} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 256} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - /* TODO: 2nd status reg (read 0x35, write 0x31) and 3rd status reg (read 0x15, write 0x11) */ - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, + .status_register = &gd25q128c_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25T80", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25T80, .total_size = 1024, .page_size = 256, /* OTP: 256B total; enter 0x3A */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_UNTESTED, @@ -6395,31 +6468,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { { 32 * 1024, 8} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 4} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {256 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {256 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25vq21_41b_q21_q41b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25VQ40C", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ41B, .total_size = 512, .page_size = 256, /* Supports SFDP */ /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, @@ -6435,31 +6508,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { { 32 * 1024, 16} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 8} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25vq16_80c_q16_40c_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25VQ41B", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ41B, .total_size = 512, .page_size = 256, /* OTP: 1536B total; read 0x48, write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, .tested = TEST_OK_PREW, @@ -6474,31 +6547,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { { 32 * 1024, 16} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 8} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25vq21_41b_q21_q41b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25VQ80C", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ80C, .total_size = 1024, .page_size = 256, /* Supports SFDP */ /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, @@ -6514,31 +6587,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { { 32 * 1024, 32} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 16} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25vq16_80c_q16_40c_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25VQ16C", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ16C, .total_size = 2 * 1024, .page_size = 256, /* Supports SFDP */ /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, @@ -6554,31 +6627,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { { 32 * 1024, 64} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 32} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_bp4_srwd, - .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */ + .status_register = &gd25vq16_80c_q16_40c_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "Hyundai", .name = "HY29F002T", .bustype = BUS_PARALLEL, .manufacture_id = HYUNDAI_ID, .model_id = HYUNDAI_HY29F002T, .total_size = 256, .page_size = 256 * 1024, .feature_bits = FEATURE_EITHER_RESET, /* Some revisions may need FEATURE_ADDR_2AA */ .tested = TEST_OK_PRE, .probe = probe_jedec, @@ -14489,31 +14562,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {32 * 1024, 16} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 8} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &w25q40bl_64fv_sr, .write = spi_chip_write_256, /* Multi I/O supported */ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "Winbond", .name = "W25Q80.V", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25Q80_V, .total_size = 1024, .page_size = 256, /* supports SFDP */ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, @@ -14649,31 +14722,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {32 * 1024, 256} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 128} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &w25q40bl_64fv_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, + .wp = &gd_w_wp, },
{ .vendor = "Winbond", .name = "W25Q128.V", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25Q128_V, .total_size = 16384, .page_size = 256, /* supports SFDP */ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, @@ -14926,31 +14999,31 @@ const struct flashchip flashchips[] = { .eraseblocks = { {32 * 1024, 256} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 128} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, - .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ - .unlock = spi_disable_blockprotect, + .status_register = &w25q40bl_64fv_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */ + .wp = &gd_w_wp, },
{ .vendor = "Winbond", .name = "W25X10", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25X10, .total_size = 128, .page_size = 256, .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, diff --git a/spi25_statusreg.h b/spi25_statusreg.h index 8bfffa0..f93a084 100644 --- a/spi25_statusreg.h +++ b/spi25_statusreg.h @@ -70,26 +70,32 @@ enum wp_mode { WP_MODE_POWER_CYCLE,
/* Status register is permanently protected and cannot be written to. */ WP_MODE_PERMANENT, };
/* Describes corresponding bits from enum status_register_bit */ extern char *statreg_bit_desc[][2];
/* === Single status register === */ /* === AMIC === */ extern struct status_register a25l080_sr;
+/* === Eon === */ +extern struct status_register en25qh128_sr; +extern struct status_register en25qh16_sr; +extern struct status_register en25q16_sr; +extern struct status_register en25q80a_sr; + /* === Macronix === */ extern struct status_register mx25l64xe_sr; extern struct status_register mx25lx5d_sr; extern struct status_register mx25lx65e_sr;
/* === Double status registers === */ /* === AMIC === */ extern struct status_register a25lq16_32a_sr; extern struct status_register a25l032_sr;
/* === GigaDevice === */ extern struct status_register gd25lq_sr; extern struct status_register gd25q16_32_64b_sr; diff --git a/statusreg_layouts.c b/statusreg_layouts.c index f4275af..e83b1b4 100644 --- a/statusreg_layouts.c +++ b/statusreg_layouts.c @@ -38,26 +38,80 @@ /* === AMIC === */ /* A25L080 */ struct status_register a25l080_sr = { .layout = { { WIP, WEL, BP0, BP1, BP2, RESV, RESV, SRP0 }, }, .read = &spi_read_status_register_generic, .write = &spi_write_status_register_generic, .print = &spi_prettyprint_status_register_generic, .print_wp_mode = &spi_prettyprint_status_register_wp_generic, .get_wp_mode = &get_wp_mode_generic, .set_wp_mode = &set_wp_mode_generic, }; + +/* === Eon === */ +/* EN25QH128, EN25Q128, EN25QH64, EN25Q64, EN25QH32 */ +struct status_register en25qh128_sr = { + .layout = { + { WIP, WEL, BP0, BP1, BP2, BP3, WP, SRP0 }, + }, + .read = &spi_read_status_register_generic, + .write = &spi_write_status_register_generic, + .print = &spi_prettyprint_status_register_generic, + .print_wp_mode = &spi_prettyprint_status_register_wp_generic, + .get_wp_mode = &get_wp_mode_generic, + .set_wp_mode = &set_wp_mode_generic, +}; + +/* EN25QH16 */ +struct status_register en25qh16_sr = { + .layout = { + { WIP, WEL, BP0, BP1, BP2, BP3, QE, SRP0 }, + }, + .read = &spi_read_status_register_generic, + .write = &spi_write_status_register_generic, + .print = &spi_prettyprint_status_register_generic, + .print_wp_mode = &spi_prettyprint_status_register_wp_generic, + .get_wp_mode = &get_wp_mode_generic, + .set_wp_mode = &set_wp_mode_generic, +}; + +/* EN25Q16, EN25Q32 */ +struct status_register en25q16_sr = { + .layout = { + { WIP, WEL, BP0, BP1, BP2, RESV, RESV, SRP0 }, + }, + .read = &spi_read_status_register_generic, + .write = &spi_write_status_register_generic, + .print = &spi_prettyprint_status_register_generic, + .print_wp_mode = &spi_prettyprint_status_register_wp_generic, + .get_wp_mode = &get_wp_mode_generic, + .set_wp_mode = &set_wp_mode_generic, +}; + +/* EN25Q80A, EN25Q40 */ +struct status_register en25q80a_sr = { + .layout = { + { WIP, WEL, BP0, BP1, BP2, RESV, WP, SRP0 }, + }, + .read = &spi_read_status_register_generic, + .write = &spi_write_status_register_generic, + .print = &spi_prettyprint_status_register_generic, + .print_wp_mode = &spi_prettyprint_status_register_wp_generic, + .get_wp_mode = &get_wp_mode_generic, + .set_wp_mode = &set_wp_mode_generic, +}; + /* === Macronix === */ /* MX25L6408E, MX25L6406E */ struct status_register mx25l64xe_sr = { .layout = { { WIP, WEL, BP0, BP1, BP2, BP3, RESV, SRP0 }, }, .read = &spi_read_status_register_generic, .write = &spi_write_status_register_generic, .print = &spi_prettyprint_status_register_generic, .print_wp_mode = &spi_prettyprint_status_register_wp_generic, .get_wp_mode = &get_wp_mode_generic, .set_wp_mode = &set_wp_mode_generic, }; @@ -132,27 +186,27 @@ struct status_register a25l032_sr = { struct status_register gd25lq_sr = { .layout = { { WIP, WEL, BP0, BP1, BP2, BP3, BP4, SRP0 }, { SRP1, QE, SUS2, LB1, LB2, LB3, CMP, SUS1 }, }, .read = &spi_read_status_register_generic, .write = &spi_write_status_register_generic, .print = &spi_prettyprint_status_register_generic, .print_wp_mode = &spi_prettyprint_status_register_wp_generic, .get_wp_mode = &get_wp_mode_generic, .set_wp_mode = &set_wp_mode_generic, };
-/* GD25Q16B, GD25Q32B, GD25Q64B */ +/* GD25Q16B, GD25Q32B, GD25Q64B, GD25Q128B */ struct status_register gd25q16_32_64b_sr = { .layout = { { WIP, WEL, BP0, BP1, BP2, BP3, BP4, SRP0 }, { RESV, QE, LB1, RESV, RESV, RESV, CMP, SUS }, }, .read = &spi_read_status_register_generic, .write = &spi_write_status_register_generic, .print = &spi_prettyprint_status_register_generic, .print_wp_mode = &spi_prettyprint_status_register_wp_generic, .get_wp_mode = &get_wp_mode_generic, .set_wp_mode = &set_wp_mode_generic, };
@@ -160,27 +214,27 @@ struct status_register gd25q16_32_64b_sr = { struct status_register gd25q10_20_40_80_sr = { .layout = { { WIP, WEL, BP0, BP1, BP2, BP3, BP4, SRP0 }, { SRP1, QE, RESV, RESV, RESV, RESV, RESV, RESV }, }, .read = &spi_read_status_register_generic, .write = &spi_write_status_register_generic, .print = &spi_prettyprint_status_register_generic, .print_wp_mode = &spi_prettyprint_status_register_wp_generic, .get_wp_mode = &get_wp_mode_generic, .set_wp_mode = &set_wp_mode_generic, };
-/* GD25VQ16C, GD25VQ80C, GD25Q16C, GD25Q40C */ +/* GD25VQ16C, GD25VQ40C, GD25VQ80C, GD25Q16C, GD25Q40C */ struct status_register gd25vq16_80c_q16_40c_sr = { .layout = { { WIP, WEL, BP0, BP1, BP2, BP3, BP4, SRP0 }, { SRP1, QE, LB1, RESV, RESV, HPF, CMP, SUS }, }, .read = &spi_read_status_register_generic, .write = &spi_write_status_register_generic, .print = &spi_prettyprint_status_register_generic, .print_wp_mode = &spi_prettyprint_status_register_wp_generic, .get_wp_mode = &get_wp_mode_generic, .set_wp_mode = &set_wp_mode_generic, };
diff --git a/writeprotect.h b/writeprotect.h index 41dbc7c..87a0f63 100644 --- a/writeprotect.h +++ b/writeprotect.h @@ -21,20 +21,31 @@ #ifndef __WRITEPROTECT_H__ #define __WRITEPROTECT_H__ 1
#include "flash.h"
#define LEN_RANGES 64
struct range { uint32_t start; uint32_t len; /* in kB */ };
extern struct wp a25l032_32a_wp; -extern struct wp gd25_a25l080_q16_32a_wp; +extern struct wp gd_w_wp; +extern struct wp en25qh128_wp; +extern struct wp en25q128_wp; +extern struct wp en25qh64_wp; +extern struct wp en25q64_wp; +extern struct wp en25qh32_wp; +extern struct wp en25q32ab_wp; +extern struct wp en25qh16_wp; +extern struct wp en25q16_wp; +extern struct wp en25q32_wp; +extern struct wp en25q80a_wp; +extern struct wp en25q40_wp; extern struct wp mx25l16xd_wp; extern struct wp mx25l6405d_wp; extern struct wp mx25lx5d_wp; extern struct wp mx25lx65e_wp;
#endif /* !__WRITEPROTECT_H__ */ diff --git a/writeprotect_layouts.c b/writeprotect_layouts.c index f5ac2ec..67e59c2 100644 --- a/writeprotect_layouts.c +++ b/writeprotect_layouts.c @@ -32,35 +32,300 @@ * int (*print_table) (struct flashctx *flash); * }; */
/* A25LQ032, A25LQ32A */ struct wp a25l032_32a_wp = { .range_table = &a25l032_range_table, .bp_bitmask = &bp_bitmask_generic, .print_table = &print_table_generic, .set_range = &set_range_generic, .disable = &disable_generic, };
-/* A25L080, A25LQ16, GD25LQ40, GD25LQ80, GD25LQ16, GD25Q16, GD25Q16B */ -struct wp gd25_a25l080_q16_32a_wp = { +/* A25L080, A25LQ16, GD25LQ40, GD25LQ80, GD25LQ16, GD25Q16, GD25Q16B GD25Q32(B), + * GD25Q64(B), GD25Q128B, GD25Q128C, GD25VQ16C, GD25VQ21B, GD25VQ40C, GD25VQ41B, + * GD25VQ80C, W25Q40BL, W25Q64FV */ +struct wp gd_w_wp = { .range_table = &sec_block_range_pattern, .bp_bitmask = &bp_bitmask_generic, .print_table = &print_table_generic, .set_range = &set_range_generic, .disable = &disable_generic, };
+/* EN25QH128 */ +struct wp en25qh128_wp = { + .ranges = (struct range[]){ + /* BP3 effectively acts as TB bit, + * BP[0..2] function normally. */ + { 0x000000, 0 }, + { 0xff0000, 64 }, + { 0xfe0000, 128 }, + { 0xfc0000, 256 }, + { 0xf80000, 512 }, + { 0xf00000, 1024 }, + { 0xe00000, 2048 }, + { 0x000000, 16384 }, + + { 0x000000, 0 }, + { 0x000000, 64 }, + { 0x000000, 128 }, + { 0x000000, 256 }, + { 0x000000, 512 }, + { 0x000000, 1024 }, + { 0x000000, 2048 }, + { 0x000000, 16384 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25Q128 */ +struct wp en25q128_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x000000, 16320 }, + { 0x000000, 16256 }, + { 0x000000, 16128 }, + { 0x000000, 15872 }, + { 0x000000, 15360 }, + { 0x000000, 14336 }, + { 0x000000, 16384 }, + + { 0x000000, 0 }, + { 0x010000, 16320 }, + { 0x020000, 16256 }, + { 0x040000, 16128 }, + { 0x080000, 15872 }, + { 0x100000, 15360 }, + { 0x200000, 14336 }, + { 0x000000, 16384 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25QH64 */ +struct wp en25qh64_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x7f0000, 64 }, + { 0x7e0000, 128 }, + { 0x7c0000, 256 }, + { 0x780000, 512 }, + { 0x700000, 1024 }, + { 0x600000, 2048 }, + { 0x000000, 8192 }, + + { 0x000000, 0 }, + { 0x000000, 64 }, + { 0x000000, 128 }, + { 0x000000, 256 }, + { 0x000000, 512 }, + { 0x000000, 1024 }, + { 0x000000, 2048 }, + { 0x000000, 8192 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25Q64 */ +struct wp en25q64_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x000000, 8128 }, + { 0x000000, 8064 }, + { 0x000000, 7936 }, + { 0x000000, 7680 }, + { 0x000000, 7168 }, + { 0x000000, 6144 }, + { 0x000000, 8192 }, + + { 0x000000, 0 }, + { 0x010000, 8128 }, + { 0x020000, 8064 }, + { 0x040000, 7936 }, + { 0x080000, 7680 }, + { 0x100000, 7168 }, + { 0x200000, 6144 }, + { 0x000000, 8192 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25QH32 */ +struct wp en25qh32_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x3f0000, 64 }, + { 0x3e0000, 128 }, + { 0x3c0000, 256 }, + { 0x380000, 512 }, + { 0x300000, 1024 }, + { 0x200000, 2048 }, + { 0x000000, 4096 }, + + { 0x000000, 0 }, + { 0x000000, 64 }, + { 0x000000, 128 }, + { 0x000000, 256 }, + { 0x000000, 512 }, + { 0x000000, 1024 }, + { 0x000000, 2048 }, + { 0x000000, 4096 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25Q32A/EN25Q32B */ +struct wp en25q32ab_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x3f0000, 4032 }, + { 0x3e0000, 3968 }, + { 0x3c0000, 3840 }, + { 0x380000, 3584 }, + { 0x300000, 3072 }, + { 0x200000, 2048 }, + { 0x000000, 4096 }, + + { 0x000000, 0 }, + { 0x010000, 4032 }, + { 0x020000, 3968 }, + { 0x040000, 3840 }, + { 0x080000, 3584 }, + { 0x100000, 3072 }, + { 0x200000, 2048 }, + { 0x000000, 4096 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25QH16 */ +struct wp en25qh16_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x1f0000, 64 }, + { 0x1e0000, 128 }, + { 0x1c0000, 256 }, + { 0x180000, 512 }, + { 0x100000, 1024 }, + { 0x000000, 2048 }, + { 0x000000, 2048 }, + + { 0x000000, 0 }, + { 0x000000, 64 }, + { 0x000000, 128 }, + { 0x000000, 256 }, + { 0x000000, 512 }, + { 0x000000, 1024 }, + { 0x000000, 2048 }, + { 0x000000, 2048 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25Q16 */ +struct wp en25q16_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x000000, 1984 }, + { 0x000000, 1920 }, + { 0x000000, 1792 }, + { 0x000000, 1536 }, + { 0x000000, 1024 }, + { 0x000000, 2048 }, + { 0x000000, 2048 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25Q32 */ +struct wp en25q32_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x3f0000, 64 }, + { 0x3e0000, 128 }, + { 0x3c0000, 256 }, + { 0x380000, 512 }, + { 0x300000, 1024 }, + { 0x200000, 2048 }, + { 0x000000, 4096 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25Q80A */ +struct wp en25q80a_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x000000, 1016 }, + { 0x000000, 1008 }, + { 0x000000, 992 }, + { 0x000000, 960 }, + { 0x000000, 896 }, + { 0x000000, 768 }, + { 0x000000, 1024 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + +/* EN25Q40 */ +struct wp en25q40_wp = { + .ranges = (struct range[]){ + { 0x000000, 0 }, + { 0x000000, 504 }, + { 0x000000, 496 }, + { 0x000000, 480 }, + { 0x000000, 448 }, + { 0x000000, 384 }, + { 0x000000, 256 }, + { 0x000000, 512 }, + }, + .bp_bitmask = &bp_bitmask_generic, + .print_table = &print_table_generic, + .set_range = &set_range_generic, + .disable = &disable_generic, +}; + /* MX25L1605D, MX25L1608D, MX25L1673E */ struct wp mx25l16xd_wp = { .ranges = (struct range[]){ /* BP3 effectively acts as CMP bit, * BP[0..2] function normally. */ { 0x000000, 0 }, { 0x1f0000, 64 }, { 0x1e0000, 128 }, { 0x1c0000, 256 }, { 0x180000, 512 }, { 0x100000, 1024 }, { 0x000000, 2048 }, { 0x000000, 2048 },
- Read, write or erase OTP memory - Two distinct models (not exhaustive) - - Security sector (Eon) - Separate memory array accessible as a normal sector while in OTP mode, WRSR locks it permanently - Security Registers (GigaDevice and Winbond) - Separate memory location with dedicated opcodes for read, program and erase; OTP status controlled by Lock Bits (LB1, LB2, ...) in status register(s) - struct region represents a quantum of OTP memory - start address, size and modifier bit in status register (generic design applies to all aforementioned models) - struct flashchip contains pointer to struct otp, which has members to represent OTP regions and function pointers to fetch and print status, read, write, erase and lock OTP regions
Signed-off-by: Hatim Kanchwala hatim@hatimak.me --- Makefile | 2 +- chipdrivers.h | 23 ++++ flash.h | 23 ++++ otp.c | 381 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ otp.h | 28 +++++ spi.h | 20 +++ spi25.c | 73 +++++++++++ 7 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 otp.c create mode 100644 otp.h
diff --git a/Makefile b/Makefile index c274e79..e97f641 100644 --- a/Makefile +++ b/Makefile @@ -503,27 +503,27 @@ override CONFIG_SATAMV = no endif ifeq ($(CONFIG_IT8212), yes) UNSUPPORTED_FEATURES += CONFIG_IT8212=yes else override CONFIG_IT8212 = no endif endif
############################################################################### # Flash chip drivers and bus support infrastructure.
CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \ sst28sf040.o 82802ab.o sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o \ - spi25.o spi25_statusreg.o writeprotect.o statusreg_layouts.o \ + spi25.o spi25_statusreg.o otp.o writeprotect.o statusreg_layouts.o \ writeprotect_layouts.o opaque.o sfdp.o en29lv640b.o at45db.o
############################################################################### # Library code.
LIB_OBJS = layout.o flashrom.o udelay.o programmer.o helpers.o
############################################################################### # Frontend related stuff.
CLI_OBJS = cli_classic.o cli_output.o cli_common.o print.o
# Set the flashrom version string from the highest revision number of the checked out flashrom files. diff --git a/chipdrivers.h b/chipdrivers.h index d3b27cc..ab18cc5 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -16,26 +16,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * * Header file for flash chip drivers. Included from flash.h. * As a general rule, every function listed here should take a pointer to * struct flashctx as first parameter. */
#ifndef __CHIPDRIVERS_H__ #define __CHIPDRIVERS_H__ 1
#include "flash.h" /* for chipaddr and flashctx */ +#include "otp.h" /* For enum otp_region */ #include "spi25_statusreg.h" /* For enum status_register_num */ #include "writeprotect.h"
/* spi.c */ int spi_aai_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len); int spi_chip_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len); int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, int unsigned len);
/* spi25.c */ int probe_spi_rdid(struct flashctx *flash); int probe_spi_rdid4(struct flashctx *flash); int probe_spi_rems(struct flashctx *flash); int probe_spi_res1(struct flashctx *flash); @@ -52,26 +53,30 @@ int spi_block_erase_62(struct flashctx *flash, unsigned int addr, unsigned int b int spi_block_erase_81(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_c4(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_c7(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d7(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d8(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_db(struct flashctx *flash, unsigned int addr, unsigned int blocklen); erasefunc_t *spi_get_erasefn_from_opcode(uint8_t opcode); int spi_chip_write_1(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len); int spi_byte_program(struct flashctx *flash, unsigned int addr, uint8_t databyte); int spi_nbyte_program(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); int spi_nbyte_read(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize); int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize); +int spi_enter_otp_mode(struct flashctx *flash); +int spi_sec_reg_read(struct flashctx *flash, uint8_t *buf, uint32_t start_addr, uint32_t len); +int spi_sec_reg_prog(struct flashctx *flash, uint8_t const *buf, uint32_t start_addr, uint32_t len); +int spi_sec_reg_erase(struct flashctx *flash, uint32_t addr);
/* spi25_statusreg.c */ uint8_t spi_read_status_register(struct flashctx *flash); uint8_t spi_read_status_register_generic(struct flashctx *flash, enum status_register_num SRn); int spi_write_status_register(struct flashctx *flash, int status); int spi_write_status_register_generic(struct flashctx *flash, enum status_register_num SRn, uint8_t status); enum status_register_num top_status_register(struct flashctx *flash); char pos_bit(struct flashctx *flash, enum status_register_bit bit); enum wp_mode get_wp_mode_generic(struct flashctx *flash); int set_wp_mode_generic(struct flashctx *flash, enum wp_mode wp_mode); int spi_prettyprint_status_register_generic(struct flashctx *flash, enum status_register_num SRn); int spi_prettyprint_status_register_wp_generic(struct flashctx *flash); void spi_prettyprint_status_register_bit(uint8_t status, int bit); @@ -104,26 +109,44 @@ int spi_disable_blockprotect_at25f(struct flashctx *flash); int spi_disable_blockprotect_at25f512a(struct flashctx *flash); int spi_disable_blockprotect_at25f512b(struct flashctx *flash); int spi_disable_blockprotect_at25fs010(struct flashctx *flash); int spi_disable_blockprotect_at25fs040(struct flashctx *flash); int spi_prettyprint_status_register_en25s_wp(struct flashctx *flash); int spi_prettyprint_status_register_n25q(struct flashctx *flash); int spi_disable_blockprotect_n25q(struct flashctx *flash); int spi_prettyprint_status_register_bp2_ep_srwd(struct flashctx *flash); int spi_disable_blockprotect_bp2_ep_srwd(struct flashctx *flash); int spi_prettyprint_status_register_sst25(struct flashctx *flash); int spi_prettyprint_status_register_sst25vf016(struct flashctx *flash); int spi_prettyprint_status_register_sst25vf040b(struct flashctx *flash);
+/* otp.c */ +int eon_status_generic(struct flashctx *flash, enum otp_region otp_region); +int eon_print_status_generic(struct flashctx *flash); +int eon_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len); +int eon_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len); +int eon_erase_generic(struct flashctx *flash, enum otp_region otp_region); +int eon_lock_generic(struct flashctx *flash, enum otp_region otp_region); +int gd_w_status_generic(struct flashctx *flash, enum otp_region otp_region); +int gd_w_print_status_generic(struct flashctx *flash); +int gd_w_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len); +int gd_w_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len); +int gd_erase_generic(struct flashctx *flash, enum otp_region otp_region); +int gd_w_lock_generic(struct flashctx *flash, enum otp_region otp_region); + /* writeprotect.c */ struct range *sec_block_range_pattern(struct flashctx *flash); char get_cmp(struct flashctx *flash); int set_cmp(struct flashctx *flash, uint8_t cmp); uint32_t bp_bitmask_generic(struct flashctx *flash); struct range *bp_to_range(struct flashctx *flash, unsigned char bp_config); int range_to_bp_bitfield(struct flashctx *flash, uint32_t start, uint32_t len); int print_range_generic(struct flashctx *flash); int print_table_generic(struct flashctx *flash); int set_range_generic(struct flashctx *flash, uint32_t start, uint32_t len); int disable_generic(struct flashctx *flash); struct range *range_table_global(struct flashctx *flash); struct range *a25l032_range_table(struct flashctx *flash); diff --git a/flash.h b/flash.h index 631c1a8..c37426c 100644 --- a/flash.h +++ b/flash.h @@ -27,26 +27,27 @@ #include "platform.h"
#include <inttypes.h> #include <stdio.h> #include <stdint.h> #include <stddef.h> #include <stdbool.h> #if IS_WINDOWS #include <windows.h> #undef min #undef max #endif
+#include "otp.h" #include "spi25_statusreg.h" #include "writeprotect.h"
#define ERROR_PTR ((void*)-1)
/* Error codes */ #define ERROR_OOM -100 #define TIMEOUT_ERROR -101
/* TODO: check using code for correct usage of types */ typedef uintptr_t chipaddr; #define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))
@@ -240,26 +241,48 @@ struct flashchip { struct range *ranges; /* Either ranges is assigned to or range_table is, NOT both. */ /* Return pointer to WP range table. */ struct range *(*range_table) (struct flashctx *flash); /* Return BP(BP0, BP1, ... , SEC, TB) bit mask. */ uint32_t (*bp_bitmask) (struct flashctx *flash); /* Given a range, set the corresponding BP and CMP bit (if present) in the status * register. If range is invalid, return -1 and abort writing to status register. */ int (*set_range) (struct flashctx *flash, uint32_t start, uint32_t len); /* Disable any block protection in effect. */ int (*disable) (struct flashctx *flash); int (*print_table) (struct flashctx *flash); } *wp; + + struct otp { + struct region { + /* This address corresponds to the first byte in the OTP memory region. */ + uint32_t addr; + uint32_t size; /* in bytes */ + + /* Usually, setting this modifier bit will permanently lock the + * corresponding OTP region against writes. + * Not all chips have a modifier bit (AMIC, Macronix). */ + enum status_register_bit status_bit; + } region[MAX_OTP_REGIONS + 1]; /* We need one more than MAX_STATUS_REGISTERS */ + + int (*status) (struct flashctx *flash, enum otp_region otp_region); + int (*print_status) (struct flashctx *flash); + int (*read) (struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_addr, uint32_t len); + int (*write) (struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_addr, uint32_t len); + int (*erase) (struct flashctx *flash, enum otp_region otp_region); + int (*lock) (struct flashctx *flash, enum otp_region otp_region); + } *otp; };
struct flashctx { struct flashchip *chip; /* FIXME: The memory mappings should be saved in a more structured way. */ /* The physical_* fields store the respective addresses in the physical address space of the CPU. */ uintptr_t physical_memory; /* The virtual_* fields store where the respective physical address is mapped into flashrom's address * space. A value equivalent to (chipaddr)ERROR_PTR indicates an invalid mapping (or none at all). */ chipaddr virtual_memory; /* Some flash devices have an additional register space; semantics are like above. */ uintptr_t physical_registers; chipaddr virtual_registers; diff --git a/otp.c b/otp.c new file mode 100644 index 0000000..4af4b84 --- /dev/null +++ b/otp.c @@ -0,0 +1,381 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2016 Hatim Kanchwala hatim@hatimak.me + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "chipdrivers.h" +#include "flash.h" +#include "spi25_statusreg.h" + +/* Return the top-most (highest) OTP region of flash. */ +static enum otp_region top_otp_region(struct flashctx *flash) { + enum otp_region otp_region = OTP_REG_1; + struct region *region = flash->chip->otp->region; + + while (region[otp_region++].size != 0) + ; + return otp_region - 2; +} + +/* Some standard error checking used by program and erase functions. */ +static int otp_error_check(struct flashctx *flash, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + if (otp_region > top_otp_region(flash)) { + msg_cdbg("Trying to access non-existent OTP region %d\n%s has only %d OTP regions\n", + otp_region + 1, flash->chip->name, top_otp_region(flash) + 1); + return 1; + } + if (start_byte + len > flash->chip->otp->region[otp_region].size) { + msg_cdbg("OTP region for %s is %d bytes\n", flash->chip->name, + flash->chip->otp->region[otp_region].size); + return 1; + } + return 0; +} + +/* === Eon chip specific functions === */ +static uint8_t bp_bitfield, to_restore = 0; + +static void save_bp(struct flashctx *flash) { + uint8_t status = flash->chip->status_register->read(flash, SR1); + uint32_t bp_bitmask = flash->chip->wp->bp_bitmask(flash); + bp_bitfield = (status & bp_bitmask) >> pos_bit(flash, BP0); +} + +static int restore_bp(struct flashctx *flash) { + uint8_t status = flash->chip->status_register->read(flash, SR1); + uint32_t bp_bitmask = flash->chip->wp->bp_bitmask(flash); + status = ((status & ~bp_bitmask) | (bp_bitfield << pos_bit(flash, BP0))) & 0xff; + return flash->chip->status_register->write(flash, SR1, status); +} + +/* Enter OTP mode. If any Block Protect bits are set, then save + * their state and temporarily unset them all. */ +static int enter_otp_mode(struct flashctx *flash) { + uint8_t bp = flash->chip->status_register->read(flash, SR1) & flash->chip->wp->bp_bitmask(flash); + if (bp) { + msg_cdbg("Need to unset all BP bits before entering OTP mode ...\n"); + msg_cdbg("BP bits will be restored to 0x%02x\n", bp >> pos_bit(flash, BP0)); + to_restore = 1; + save_bp(flash); + } + return spi_enter_otp_mode(flash); +} + +/* Exit OTP mode. If any Block Protect bits were set prior to issuing + * an Enter OTP, then restore those bits after exiting. */ +static int exit_otp_mode(struct flashctx *flash) { + int result = spi_write_disable(flash); + if (result) { + msg_cdbg("Couldn't exit OTP mode\n"); + return result; + } + + if (to_restore) { + msg_cdbg("Restoring BP bits to their state prior to entering OTP mode ...\n"); + result = restore_bp(flash); + if (result) + msg_cdbg("Couldn't restore BP bits\n"); + } + to_restore = 0; + return result; +} + +int eon_status_generic(struct flashctx *flash, enum otp_region otp_region) { + enter_otp_mode(flash); + uint8_t status = (flash->chip->status_register->read(flash, SR1) & + (1 << pos_bit(flash, SRP0))) ? 1 : 0; + exit_otp_mode(flash); + return status; +} + +int eon_print_status_generic(struct flashctx *flash) { + enum otp_region top_region = top_otp_region(flash), region_n; + msg_cdbg("%s contains %d OTP memory region%s (also called OTP sector%s) -\n", + flash->chip->name, top_region + 1, + (top_region == 0) ? "" : "s", (top_region == 0) ? "" : "s"); + + for (region_n = OTP_REG_1; region_n <= top_region; region_n++) { + msg_cdbg("OTP memory region %d: %d bytes, controlled by %s bit in status register %d " + "(while in OTP mode)\n", region_n + 1, flash->chip->otp->region[region_n].size, + statreg_bit_desc[flash->chip->otp->region[region_n].status_bit][0], + (pos_bit(flash, flash->chip->otp->region[region_n].status_bit) / 8) + 1); + if (flash->chip->otp->status(flash, region_n)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be erased " + "or written to\n", region_n + 1); + } + } + return 0; +} + +/* Read len bytes of the security register (corresponding to otp_region) into buf, + * starting from start_byte. */ +int eon_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + int result = otp_error_check(flash, otp_region, start_byte, len); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + + enter_otp_mode(flash); + result = flash->chip->read(flash, buf, start_byte, len); + exit_otp_mode(flash); + + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Write len bytes to the security register (corresponding to otp_region) form buf, + * starting from start_byte. */ +int eon_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + int result = otp_error_check(flash, otp_region, start_byte, len); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be written to\n", + otp_region + 1); + msg_cerr("%s failed\n", __func__); + return 1; + } + + enter_otp_mode(flash); + result = flash->chip->write(flash, buf, start_byte, len); + exit_otp_mode(flash); + + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Erase the security register corresponding to otp_region. */ +int eon_erase_generic(struct flashctx *flash, enum otp_region otp_region) { + int result = otp_error_check(flash, otp_region, 0x000000, 0); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be written to\n", + otp_region + 1); + msg_cerr("%s failed\n", __func__); + return 1; + } + + enter_otp_mode(flash); + result = spi_block_erase_20(flash, flash->chip->otp->region[otp_region].addr, + flash->chip->otp->region[otp_region].size); + exit_otp_mode(flash); + + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Lock the OTP memory corresponding to otp_region. The corresponding bit + * in the status register is set (which is one-time programmable). For Eon + * chips, the SRP/SRP0/SRWD bit is served as OTP it while in OTP mode. + * Note that if the bit was already set, the function does not consider + * it a point of failure. */ +int eon_lock_generic(struct flashctx *flash, enum otp_region otp_region) { + int result = otp_error_check(flash, otp_region, 0x000000, 0); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + enum status_register_bit status_bit = flash->chip->otp->region[otp_region].status_bit; + if (pos_bit(flash, status_bit) == -1) { + /* Check if such a bit even exists in the status register in the first place. */ + // TODO(hatim): This block does not seem to have many use cases as the error + // can be avoided while reviewing patches itself + msg_cdbg("OTP modifier bit %s for %s defined incorrectly\n", + statreg_bit_desc[status_bit][0], flash->chip->name); + msg_cerr("%s failed\n", __func__); + return 1; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP modifier bit already set, " + "cannot alter value as it is one-time-programmable only\n"); + // FIXME(hatim): Should we return zero or non-zero here? + return 0; + } + + enter_otp_mode(flash); + /* WRSR will set OTP modifier bit irrespective of status byte supplied. */ + flash->chip->status_register->write(flash, SR1, 1 << pos_bit(flash, SRP0)); + exit_otp_mode(flash); + + if (!flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("Unable to set OTP modifier bit\n"); + msg_cerr("%s failed\n", __func__); + return 1; + } else + return 0; +} + + +/* === GigaDevice and (most) Winbond chip specific functions === */ +/* Get the OTP modifier bit (these are usually the LB1, LB2, ... bits) from the status + * registers. */ +static int gd_w_get_otp_bit(struct flashctx *flash, enum status_register_bit modifier_bit) { + enum status_register_num SRn = pos_bit(flash, modifier_bit) / 8; + uint8_t modifier_bit_mask = 1 << (pos_bit(flash, modifier_bit) - (SRn * 8)); + + uint8_t status = flash->chip->status_register->read(flash, SRn); + return status & modifier_bit_mask; +} + +/* Set the OTP modifier bit (these are usually the LB1, LB2, ... bits) in the status registers. + * We take no value of the bit as an argument because they are one-time-programmable only and + * they can only be set. */ +static int gd_w_set_otp_bit(struct flashctx *flash, enum status_register_bit modifier_bit) { + enum status_register_num SRn = pos_bit(flash, modifier_bit) / 8; + uint8_t modifier_bit_mask = 1 << (pos_bit(flash, modifier_bit) - (SRn * 8)); + + uint8_t status = flash->chip->status_register->read(flash, SRn); + status = (status & ~modifier_bit_mask) & 0xff; + status |= modifier_bit_mask; + return flash->chip->status_register->write(flash, SRn, status); +} + +int gd_w_status_generic(struct flashctx *flash, enum otp_region otp_region) { + return gd_w_get_otp_bit(flash, flash->chip->otp->region[otp_region].status_bit) ? 1 : 0; +} + +int gd_w_print_status_generic(struct flashctx *flash) { + enum otp_region top_region = top_otp_region(flash), region_n; + msg_cdbg("%s contains %d OTP memory region%s (also called Security Register%s) -\n", + flash->chip->name, top_region + 1, + (top_region == 0) ? "" : "s", (top_region == 0) ? "" : "s"); + + for (region_n = OTP_REG_1; region_n <= top_region; region_n++) { + msg_cdbg("OTP memory region %d: %d bytes, controlled by %s bit in status register %d\n", + region_n + 1, + flash->chip->otp->region[region_n].size, + statreg_bit_desc[flash->chip->otp->region[region_n].status_bit][0], + (pos_bit(flash, flash->chip->otp->region[region_n].status_bit) / 8) + 1); + if (flash->chip->otp->status(flash, region_n)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be erased " + "or written to\n", region_n + 1); + } + } + return 0; +} + +/* Read len bytes of the security register (corresponding to otp_region) into buf, + * starting from start_byte. */ +int gd_w_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + int result = otp_error_check(flash, otp_region, start_byte, len); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + + /* Prefix the first couple of pre-defined bits of the security register address. */ + result = spi_sec_reg_read(flash, buf, flash->chip->otp->region[otp_region].addr | start_byte, len); + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Write len bytes to the security register (corresponding to otp_region) form buf, + * starting from start_byte. */ +int gd_w_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + int result = otp_error_check(flash, otp_region, start_byte, len); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be written to\n", + otp_region + 1); + msg_cerr("%s failed\n", __func__); + return 1; + } + + /* Prefix the first couple of pre-defined bits of the security register address. */ + result = spi_sec_reg_prog(flash, buf, flash->chip->otp->region[otp_region].addr | start_byte, len); + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Erase the security register corresponding to otp_region. */ +int gd_erase_generic(struct flashctx *flash, enum otp_region otp_region) { + int result = otp_error_check(flash, otp_region, 0x000000, 0); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be erased\n", + otp_region + 1); + msg_cerr("%s failed\n", __func__); + return 1; + } + + result = spi_sec_reg_erase(flash, flash->chip->otp->region[otp_region].addr); + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Lock the OTP memory corresponding to otp_region. The corresponding bit + * in the status register is set (which is one-time programmable). + * Note that if the bit was already set, the function does not consider + * it a point of failure. */ +int gd_w_lock_generic(struct flashctx *flash, enum otp_region otp_region) { + int result = otp_error_check(flash, otp_region, 0x000000, 0); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + + enum status_register_bit status_bit = flash->chip->otp->region[otp_region].status_bit; + if (pos_bit(flash, status_bit) == -1) { + /* Check if such a bit even exists in the status register in the first place. */ + // TODO(hatim): This block does not seem to have many use cases as the error + // can be avoided while reviewing patches itself + msg_cdbg("OTP modifier bit %s for %s defined incorrectly\n", + statreg_bit_desc[status_bit][0], flash->chip->name); + msg_cerr("%s failed\n", __func__); + return 1; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP modifier bit already set, " + "cannot alter value as it is one-time-programmable only\n"); + // FIXME(hatim): Should we return zero or non-zero here? + return 0; + } else { + result = gd_w_set_otp_bit(flash, status_bit); + if (result) + msg_cerr("%s failed\n", __func__); + if (!flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("Unable to set OTP modifier bit\n"); + msg_cerr("%s failed\n", __func__); + return 1; + } + return result; + } +} diff --git a/otp.h b/otp.h new file mode 100644 index 0000000..790c8c5 --- /dev/null +++ b/otp.h @@ -0,0 +1,28 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2016 Hatim Kanchwala hatim@hatimak.me + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __OTP_H__ +#define __OTP_H__ 1 + +#define MAX_OTP_REGIONS 4 + +enum otp_region { OTP_REG_1 = 0, OTP_REG_2, OTP_REG_3 }; + +#endif /* !__OTP_H__ */ diff --git a/spi.h b/spi.h index f061041..90bff5f 100644 --- a/spi.h +++ b/spi.h @@ -148,22 +148,42 @@ /* JEDEC_READ_INSIZE : any length */
/* Write memory byte */ #define JEDEC_BYTE_PROGRAM 0x02 #define JEDEC_BYTE_PROGRAM_OUTSIZE 0x05 #define JEDEC_BYTE_PROGRAM_INSIZE 0x00
/* Write AAI word (SST25VF080B) */ #define JEDEC_AAI_WORD_PROGRAM 0xad #define JEDEC_AAI_WORD_PROGRAM_OUTSIZE 0x06 #define JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE 0x03 #define JEDEC_AAI_WORD_PROGRAM_INSIZE 0x00
+/* Enter OTP mode (supported by most Eon chips) */ +#define JEDEC_ENTER_OTP 0x3A +#define JEDEC_ENTER_OTP_OUTSIZE 0x01 +#define JEDEC_ENTER_OTP_INSIZE 0x00 + +/* Read Security Register(s) (supported by most GigaDevice chips) */ +#define JEDEC_READ_SEC_REG 0x48 +#define JEDEC_READ_SEC_REG_OUTSIZE 0x05 +/* JEDEC_READ_SEC_REG_INSIZE any length */ + +/* Program Security Register(s) (supported by most GigaDevice chips) */ +#define JEDEC_PROG_BYTE_SEC_REG 0x42 +#define JEDEC_PROG_BYTE_SEC_REG_OUTSIZE 0x05 +#define JEDEC_PROG_BYTE_SEC_REG_INSIZE 0x00 + +/* Erase Security Register(s) (supported by most GigaDevice chips) */ +#define JEDEC_ERASE_SEC_REG 0x44 +#define JEDEC_ERASE_SEC_REG_OUTSIZE 0x04 +#define JEDEC_ERASE_SEC_REG_INSIZE 0x00 + /* Error codes */ #define SPI_GENERIC_ERROR -1 #define SPI_INVALID_OPCODE -2 #define SPI_INVALID_ADDRESS -3 #define SPI_INVALID_LENGTH -4 #define SPI_FLASHROM_BUG -5 #define SPI_PROGRAMMER_ERROR -6
#endif /* !__SPI_H__ */ diff --git a/spi25.c b/spi25.c index 51db4c8..a38623c 100644 --- a/spi25.c +++ b/spi25.c @@ -1259,13 +1259,86 @@ int default_spi_write_aai(struct flashctx *flash, const uint8_t *buf, unsigned i if (spi_chip_write_1(flash, buf + pos - start, pos, pos % 2)) return SPI_GENERIC_ERROR; pos += pos % 2; }
return 0;
bailout: result = spi_write_disable(flash); if (result != 0) msg_cerr("%s failed to disable AAI mode.\n", __func__); return SPI_GENERIC_ERROR; } + + +/* This function is specific to mostly Eon chips. It maps the + * additional OTP sector to the top or bottom sector (depending + * on the chip). The mapped sector behaves like just another + * normal sector. */ +int spi_enter_otp_mode(struct flashctx *flash) +{ + static const unsigned char cmd[JEDEC_ENTER_OTP_OUTSIZE] = { JEDEC_ENTER_OTP }; + return spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); +} + +int spi_sec_reg_read(struct flashctx *flash, uint8_t *buf, uint32_t start_addr, uint32_t len) +{ + /* We assume that start_addr and len are correct and proceed without any error checking. */ + uint8_t cmd[JEDEC_READ_SEC_REG_OUTSIZE] = { + (uint8_t)JEDEC_READ_SEC_REG, + (uint8_t)(start_addr >> 16) & 0xff, + (uint8_t)(start_addr >> 8) & 0xff, + (uint8_t)start_addr & 0xff, + (uint8_t)0x00, + }; + + int result = spi_send_command(flash, sizeof(cmd), len, cmd, buf); + if (result) + msg_cerr("%s failed.\n", __func__); + return result; +} + +int spi_sec_reg_prog(struct flashctx *flash, uint8_t const *buf, uint32_t start_addr, uint32_t len) +{ + /* We assume that start_addr and len are correct, the security register is unlocked + * and proceed without any error checking. */ + int ret = spi_write_enable(flash); + if (ret) { + msg_cerr("%s failed\n", __func__); + return ret; + } + + uint8_t cmd[JEDEC_PROG_BYTE_SEC_REG_OUTSIZE - 1 + len]; + cmd[0] = (uint8_t)JEDEC_PROG_BYTE_SEC_REG; + cmd[1] = (uint8_t)(start_addr >> 16) & 0xff; + cmd[2] = (uint8_t)(start_addr >> 8) & 0xff; + cmd[3] = (uint8_t)start_addr & 0xff; + memcpy((void *)&cmd[4], (void *)buf, (size_t)len); + // FIXME(hatim): We should probably poll WIP bit + ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); + if (ret) + msg_cerr("%s\n", __func__); + return ret; +} + +int spi_sec_reg_erase(struct flashctx *flash, uint32_t addr) +{ + /* We assume that addr is correct and proceed without any error checking. */ + int ret = spi_write_enable(flash); + if (ret) { + msg_cerr("%s failed\n", __func__); + return ret; + } + + uint8_t cmd[JEDEC_ERASE_SEC_REG_OUTSIZE] = { + (uint8_t)JEDEC_ERASE_SEC_REG, + (uint8_t)(addr >> 16) & 0xff, + (uint8_t)(addr >> 8) & 0xff, + (uint8_t)addr & 0xff, + }; + // FIXME(hatim): We should probably poll WIP bit + ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); + if (ret) + msg_cerr("%s\n", __func__); + return ret; +}
Infrastructure support in flashchips.c added for (31 in total) - - Eon(13) : EN25Q40, EN25Q80(A), EN25Q16, EN25Q32(A/B), EN25Q64, EN25Q128, EN25QH16, EN25QH32, EN25QH64, EN25QH128 - GigaDevice(16) : GD25LQ40, GD25LQ80, GD25LQ16, GD25Q80B, GD25Q16B, GD25Q32(B), GD25Q64(B), GD25Q128B, GD25Q128C, GD25VQ21B, GD25VQ40C, GD25VQ41B, GD25VQ80C, GD25VQ16C - Winbond(2) : W25Q40BL, W25Q64FV
Signed-off-by: Hatim Kanchwala hatim@hatimak.me --- Makefile | 3 +- flashchips.c | 117 +++++++++++++++--------------- otp.h | 11 +++ otp_layouts.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 302 insertions(+), 58 deletions(-) create mode 100644 otp_layouts.c
diff --git a/Makefile b/Makefile index e97f641..47b9e2c 100644 --- a/Makefile +++ b/Makefile @@ -504,27 +504,28 @@ endif ifeq ($(CONFIG_IT8212), yes) UNSUPPORTED_FEATURES += CONFIG_IT8212=yes else override CONFIG_IT8212 = no endif endif
############################################################################### # Flash chip drivers and bus support infrastructure.
CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \ sst28sf040.o 82802ab.o sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o \ spi25.o spi25_statusreg.o otp.o writeprotect.o statusreg_layouts.o \ - writeprotect_layouts.o opaque.o sfdp.o en29lv640b.o at45db.o + otp_layouts.o writeprotect_layouts.o opaque.o sfdp.o en29lv640b.o \ + at45db.o
############################################################################### # Library code.
LIB_OBJS = layout.o flashrom.o udelay.o programmer.o helpers.o
############################################################################### # Frontend related stuff.
CLI_OBJS = cli_classic.o cli_output.o cli_common.o print.o
# Set the flashrom version string from the highest revision number of the checked out flashrom files. # Note to packagers: Any tree exported with "make export" or "make tarball" diff --git a/flashchips.c b/flashchips.c index 43545a4..b946cb8 100644 --- a/flashchips.c +++ b/flashchips.c @@ -4507,102 +4507,101 @@ const struct flashchip flashchips[] = { .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, },
{ .vendor = "Eon", .name = "EN25Q40", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q40, .total_size = 512, .page_size = 256, - /* OTP: 256B total; enter 0x3A */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 128} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 8} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25q80a_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25q40_wp, + .otp = &en256_512otp, },
{ .vendor = "Eon", .name = "EN25Q80(A)", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q80, .total_size = 1024, .page_size = 256, - /* OTP: 256B total; enter 0x3A */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 256} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 16} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25q80a_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25q80a_wp, + .otp = &en256_1024otp, },
{ /* Note: EN25D16 is an evil twin which shares the model ID but has different write protection capabilities */ .vendor = "Eon", .name = "EN25Q16", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q16, .total_size = 2048, .page_size = 256, - /* OTP: D16 512B/Q16 128B total; enter 0x3A */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 512} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 32} }, .block_erase = spi_block_erase_d8, }, { /* not supported by Q16 version */ @@ -4611,319 +4610,320 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25q16_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25q16_wp, + .otp = &en128_2048otp, },
{ .vendor = "Eon", .name = "EN25Q32", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q32, .total_size = 4096, .page_size = 256, - /* OTP: 512B total; enter 0x3A */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 1024} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 64} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25q16_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25q32_wp, + .otp = &en512_4096otp, },
{ .vendor = "Eon", .name = "EN25Q32A/EN25Q32B", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q32, .total_size = 4096, .page_size = 256, - /* OTP: 512B total; enter 0x3A */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 1024} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 64} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25q32ab_wp, + .otp = &en512_4096otp, },
{ .vendor = "Eon", .name = "EN25Q64", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q64, .total_size = 8192, .page_size = 256, - /* OTP: 512B total; enter 0x3A */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 2048} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 128} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25q64_wp, + .otp = &en512_8192otp, },
{ .vendor = "Eon", .name = "EN25Q128", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25Q128, .total_size = 16384, .page_size = 256, - /* OTP: 512B total; enter 0x3A */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 4096} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 256} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .wp = &en25q128_wp, + .otp = &en512_16384otp, },
{ .vendor = "Eon", .name = "EN25QH16", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25QH16, .total_size = 2048, .page_size = 256, /* supports SFDP */ - /* OTP: 512B total; enter 0x3A */ /* QPI enable 0x38, disable 0xFF */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 512} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 32} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1024 * 2048, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 2048, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25qh16_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25qh16_wp, + .otp = &en512_2048otp, },
{ .vendor = "Eon", .name = "EN25QH32", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25QH32, .total_size = 4096, .page_size = 256, /* supports SFDP */ - /* OTP: 512B total; enter 0x3A */ /* QPI enable 0x38, disable 0xFF */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 1024} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 64} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { {1024 * 4096, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 4096, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25qh32_wp, + .otp = &en512_4096otp, },
{ .vendor = "Eon", .name = "EN25QH64", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25QH64, .total_size = 8192, .page_size = 256, /* supports SFDP */ - /* OTP: 512B total; enter 0x3A */ /* QPI enable 0x38, disable 0xFF */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 2048} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 128} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { { 8192 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { { 8192 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25qh64_wp, + .otp = &en512_8192otp, },
{ .vendor = "Eon", .name = "EN25QH128", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25QH128, .total_size = 16384, .page_size = 256, /* supports SFDP */ - /* OTP: 512B total; enter 0x3A */ /* QPI enable 0x38, disable 0xFF */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 4096} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {64 * 1024, 256} }, .block_erase = spi_block_erase_d8, }, { .eraseblocks = { { 16384 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { { 16384 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &en25qh128_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &en25qh128_wp, + .otp = &en512_16384otp, },
{ .vendor = "Eon", .name = "EN25S10", .bustype = BUS_SPI, .manufacture_id = EON_ID_NOPREFIX, .model_id = EON_EN25S10, .total_size = 128, .page_size = 256, /* OTP: 256B total; enter 0x3A */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_UNTESTED, @@ -5714,28 +5714,27 @@ const struct flashchip flashchips[] = { .write = write_jedec_1, /* Supports a fast mode too */ .read = read_memmapped, .voltage = {3000, 3600}, /* 3.0-3.6V for type -70, others 2.7-3.6V */ },
{ .vendor = "GigaDevice", .name = "GD25LQ40", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25LQ40, .total_size = 512, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 128} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 16} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 8} }, @@ -5743,38 +5742,38 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25lq_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {1695, 1950}, .wp = &gd_w_wp, + .otp = &gd_w256_3_otp, },
{ .vendor = "GigaDevice", .name = "GD25LQ80", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25LQ80, .total_size = 1024, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 256} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 32} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 16} }, @@ -5782,38 +5781,38 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {1 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25lq_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {1695, 1950}, .wp = &gd_w_wp, + .otp = &gd_w256_3_otp, },
{ .vendor = "GigaDevice", .name = "GD25LQ16", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25LQ16, .total_size = 2048, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 512} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 64} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 32} }, @@ -5821,26 +5820,27 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25lq_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {1695, 1950}, .wp = &gd_w_wp, + .otp = &gd_w256_3_otp, },
{ .vendor = "GigaDevice", .name = "GD25LQ32", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25LQ32, .total_size = 4096, .page_size = 256, /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_OK_PREW, @@ -6095,27 +6095,27 @@ const struct flashchip flashchips[] = { .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, },
{ .vendor = "GigaDevice", .name = "GD25Q80", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q80, .total_size = 1024, .page_size = 256, - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 256} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 32} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 16} }, @@ -6133,28 +6133,27 @@ const struct flashchip flashchips[] = { .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25Q80B", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q80, .total_size = 1024, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 256} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 32} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 16} }, @@ -6162,26 +6161,27 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25q80b_128_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, + .otp = &gd256_4_otp, },
{ .vendor = "GigaDevice", .name = "GD25Q16", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q16, .total_size = 2048, .page_size = 256, .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, @@ -6210,28 +6210,27 @@ const struct flashchip flashchips[] = { .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, },
{ .vendor = "GigaDevice", .name = "GD25Q16B", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q16, .total_size = 2048, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 (B version only) */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 512} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 64} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 32} }, @@ -6239,38 +6238,38 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25q16_32_64b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, + .otp = &gd256_4_otp, },
{ .vendor = "GigaDevice", .name = "GD25Q32(B)", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q32, .total_size = 4096, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 1024} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 128} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 64} }, @@ -6278,38 +6277,38 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {4 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25q16_32_64b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, + .otp = &gd256_4_otp, },
{ .vendor = "GigaDevice", .name = "GD25Q64(B)", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q64, .total_size = 8192, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 2048} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 256} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 128} }, @@ -6317,38 +6316,38 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25q16_32_64b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, + .otp = &gd256_4_otp, },
{ .vendor = "GigaDevice", .name = "GD25Q128B", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q128, .total_size = 16384, .page_size = 256, - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 4096} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 512} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 256} }, @@ -6356,39 +6355,39 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25q16_32_64b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, + .otp = &gd256_4_otp, },
{ .vendor = "GigaDevice", .name = "GD25Q128C", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25Q128, .total_size = 16384, .page_size = 256, - /* OTP: 1536B total; read 0x48; write 0x42, erase 0x44 */ /* QPI: enable 0x38, disable 0xFF */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 4096} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 512} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 256} }, @@ -6396,26 +6395,27 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {16 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25q128c_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, + .otp = &gd512_3_otp, },
{ .vendor = "GigaDevice", .name = "GD25T80", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25T80, .total_size = 1024, .page_size = 256, /* OTP: 256B total; enter 0x3A */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, .tested = TEST_UNTESTED, @@ -6444,28 +6444,27 @@ const struct flashchip flashchips[] = { .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, },
{ .vendor = "GigaDevice", .name = "GD25VQ21B", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ21B, .total_size = 256, .page_size = 256, - /* OTP: 1536B total; read 0x48, write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { { 4 * 1024, 64} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { { 32 * 1024, 8} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 4} }, @@ -6473,39 +6472,39 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {256 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {256 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25vq21_41b_q21_q41b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, .wp = &gd_w_wp, + .otp = &gd512_3_otp, },
{ .vendor = "GigaDevice", .name = "GD25VQ40C", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ41B, .total_size = 512, .page_size = 256, /* Supports SFDP */ - /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { { 4 * 1024, 128} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { { 32 * 1024, 16} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 8} }, @@ -6513,38 +6512,38 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25vq16_80c_q16_40c_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, .wp = &gd_w_wp, + .otp = &gd256_4_otp, },
{ .vendor = "GigaDevice", .name = "GD25VQ41B", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ41B, .total_size = 512, .page_size = 256, - /* OTP: 1536B total; read 0x48, write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { { 4 * 1024, 128} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { { 32 * 1024, 16} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 8} }, @@ -6552,39 +6551,39 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25vq21_41b_q21_q41b_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, .wp = &gd_w_wp, + .otp = &gd512_3_otp, },
{ .vendor = "GigaDevice", .name = "GD25VQ80C", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ80C, .total_size = 1024, .page_size = 256, /* Supports SFDP */ - /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { { 4 * 1024, 256} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { { 32 * 1024, 32} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 16} }, @@ -6592,39 +6591,39 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25vq16_80c_q16_40c_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, .wp = &gd_w_wp, + .otp = &gd256_4_otp, },
{ .vendor = "GigaDevice", .name = "GD25VQ16C", .bustype = BUS_SPI, .manufacture_id = GIGADEVICE_ID, .model_id = GIGADEVICE_GD25VQ16C, .total_size = 2 * 1024, .page_size = 256, /* Supports SFDP */ - /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { { 4 * 1024, 512} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { { 32 * 1024, 64} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { { 64 * 1024, 32} }, @@ -6632,26 +6631,27 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {2 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &gd25vq16_80c_q16_40c_sr, .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2300, 3600}, .wp = &gd_w_wp, + .otp = &gd256_4_otp, },
{ .vendor = "Hyundai", .name = "HY29F002T", .bustype = BUS_PARALLEL, .manufacture_id = HYUNDAI_ID, .model_id = HYUNDAI_HY29F002T, .total_size = 256, .page_size = 256 * 1024, .feature_bits = FEATURE_EITHER_RESET, /* Some revisions may need FEATURE_ADDR_2AA */ .tested = TEST_OK_PRE, .probe = probe_jedec, @@ -14538,28 +14538,28 @@ const struct flashchip flashchips[] = { .read = read_memmapped, .voltage = {4500, 5500}, },
{ .vendor = "Winbond", .name = "W25Q40.V", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25Q40_V, .total_size = 512, .page_size = 256, /* supports SFDP */ - /* OTP: 756B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + /* Read ID 0x4B */ + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 128} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 16} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 8} }, @@ -14567,26 +14567,27 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {512 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &w25q40bl_64fv_sr, .write = spi_chip_write_256, /* Multi I/O supported */ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, .wp = &gd_w_wp, + .otp = &gd_w256_3_otp, },
{ .vendor = "Winbond", .name = "W25Q80.V", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25Q80_V, .total_size = 1024, .page_size = 256, /* supports SFDP */ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, @@ -14698,28 +14699,28 @@ const struct flashchip flashchips[] = { .read = spi_chip_read, .voltage = {2700, 3600}, },
{ .vendor = "Winbond", .name = "W25Q64.V", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25Q64_V, .total_size = 8192, .page_size = 256, /* supports SFDP */ - /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, + /* Read ID 0x4B */ + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 2048} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 256} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 128} }, @@ -14727,26 +14728,27 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &w25q40bl_64fv_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, .wp = &gd_w_wp, + .otp = &gd_w256_3_otp, },
{ .vendor = "Winbond", .name = "W25Q128.V", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25Q128_V, .total_size = 16384, .page_size = 256, /* supports SFDP */ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP, @@ -14974,29 +14976,29 @@ const struct flashchip flashchips[] = { .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */ },
{ .vendor = "Winbond", .name = "W25Q64.W", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25Q64_W, .total_size = 8192, .page_size = 256, - /* OTP: 256B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ + /* Read ID 0x4B */ /* QPI enable 0x38, disable 0xFF */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_QPI, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, .block_erasers = { { .eraseblocks = { {4 * 1024, 2048} }, .block_erase = spi_block_erase_20, }, { .eraseblocks = { {32 * 1024, 256} }, .block_erase = spi_block_erase_52, }, { .eraseblocks = { {64 * 1024, 128} }, @@ -15004,26 +15006,27 @@ const struct flashchip flashchips[] = { }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_60, }, { .eraseblocks = { {8 * 1024 * 1024, 1} }, .block_erase = spi_block_erase_c7, } }, .status_register = &w25q40bl_64fv_sr, .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */ .wp = &gd_w_wp, + .otp = &gd_w256_3_otp, },
{ .vendor = "Winbond", .name = "W25X10", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, .model_id = WINBOND_NEX_W25X10, .total_size = 128, .page_size = 256, .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PREW, .probe = probe_spi_rdid, diff --git a/otp.h b/otp.h index 790c8c5..7ea9d3f 100644 --- a/otp.h +++ b/otp.h @@ -15,14 +15,25 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef __OTP_H__ #define __OTP_H__ 1
#define MAX_OTP_REGIONS 4
enum otp_region { OTP_REG_1 = 0, OTP_REG_2, OTP_REG_3 };
+extern struct otp en128_2048otp; +extern struct otp en256_512otp; +extern struct otp en256_1024otp; +extern struct otp en512_2048otp; +extern struct otp en512_4096otp; +extern struct otp en512_8192otp; +extern struct otp en512_16384otp; +extern struct otp gd_w256_3_otp; +extern struct otp gd256_4_otp; +extern struct otp gd512_3_otp; + #endif /* !__OTP_H__ */ diff --git a/otp_layouts.c b/otp_layouts.c new file mode 100644 index 0000000..e19516a --- /dev/null +++ b/otp_layouts.c @@ -0,0 +1,229 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2016 Hatim Kanchwala hatim@hatimak.me + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "chipdrivers.h" +#include "flash.h" +#include "otp.h" + +/* === Eon === */ +/* EN25Q16 */ +struct otp en128_2048otp = { + .region = { + { + .addr = 0x1FF000, + .size = 128, + .status_bit = SRP0, + }, + }, + .status = &eon_status_generic, + .print_status = &eon_print_status_generic, + .read = &eon_read_generic, + .write = &eon_write_generic, + .erase = &eon_erase_generic, + .lock = &eon_lock_generic, +}; + +/* EN25Q40 */ +struct otp en256_512otp = { + .region = { + { + .addr = 0x07F000, + .size = 256, + .status_bit = SRP0, + }, + }, + .status = &eon_status_generic, + .print_status = &eon_print_status_generic, + .read = &eon_read_generic, + .write = &eon_write_generic, + .erase = &eon_erase_generic, + .lock = &eon_lock_generic, +}; + +/* EN25Q80(A) */ +struct otp en256_1024otp = { + .region = { + { + .addr = 0x0FF000, + .size = 256, + .status_bit = SRP0, + }, + }, + .status = &eon_status_generic, + .print_status = &eon_print_status_generic, + .read = &eon_read_generic, + .write = &eon_write_generic, + .erase = &eon_erase_generic, + .lock = &eon_lock_generic, +}; + +/* EN25QH16 */ +struct otp en512_2048otp = { + .region = { + { + .addr = 0x1FF000, + .size = 512, + .status_bit = SRP0, + }, + }, + .status = &eon_status_generic, + .print_status = &eon_print_status_generic, + .read = &eon_read_generic, + .write = &eon_write_generic, + .erase = &eon_erase_generic, + .lock = &eon_lock_generic, +}; + +/* EN25Q32(A/B), EN25QH32 */ +struct otp en512_4096otp = { + .region = { + { + .addr = 0x3FF000, + .size = 512, + .status_bit = SRP0, + }, + }, + .status = &eon_status_generic, + .print_status = &eon_print_status_generic, + .read = &eon_read_generic, + .write = &eon_write_generic, + .erase = &eon_erase_generic, + .lock = &eon_lock_generic, +}; + +/* EN25Q64, EN25QH64 */ +struct otp en512_8192otp = { + .region = { + { + .addr = 0x7FF000, + .size = 512, + .status_bit = SRP0, + }, + }, + .status = &eon_status_generic, + .print_status = &eon_print_status_generic, + .read = &eon_read_generic, + .write = &eon_write_generic, + .erase = &eon_erase_generic, + .lock = &eon_lock_generic, +}; + +/* EN25Q128, EN25QH128 */ +struct otp en512_16384otp = { + .region = { + { + .addr = 0xFFF000, + .size = 512, + .status_bit = SRP0, + }, + }, + .status = &eon_status_generic, + .print_status = &eon_print_status_generic, + .read = &eon_read_generic, + .write = &eon_write_generic, + .erase = &eon_erase_generic, + .lock = &eon_lock_generic, +}; + +/* === GigaDevice and Winbond === */ +// FIXME(hatim): Deal with chips with shared OTP modifier bit +/* GD25LQ16, GD25LQ80, GD25LQ40, W25Q40BL, W25Q64FV + * (There is an additional 256 bytes security register + * at 0x000000 which is reserved and can only be read.) */ +// FIXME(hatim): Add support to interact with the reserved security register +struct otp gd_w256_3_otp = { + .region = { + { + .addr = 0x001000, + .size = 256, + .status_bit = LB1, + }, { + .addr = 0x002000, + .size = 256, + .status_bit = LB2, + }, { + .addr = 0x003000, + .size = 256, + .status_bit = LB3, + }, + }, + .status = &gd_w_status_generic, + .print_status = &gd_w_print_status_generic, + .read = &gd_w_read_generic, + .write = &gd_w_write_generic, + .erase = &gd_erase_generic, + .lock = &gd_w_lock_generic, +}; + +/* GD25VQ16C, GD25VQ40C, GD25VQ80C, GD25Q16B, GD25Q32B, GD25Q64B, GD25Q80(B), GD25Q128B */ +// FIXME(hatim): Datasheets show 4 registers for program, but single 1024 B register +// for read and erase; which could be modelled as a single 1024 B register starting 0x000000 +struct otp gd256_4_otp = { + .region = { + { + .addr = 0x000000, + .size = 256, + .status_bit = LB1, /* LB1 is same as LB */ + }, { + .addr = 0x000100, + .size = 256, + .status_bit = LB1, /* LB1 is same as LB */ + }, { + .addr = 0x000200, + .size = 256, + .status_bit = LB1, /* LB1 is same as LB */ + }, { + .addr = 0x000300, + .size = 256, + .status_bit = LB1, /* LB1 is same as LB */ + }, + }, + .status = &gd_w_status_generic, + .print_status = &gd_w_print_status_generic, + .read = &gd_w_read_generic, + .write = &gd_w_write_generic, + .erase = &gd_erase_generic, + .lock = &gd_w_lock_generic, +}; + +/* GD25VQ21B, GD25VQ41B, GD25Q128C */ +struct otp gd512_3_otp = { + .region = { + { + .addr = 0x001000, + .size = 512, + .status_bit = LB1, + }, { + .addr = 0x002000, + .size = 512, + .status_bit = LB2, + }, { + .addr = 0x003000, + .size = 512, + .status_bit = LB3, + }, + }, + .status = &gd_w_status_generic, + .print_status = &gd_w_print_status_generic, + .read = &gd_w_read_generic, + .write = &gd_w_write_generic, + .erase = &gd_erase_generic, + .lock = &gd_w_lock_generic, +};
- Following CLI options added - - print-otp-status : print OTP memory regions and lock status - read-otp : read OTP memory (optionally specifiy region) and save to <file> - write-otp : write <file> to OTP memory (optionally specifiy region) - erase-otp : erase OTP memory (optionally specifiy region) - lock-otp : lock OTP memory (optionally specifiy region) - Updated man page with new CLI options - Updated message for chips with FEATURE_OTP, but which do not yet support new infrastructure
Signed-off-by: Hatim Kanchwala hatim@hatimak.me --- cli_classic.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- cli_common.c | 54 ++++++------ flash.h | 2 +- flashrom.8.tmpl | 52 ++++++++++- 4 files changed, 327 insertions(+), 44 deletions(-)
diff --git a/cli_classic.c b/cli_classic.c index 1170d33..146750e 100644 --- a/cli_classic.c +++ b/cli_classic.c @@ -31,42 +31,49 @@ #include "flash.h" #include "flashchips.h" #include "programmer.h" #include "writeprotect.h"
enum LONGOPT_RETURN_VALUES { /* Start after ASCII chars */ LONGOPT_PRINT_STATUSREG = 256, LONGOPT_PRINT_WP_STATUS, LONGOPT_LIST_BP_RANGES, LONGOPT_WP_ENABLE, LONGOPT_WP_DISABLE, LONGOPT_SET_BP_RANGE, + LONGOPT_PRINT_OTP_STATUS, + LONGOPT_READ_OTP, + LONGOPT_WRITE_OTP, + LONGOPT_ERASE_OTP, + LONGOPT_LOCK_OTP, };
static void cli_classic_usage(const char *name) { printf("Please note that the command line interface for flashrom has changed between\n" "0.9.5 and 0.9.6 and will change again before flashrom 1.0.\n\n");
printf("Usage: %s [-h|-R|-L|" #if CONFIG_PRINT_WIKI == 1 "-z|" #endif "-p <programmername>[:<parameters>] [-c <chipname>]\n" "[-E|(-r|-w|-v) <file>] [-l <layoutfile> [-i <imagename>]...] [-n] [-f]]\n" "[-V[V[V]]] [-o <logfile>] [--print-status-reg] [--print-wp-status]\n" "[--wp-list] [--wp-enable[=<MODE>]] [--wp-disable]\n" - "[--wp-set-range start=<start>,len=<len>]\n\n", name); + "[--wp-set-range start=<start>,len=<len>] [--print-otp-status]\n" + "[--read-otp file=<file>[,reg=<region>]] [--erase-otp [reg=<region>]]\n" + "[--write-otp file=<file>[,reg=<region>]] [--lock-otp [reg=<region>]]\n\n", name);
printf(" -h | --help print this help text\n" " -R | --version print version (release)\n" " -r | --read <file> read flash and save to <file>\n" " -w | --write <file> write <file> to flash\n" " -v | --verify <file> verify flash against <file>\n" " -E | --erase erase flash memory\n" " -V | --verbose more verbose output\n" " -c | --chip <chipname> probe only for specified flash chip\n" " -f | --force force specific operations (see man page)\n" " -n | --noverify don't auto-verify\n" " -l | --layout <layoutfile> read ROM layout from <layoutfile>\n" " -i | --image <name> only flash image <name> from flash layout\n" @@ -74,47 +81,55 @@ static void cli_classic_usage(const char *name) " -L | --list-supported print supported devices\n" #if CONFIG_PRINT_WIKI == 1 " -z | --list-supported-wiki print supported devices in wiki syntax\n" #endif " --print-status-reg print detailed contents of status register(s)\n" " --print-wp-status print write protection mode of status register(s)\n" " --wp-list print list of write protection ranges\n" " --wp-enable[=MODE] enable write protection of status register(s)\n" " MODE can be one of HARDWARE (default),\n" " PERMANENT, POWER_CYCLE, SOFTWARE (see man page)\n" " --wp-disable disable any write protection of status register(s)\n" " --wp-set-range start=<start>,len=<len>\n" " set write protection range (see man page)\n" + " --print-otp-status print OTP memory regions and lock status\n" + " --read-otp <file>[,reg=<region>]\n" + " read OTP memory <region> (defaults to 1)\n" + " and save to <file>\n" + " --write-otp file=<file>[,reg=<region>]\n" + " write <file> to OTP memory <region> (defaults to 1)\n" + " --erase-otp [reg=<region>] erase OTP memory <region> (defaults to 1)\n" + " --lock-otp [reg=<region>] lock OTP memory <region> (defaults to 1)\n" " -p | --programmer <name>[:<param>] specify the programmer device. One of\n"); list_programmers_linebreak(4, 80, 0); printf(".\n\nYou can specify one of -h, -R, -L, " #if CONFIG_PRINT_WIKI == 1 "-z, " #endif "-E, -r, -w, -v or no operation.\n" "If no operation is specified, flashrom will only probe for flash chips.\n"); }
static void cli_classic_abort_usage(void) { printf("Please run "flashrom --help" for usage info.\n"); exit(1); }
-static void cli_statreg_wp_support(struct flashctx *flash) +static void cli_infra_support(struct flashctx *flash, char const *infra) { - msg_ginfo("flashrom does not (yet) support write protection for chip "%s".\n" + msg_ginfo("flashrom does not (yet) support %s infrastructure for chip "%s".\n" "You could add support and send the patch to flashrom@flashrom.org\n", - flash->chip->name); + infra, flash->chip->name); }
static int check_filename(char *filename, char *type) { if (!filename || (filename[0] == '\0')) { fprintf(stderr, "Error: No %s file specified.\n", type); return 1; } /* Not an error, but maybe the user intended to specify a CLI option instead of a file name. */ if (filename[0] == '-') fprintf(stderr, "Warning: Supplied %s file name starts with -\n", type); return 0; } @@ -123,26 +138,27 @@ int main(int argc, char *argv[]) { const struct flashchip *chip = NULL; /* Probe for up to eight flash chips. */ struct flashctx flashes[8] = {{0}}; struct flashctx *fill_flash; const char *name; int namelen, opt, i, j; int startchip = -1, chipcount = 0, option_index = 0, force = 0; #if CONFIG_PRINT_WIKI == 1 int list_supported_wiki = 0; #endif int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0, print_status_reg = 0; int print_wp_status = 0, wp_list = 0, wp_enable = 0, wp_disable = 0, wp_set_range = 0; + int print_otp_status = 0, read_otp = 0, write_otp = 0, erase_otp = 0, lock_otp = 0; int dont_verify_it = 0, list_supported = 0, operation_specified = 0; enum programmer prog = PROGRAMMER_INVALID; int ret = 0;
static const char optstring[] = "r:Rw:v:nVEfc:l:i:p:Lzho:"; static const struct option long_options[] = { {"read", 1, NULL, 'r'}, {"write", 1, NULL, 'w'}, {"erase", 0, NULL, 'E'}, {"verify", 1, NULL, 'v'}, {"noverify", 0, NULL, 'n'}, {"chip", 1, NULL, 'c'}, {"verbose", 0, NULL, 'V'}, @@ -151,38 +167,47 @@ int main(int argc, char *argv[]) {"image", 1, NULL, 'i'}, {"list-supported", 0, NULL, 'L'}, {"list-supported-wiki", 0, NULL, 'z'}, {"programmer", 1, NULL, 'p'}, {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'R'}, {"output", 1, NULL, 'o'}, {"print-status-reg", 0, NULL, LONGOPT_PRINT_STATUSREG}, {"print-wp-status", 0, NULL, LONGOPT_PRINT_WP_STATUS}, {"wp-list", 0, NULL, LONGOPT_LIST_BP_RANGES}, {"wp-enable", optional_argument, NULL, LONGOPT_WP_ENABLE}, {"wp-disable", 0, NULL, LONGOPT_WP_DISABLE}, {"wp-set-range", 1, NULL, LONGOPT_SET_BP_RANGE}, + {"print-otp-status", 0, NULL, LONGOPT_PRINT_OTP_STATUS}, + {"read-otp", 1, NULL, LONGOPT_READ_OTP}, + {"write-otp", 1, NULL, LONGOPT_WRITE_OTP}, + {"erase-otp", optional_argument, NULL, LONGOPT_ERASE_OTP}, + {"lock-otp", optional_argument, NULL, LONGOPT_LOCK_OTP}, {NULL, 0, NULL, 0}, };
char *filename = NULL; char *layoutfile = NULL; #ifndef STANDALONE char *logfile = NULL; #endif /* !STANDALONE */ char *tempstr = NULL; char *pparam = NULL; char *wp_mode_opt = NULL; char const *wp_set_range_opt = NULL; + char const *read_otp_opt = NULL; + char const *write_otp_opt = NULL; + char const *erase_otp_opt = NULL; + char const *lock_otp_opt = NULL;
print_version(); print_banner();
if (selfcheck()) exit(1);
setbuf(stdout, NULL); /* FIXME: Delay all operation_specified checks until after command * line parsing to allow --help overriding everything else. */ while ((opt = getopt_long(argc, argv, optstring, long_options, &option_index)) != EOF) { @@ -346,28 +371,29 @@ int main(int argc, char *argv[]) #ifdef STANDALONE fprintf(stderr, "Log file not supported in standalone mode. Aborting.\n"); cli_classic_abort_usage(); #else /* STANDALONE */ logfile = strdup(optarg); if (logfile[0] == '\0') { fprintf(stderr, "No log filename specified.\n"); cli_classic_abort_usage(); } #endif /* STANDALONE */ break; /* FIXME(hatim): For the following long options, not _all_ * of them are mutually exclusive per se (like wp_set_range - * and wp_enable makes sense). There is scope for improvement - * here, but for now let's treat each one as separate operation. */ + * and wp_enable, or read_otp and lock_otp makes sense). There + * is scope for improvement here, but for now let's treat each + * one as separate operation. */ case LONGOPT_PRINT_STATUSREG: if (++operation_specified > 1) { fprintf(stderr, "More than one operation " "specified. Aborting.\n"); cli_classic_abort_usage(); } print_status_reg = 1; break; case LONGOPT_PRINT_WP_STATUS: if (++operation_specified > 1) { fprintf(stderr, "More than one operation " "specified. Aborting.\n"); cli_classic_abort_usage(); @@ -399,38 +425,84 @@ int main(int argc, char *argv[]) cli_classic_abort_usage(); } wp_disable = 1; break; case LONGOPT_SET_BP_RANGE: if (++operation_specified > 1) { fprintf(stderr, "More than one operation " "specified. Aborting.\n"); cli_classic_abort_usage(); } wp_set_range_opt = strdup(optarg); wp_set_range = 1; break; + case LONGOPT_PRINT_OTP_STATUS: + if (++operation_specified > 1) { + fprintf(stderr, "More than one operation " + "specified. Aborting.\n"); + cli_classic_abort_usage(); + } + print_otp_status = 1; + break; + case LONGOPT_READ_OTP: + if (++operation_specified > 1) { + fprintf(stderr, "More than one operation " + "specified. Aborting.\n"); + cli_classic_abort_usage(); + } + read_otp_opt = strdup(optarg); + filename = extract_param(&read_otp_opt, "file", ","); + read_otp = 1; + break; + case LONGOPT_WRITE_OTP: + if (++operation_specified > 1) { + fprintf(stderr, "More than one operation " + "specified. Aborting.\n"); + cli_classic_abort_usage(); + } + write_otp_opt = strdup(optarg); + filename = extract_param(&write_otp_opt, "file", ","); + write_otp = 1; + break; + case LONGOPT_ERASE_OTP: + if (++operation_specified > 1) { + fprintf(stderr, "More than one operation " + "specified. Aborting.\n"); + cli_classic_abort_usage(); + } + erase_otp_opt = strdup(optarg); + erase_otp = 1; + break; + case LONGOPT_LOCK_OTP: + if (++operation_specified > 1) { + fprintf(stderr, "More than one operation " + "specified. Aborting.\n"); + cli_classic_abort_usage(); + } + lock_otp_opt = strdup(optarg); + lock_otp = 1; + break; default: cli_classic_abort_usage(); break; } }
if (optind < argc) { fprintf(stderr, "Error: Extra parameter found.\n"); cli_classic_abort_usage(); }
- if ((read_it | write_it | verify_it) && check_filename(filename, "image")) { + if ((read_it | write_it | verify_it | read_otp | write_otp) && check_filename(filename, "image")) { cli_classic_abort_usage(); } if (layoutfile && check_filename(layoutfile, "layout")) { cli_classic_abort_usage(); }
#ifndef STANDALONE if (logfile && check_filename(logfile, "log")) cli_classic_abort_usage(); if (logfile && open_logfile(logfile)) cli_classic_abort_usage(); #endif /* !STANDALONE */
@@ -588,125 +660,126 @@ int main(int argc, char *argv[]) } ret = 1; goto out_shutdown; } else if (!chip_to_probe) { /* repeat for convenience when looking at foreign logs */ tempstr = flashbuses_to_text(flashes[0].chip->bustype); msg_gdbg("Found %s flash chip "%s" (%d kB, %s).\n", flashes[0].chip->vendor, flashes[0].chip->name, flashes[0].chip->total_size, tempstr); free(tempstr); }
fill_flash = &flashes[0];
- print_chip_support_status(fill_flash->chip); + print_chip_support_status(fill_flash);
unsigned int limitexceeded = count_max_decode_exceedings(fill_flash); if (limitexceeded > 0 && !force) { enum chipbustype commonbuses = fill_flash->mst->buses_supported & fill_flash->chip->bustype;
/* Sometimes chip and programmer have more than one bus in common, * and the limit is not exceeded on all buses. Tell the user. */ if ((bitcount(commonbuses) > limitexceeded)) { msg_pdbg("There is at least one interface available which could support the size of\n" "the selected flash chip.\n"); } msg_cerr("This flash chip is too big for this programmer (--verbose/-V gives details).\n" "Use --force/-f to override at your own risk.\n"); ret = 1; goto out_shutdown; }
if (!(read_it | write_it | verify_it | erase_it | print_status_reg | - print_wp_status | wp_list | wp_enable | wp_disable | wp_set_range)) { + print_wp_status | wp_list | wp_enable | wp_disable | wp_set_range | + print_otp_status | read_otp | write_otp | erase_otp | lock_otp)) { msg_ginfo("No operations were specified.\n"); goto out_shutdown; }
/* Always verify write operations unless -n is used. */ if (write_it && !dont_verify_it) verify_it = 1;
/* Map the selected flash chip again. */ if (map_flash(fill_flash) != 0) { ret = 1; goto out_shutdown; }
if (print_status_reg) { verbose_screen++; if (fill_flash->chip->status_register) { for (enum status_register_num SRn = SR1; SRn <= top_status_register(fill_flash); SRn++) fill_flash->chip->status_register->print(fill_flash, SRn); fill_flash->chip->status_register->print_wp_mode(fill_flash); if (fill_flash->chip->wp) print_range_generic(fill_flash); } else - cli_statreg_wp_support(fill_flash); + cli_infra_support(fill_flash, "access protection"); goto out_shutdown; }
if (print_wp_status) { verbose_screen++; if (fill_flash->chip->status_register || fill_flash->chip->wp) { msg_ginfo("WP status -\n"); fill_flash->chip->status_register->print_wp_mode(fill_flash); if (fill_flash->chip->wp) print_range_generic(fill_flash); } else - cli_statreg_wp_support(fill_flash); + cli_infra_support(fill_flash, "access protection"); goto out_shutdown; }
if (wp_list) { verbose_screen++; if (fill_flash->chip->wp) { msg_ginfo("Valid write protection ranges for chip "%s" are -\n", fill_flash->chip->name); fill_flash->chip->wp->print_table(fill_flash); } else - cli_statreg_wp_support(fill_flash); + cli_infra_support(fill_flash, "access protection"); goto out_shutdown; }
if (wp_disable) { verbose_screen++; if (fill_flash->chip->wp) { ret = fill_flash->chip->wp->disable(fill_flash); } else - cli_statreg_wp_support(fill_flash); + cli_infra_support(fill_flash, "access protection"); goto out_shutdown; }
if (wp_set_range) { verbose_screen++; if (fill_flash->chip->wp) { char *endptr = NULL; uint8_t start_cmp, end_cmp, has_cmp = pos_bit(fill_flash, CMP) != -1; if (has_cmp) start_cmp = get_cmp(fill_flash); /* FIXME(hatim): Implement error checking */ uint32_t start = strtoul(extract_param(&wp_set_range_opt, "start", ","), &endptr, 0); uint32_t len = strtoul(extract_param(&wp_set_range_opt, "len", ","), &endptr, 0); msg_ginfo("Trying to protect %d kB starting from address 0x%06x...\n", len, start); ret = fill_flash->chip->wp->set_range(fill_flash, start, len); if (has_cmp && start_cmp != (end_cmp = get_cmp(fill_flash))) msg_ginfo("CMP bit was %sset\n", end_cmp ? "" : "un"); if (ret) msg_gerr("Failed to protect\n"); else msg_ginfo("Protection successful!\n"); } else - cli_statreg_wp_support(fill_flash); + cli_infra_support(fill_flash, "access protection"); goto out_shutdown; }
if (wp_enable) { verbose_screen++; if (fill_flash->chip->status_register) { enum wp_mode wp_mode = WP_MODE_HARDWARE_UNPROTECTED;
if (wp_mode_opt) { if (!strcasecmp(wp_mode_opt, "PERMANENT")) { wp_mode = WP_MODE_PERMANENT; } else if (!strcasecmp(wp_mode_opt, "POWER_CYCLE")) { wp_mode = WP_MODE_POWER_CYCLE; @@ -717,27 +790,187 @@ int main(int argc, char *argv[]) } else { msg_gerr("Invalid write protection mode for status register(s)\n"); cli_classic_abort_usage(); ret = 1; goto out_shutdown; } } msg_ginfo("Setting write protection mode to %s ...\n", wp_mode_opt ? wp_mode_opt : "HARDWARE"); fill_flash->chip->status_register->set_wp_mode(fill_flash, wp_mode); ret = !(wp_mode == fill_flash->chip->status_register->get_wp_mode(fill_flash)); msg_gerr("%s\n", ret ? "Failed" : "Success"); } else - cli_statreg_wp_support(fill_flash); + cli_infra_support(fill_flash, "access protection"); + goto out_shutdown; + } + + if (print_otp_status) { + verbose_screen++; + if (fill_flash->chip->otp) { + msg_ginfo("OTP status -\n"); + ret = fill_flash->chip->otp->print_status(fill_flash); + } else + cli_infra_support(fill_flash, "OTP"); + + goto out_shutdown; + } + + if (read_otp) { + verbose_screen++; + if (fill_flash->chip->otp) { + char *otp_region_opt = extract_param(&read_otp_opt, "reg", ","); + enum otp_region otp_region = OTP_REG_1; + if (otp_region_opt) { + char *endptr = NULL; + // FIXME(hatim): Implement error-checking (?) + otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1; + } else + msg_gdbg("OTP region not specified, using default region 1\n"); + + uint32_t len = fill_flash->chip->otp->region[otp_region].size; + uint8_t *buf = calloc(len, sizeof(uint8_t)); + if (!buf) { + msg_gerr("Memory allocation failed\n"); + ret = 1; + goto out_shutdown; + } + if (!fill_flash->chip->otp->read) { + msg_gerr("No OTP read function available for "%s"\n", + fill_flash->chip->name); + ret = 1; + free(buf); + goto out_shutdown; + } + msg_gdbg("Reading OTP memory...\n"); + ret = fill_flash->chip->otp->read(fill_flash, buf, otp_region, 0x000000, len); + if (ret) { + msg_gerr("Reading OTP memory failed\n"); + free(buf); + goto out_shutdown; + } + ret = write_buf_to_file(buf, len, filename); + } else + cli_infra_support(fill_flash, "OTP"); + + goto out_shutdown; + } + + if (write_otp) { + verbose_screen++; + if (fill_flash->chip->otp) { + char *otp_region_opt = extract_param(&write_otp_opt, "reg", ","); + enum otp_region otp_region = OTP_REG_1; + if (otp_region_opt) { + char *endptr = NULL; + // FIXME(hatim): Implement error-checking (?) + otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1; + } else + msg_gdbg("OTP region not specified, using default region 1\n"); + + uint32_t len = fill_flash->chip->otp->region[otp_region].size; + uint8_t *buf = calloc(len, sizeof(uint8_t)); + if (!buf) { + msg_gerr("Memory allocation failed\n"); + ret = 1; + goto out_shutdown; + } + + ret = read_buf_from_file(buf, len, filename); + if (ret) { + msg_gerr("Error reading from file "%s", failed\n", filename); + free(buf); + goto out_shutdown; + } + msg_gdbg("Reading form file "%s" complete\n", filename); + if (!fill_flash->chip->otp->write) { + msg_gerr("No OTP write function available for "%s"\n", + fill_flash->chip->name); + ret = 1; + free(buf); + goto out_shutdown; + } + + msg_gdbg("Erasing OTP memory...\n"); + ret = fill_flash->chip->otp->erase(fill_flash, otp_region); + if (ret) { + msg_gerr("Erasing OTP memory failed\n"); + free(buf); + goto out_shutdown; + } + msg_gdbg("Erasing OTP memory done\n"); + + msg_gdbg("Writing OTP memory...\n"); + ret = fill_flash->chip->otp->write(fill_flash, buf, otp_region, 0x000000, len); + if (ret) { + msg_gerr("Writing OTP memory failed\n"); + free(buf); + goto out_shutdown; + } + msg_gdbg("Writing OTP memory done\n"); + // FIXME(hatim): Verify written contents + } else + cli_infra_support(fill_flash, "OTP"); + + goto out_shutdown; + } + + if (erase_otp) { + verbose_screen++; + if (fill_flash->chip->otp) { + char *otp_region_opt = NULL; + enum otp_region otp_region = OTP_REG_1; + if (!erase_otp_opt && (otp_region_opt = extract_param(&read_otp_opt, "reg", ","))) { + char *endptr = NULL; + // FIXME(hatim): Implement error-checking (?) + otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1; + } else + msg_gdbg("OTP region not specified, using default region 1\n"); + + msg_gdbg("Erasing OTP memory ...\n"); + ret = fill_flash->chip->otp->erase(fill_flash, otp_region); + if (ret) { + msg_gerr("Erasing OTP memory failed\n"); + goto out_shutdown; + } + msg_gdbg("Erasing OTP memory done\n"); + } else + cli_infra_support(fill_flash, "OTP"); + + goto out_shutdown; + } + + if (lock_otp) { + verbose_screen++; + if (fill_flash->chip->otp) { + char *otp_region_opt = NULL; + enum otp_region otp_region = OTP_REG_1; + if (!lock_otp_opt && (otp_region_opt = extract_param(&read_otp_opt, "reg", ","))) { + char *endptr = NULL; + // FIXME(hatim): Implement error-checking (?) + otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1; + } else + msg_gdbg("OTP region not specified, using default region 1\n"); + + msg_gdbg("Trying to lock OTP memory...\n"); + ret = fill_flash->chip->otp->lock(fill_flash, otp_region); + if (ret) { + msg_gerr("Failed to lock\n"); + goto out_shutdown; + } + msg_gdbg("OTP memory locked\n"); + } else + cli_infra_support(fill_flash, "OTP"); + goto out_shutdown; }
/* FIXME: We should issue an unconditional chip reset here. This can be * done once we have a .reset function in struct flashchip. * Give the chip time to settle. */ programmer_delay(100000); ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
unmap_flash(fill_flash); out_shutdown: programmer_shutdown(); diff --git a/cli_common.c b/cli_common.c index 71cc2dd..f0f23c9 100644 --- a/cli_common.c +++ b/cli_common.c @@ -48,70 +48,72 @@ char *flashbuses_to_text(enum chipbustype bustype) ret = strcat_realloc(ret, "SPI, "); if (bustype & BUS_PROG) ret = strcat_realloc(ret, "Programmer-specific, "); if (bustype == BUS_NONE) ret = strcat_realloc(ret, "None, "); } /* Kill last comma. */ ret[strlen(ret) - 2] = '\0'; ret = realloc(ret, strlen(ret) + 1); return ret; }
-void print_chip_support_status(const struct flashchip *chip) +void print_chip_support_status(struct flashctx *flash) { - if (chip->feature_bits & FEATURE_OTP) { - msg_cdbg("This chip may contain one-time programmable memory. flashrom cannot read\n" - "and may never be able to write it, hence it may not be able to completely\n" - "clone the contents of this chip (see man page for details).\n"); + if (flash->chip->otp) { + flash->chip->otp->print_status(flash); + } else if (flash->chip->feature_bits & FEATURE_OTP) { + msg_cdbg("This chip may contain one-time programmable memory. flashrom may be able\n" + "to read, write, erase and/or lock it, if OTP infrastructure support is added.\n" + "You could add support and send the patch to flashrom@flashrom.org\n"); }
- if ((chip->tested.erase == NA) && (chip->tested.write == NA)) { + if ((flash->chip->tested.erase == NA) && (flash->chip->tested.write == NA)) { msg_cdbg("This chip's main memory can not be erased/written by design.\n"); }
- if ((chip->tested.probe == BAD) || (chip->tested.probe == NT) || - (chip->tested.read == BAD) || (chip->tested.read == NT) || - (chip->tested.erase == BAD) || (chip->tested.erase == NT) || - (chip->tested.write == BAD) || (chip->tested.write == NT)){ + if ((flash->chip->tested.probe == BAD) || (flash->chip->tested.probe == NT) || + (flash->chip->tested.read == BAD) || (flash->chip->tested.read == NT) || + (flash->chip->tested.erase == BAD) || (flash->chip->tested.erase == NT) || + (flash->chip->tested.write == BAD) || (flash->chip->tested.write == NT)){ msg_cinfo("===\n"); - if ((chip->tested.probe == BAD) || - (chip->tested.read == BAD) || - (chip->tested.erase == BAD) || - (chip->tested.write == BAD)) { + if ((flash->chip->tested.probe == BAD) || + (flash->chip->tested.read == BAD) || + (flash->chip->tested.erase == BAD) || + (flash->chip->tested.write == BAD)) { msg_cinfo("This flash part has status NOT WORKING for operations:"); - if (chip->tested.probe == BAD) + if (flash->chip->tested.probe == BAD) msg_cinfo(" PROBE"); - if (chip->tested.read == BAD) + if (flash->chip->tested.read == BAD) msg_cinfo(" READ"); - if (chip->tested.erase == BAD) + if (flash->chip->tested.erase == BAD) msg_cinfo(" ERASE"); - if (chip->tested.write == BAD) + if (flash->chip->tested.write == BAD) msg_cinfo(" WRITE"); msg_cinfo("\n"); } - if ((chip->tested.probe == NT) || - (chip->tested.read == NT) || - (chip->tested.erase == NT) || - (chip->tested.write == NT)) { + if ((flash->chip->tested.probe == NT) || + (flash->chip->tested.read == NT) || + (flash->chip->tested.erase == NT) || + (flash->chip->tested.write == NT)) { msg_cinfo("This flash part has status UNTESTED for operations:"); - if (chip->tested.probe == NT) + if (flash->chip->tested.probe == NT) msg_cinfo(" PROBE"); - if (chip->tested.read == NT) + if (flash->chip->tested.read == NT) msg_cinfo(" READ"); - if (chip->tested.erase == NT) + if (flash->chip->tested.erase == NT) msg_cinfo(" ERASE"); - if (chip->tested.write == NT) + if (flash->chip->tested.write == NT) msg_cinfo(" WRITE"); msg_cinfo("\n"); } msg_cinfo("The test status of this chip may have been updated in the latest development\n" "version of flashrom. If you are running the latest development version,\n" "please email a report to flashrom@flashrom.org if any of the above operations\n" "work correctly for you with this flash chip. Please include the flashrom log\n" "file for all operations you tested (see the man page for details), and mention\n" "which mainboard or programmer you tested in the subject line.\n" "Thanks for your help!\n"); } }
diff --git a/flash.h b/flash.h index c37426c..61c06c3 100644 --- a/flash.h +++ b/flash.h @@ -355,27 +355,27 @@ int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *
/* Something happened that shouldn't happen, we'll abort. */ #define ERROR_FATAL -0xee #define ERROR_FLASHROM_BUG -200 /* We reached one of the hardcoded limits of flashrom. This can be fixed by * increasing the limit of a compile-time allocation or by switching to dynamic * allocation. * Note: If this warning is triggered, check first for runaway registrations. */ #define ERROR_FLASHROM_LIMIT -201
/* cli_common.c */ char *flashbuses_to_text(enum chipbustype bustype); -void print_chip_support_status(const struct flashchip *chip); +void print_chip_support_status(struct flashctx *flash);
/* cli_output.c */ extern int verbose_screen; extern int verbose_logfile; #ifndef STANDALONE int open_logfile(const char * const filename); int close_logfile(void); void start_logging(void); #endif enum msglevel { MSG_ERROR = 0, MSG_WARN = 1, MSG_INFO = 2, diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index d068ad9..9ed2fcc 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -43,26 +43,29 @@ .TH FLASHROM 8 "2016-03-13" "0.9.9-unknown" .SH NAME flashrom - detect, read, write, verify and erase flash chips .SH SYNOPSIS .B flashrom \fR[\fB-h\fR|\fB-R\fR|\fB-L\fR|\fB-z\fR|\ \fB-p\fR <programmername>[:<parameters>] [\fB-E\fR|\fB-r\fR <file>|\fB-w\fR <file>|\fB-v\fR <file>] \ [\fB-c\fR <chipname>] [\fB-l\fR <file> [\fB-i\fR <image>]] [\fB-n\fR] [\fB-f\fR]] [\fB-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>] [\fB--print-status-reg\fR] [\fB--print-wp-status\fR] [\fB--wp-list\fR] [\fB--wp-enable\fR[=<MODE>]] [\fB--wp-disable\fR] [\fB--wp-set-range\fR start=<start>,len=<len>] + [\fB--print-otp-status\fR] [\fB--read-otp\fR file=<file>[,reg=<region>]] + [\fB--erase-otp\fR [reg=<region>]] [\fB--lock-otp\fR [reg=<region>]] + [\fB--write-otp\fR file=<file>[,reg=<region>]] .SH DESCRIPTION .B flashrom is a utility for detecting, reading, writing, verifying and erasing flash chips. It's often used to flash BIOS/EFI/coreboot/firmware images in-system using a supported mainboard. However, it also supports various external PCI/USB/parallel-port/serial-port based devices which can program flash chips, including some network cards (NICs), SATA/IDE controller cards, graphics cards, the Bus Pirate device, various FTDI FT2232/FT4232H/FT232H based USB devices, and more. .PP It supports a wide range of DIP32, PLCC32, DIP8, SO8/SOIC8, TSOP32, TSOP40, TSOP48, and BGA chips, which use various protocols such as LPC, FWH, parallel flash, or SPI. .SH OPTIONS @@ -167,30 +170,75 @@ mode will lock the status register(s) until the next power down-up cycle. \ After the cycle, SOFTWARE mode will be in effect. .sp * .B "SOFTWARE" mode allows writes to status register(s) irrespective of level of WP# pin. .TP .B "--wp-disable" Disable any write protection of status register(s) in effect. SOFTWARE mode \ will be applied after disabling. .TP .B "--wp-set-range start=<start>,len=<len>" Configure status register(s) to protect .B "<len>" - kB of memory starting from address -.B "<start>". +kB of memory starting from address +.B "<start>." Both start and len must be supplied. (Consider setting a write protection \ mode to prevent against configuration changes to status register(s).) + +.TP +.B "--print-otp-status" +Print details of OTP memory region(s) for the chip along with corresponding \ +modifier bits +.TP +.B "--read-otp file=<file>[,reg=<reg>]" +Read OTP memory region +.B "<reg>" +of the chip, and save to +.B "<file>." +If +.B "<reg>" +is ommitted, then it defaults to the first OTP region. +.sp +Example: +.sp +.B " flashrom -p prog -c chip --read-otp file=read_otp.bin,reg=2" +.sp +will read the the 2nd OTP region and save it to +.B "read_otp.bin." +.TP +.B "--write-otp file=<file>[,reg=<reg>]" +Write +.B "<file>" +to OTP memory region +.B "<reg>." +of the chip. If +.B "<reg>" +is ommitted, then it defaults to the first OTP region. +.TP +.B "--erase-otp [reg=<reg>]" +Erase OTP memory region +.B "<reg>." +of the chip. If +.B "<reg>" +is ommitted, then it defaults to the first OTP region. +.TP +.B "--lock-otp [reg=<reg>]" +Lock OTP memory region +.B "<reg>." +of the chip against any future writes (Please be cautious, this action is \ +almost always irreversible). If +.B "<reg>" +is ommitted, then it defaults to the first OTP region. .TP .B "-f, --force" Force one or more of the following actions: .sp * Force chip read and pretend the chip is there. .sp * Force chip access even if the chip is bigger than the maximum supported \ size for the flash bus. .sp * Force erase even if erase is known bad. .sp * Force write even if write is known bad. .TP