mail.coreboot.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
May
April
March
February
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
List overview
Download
flashrom-gerrit
February 2021
----- 2025 -----
May 2025
April 2025
March 2025
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
flashrom-gerrit@flashrom.org
1 participants
155 discussions
Start a n
N
ew thread
Change in flashrom[master]: flashchips: Add W25Q64.V.M
by Keith Hui (Code Review)
26 Dec '21
26 Dec '21
Keith Hui has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/38578
) Change subject: flashchips: Add W25Q64.V.M ...................................................................... flashchips: Add W25Q64.V.M Add Winbond W25Q64JV?M SPI flash chip. Some tests were done on a W25Q64JVSSIM mounted on a Sparkfun SOIC-DIP adapter, connected to an Arduino Mega 2560 r3 loaded with frser-duino. Probe and read works, but instability were observed in the connection between Arduino and the chip, so this is being submitted as untested. Datasheet:
https://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf
Change-Id: Ib7749835178ee6a1b0fe579e7a0c29e69a67933b Signed-off-by: Keith Hui <buurin(a)gmail.com> --- M flashchips.c M flashchips.h 2 files changed, 41 insertions(+), 0 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/78/38578/1 diff --git a/flashchips.c b/flashchips.c index 65dda0e..bfa2927 100644 --- a/flashchips.c +++ b/flashchips.c @@ -16936,6 +16936,46 @@ { .vendor = "Winbond", + .name = "W25Q64.V.M", + .bustype = BUS_SPI, + .manufacture_id = WINBOND_NEX_ID, + .model_id = WINBOND_NEX_W25Q64_V_M, + .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 | FEATURE_QPI, + .tested = TEST_UNTESTED, + .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} }, + .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, + .unlock = spi_disable_blockprotect, + .write = spi_chip_write_256, + .read = spi_chip_read, + .voltage = {2700, 3600}, + }, + + { + .vendor = "Winbond", .name = "W25Q64.W", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID, diff --git a/flashchips.h b/flashchips.h index 14ab6de..bdfadad 100644 --- a/flashchips.h +++ b/flashchips.h @@ -938,6 +938,7 @@ #define WINBOND_NEX_W25Q32_W 0x6016 /* W25Q32DW; W25Q32FV in QPI mode */ #define WINBOND_NEX_W25Q64_W 0x6017 /* W25Q64DW; W25Q64FV in QPI mode */ #define WINBOND_NEX_W25Q128_W 0x6018 /* W25Q128FW; W25Q128FV in QPI mode */ +#define WINBOND_NEX_W25Q64_V_M 0x7017 /* W25Q64JVSIM */ #define WINBOND_NEX_W25Q128_V_M 0x7018 /* W25Q128JVSM */ #define WINBOND_NEX_W25Q256JV_M 0x7019 /* W25Q256JV_M (QE=0) */ #define WINBOND_NEX_W25Q128_DTR 0x8018 /* W25Q128JW_DTR */ -- To view, visit
https://review.coreboot.org/c/flashrom/+/38578
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: Ib7749835178ee6a1b0fe579e7a0c29e69a67933b Gerrit-Change-Number: 38578 Gerrit-PatchSet: 1 Gerrit-Owner: Keith Hui <buurin(a)gmail.com> Gerrit-MessageType: newchange
2
9
0
0
Change in flashrom[master]: writeprotect.h,c: Add Winbond support (WIP)
by Edward O'Callaghan (Code Review)
06 Dec '21
06 Dec '21
Edward O'Callaghan has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/45572
) Change subject: writeprotect.h,c: Add Winbond support (WIP) ...................................................................... writeprotect.h,c: Add Winbond support (WIP) Adds Winbond support. WIP Change-Id: I54eedaa8e24c69d6d088e9ebcfb9a284e0fbe893 Signed-off-by: Edward O'Callaghan <quasisec(a)google.com> --- M writeprotect.c M writeprotect.h 2 files changed, 1,068 insertions(+), 0 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/72/45572/1 diff --git a/writeprotect.c b/writeprotect.c index 0cef19b..1cd3950 100644 --- a/writeprotect.c +++ b/writeprotect.c @@ -87,6 +87,43 @@ struct modifier_bits *m); }; +struct w25q_status { + /* this maps to register layout -- do not change ordering */ + unsigned char busy : 1; + unsigned char wel : 1; + unsigned char bp0 : 1; + unsigned char bp1 : 1; + unsigned char bp2 : 1; + unsigned char tb : 1; + unsigned char sec : 1; + unsigned char srp0 : 1; +} __attribute__ ((packed)); + +/* Status register for large flash layouts with 4 BP bits */ +struct w25q_status_large { + unsigned char busy : 1; + unsigned char wel : 1; + unsigned char bp0 : 1; + unsigned char bp1 : 1; + unsigned char bp2 : 1; + unsigned char bp3 : 1; + unsigned char tb : 1; + unsigned char srp0 : 1; +} __attribute__ ((packed)); + +struct w25q_status_2 { + unsigned char srp1 : 1; + unsigned char qe : 1; + unsigned char rsvd : 6; +} __attribute__ ((packed)); + +int w25_range_to_status(const struct flashctx *flash, + unsigned int start, unsigned int len, + struct w25q_status *status); +int w25_status_to_range(const struct flashctx *flash, + const struct w25q_status *status, + unsigned int *start, unsigned int *len); + /* * Mask to extract write-protect enable and range bits * Status register 1: @@ -100,6 +137,295 @@ #define MASK_WP_AREA_LARGE (0x9C) #define MASK_WP2_AREA (0x01) +static struct wp_range_descriptor w25q16_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */ + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x1f0000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x2, {0x1e0000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x3, {0x1c0000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x4, {0x180000, 512 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x5, {0x100000, 1024 * 1024} }, + + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} }, + { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 2048 * 1024} }, + { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 2048 * 1024} }, + + { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} }, + + { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, +}; + +static struct wp_range_descriptor w25q32_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */ + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x3f0000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x2, {0x3e0000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x3, {0x3c0000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x4, {0x380000, 512 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x5, {0x300000, 1024 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x6, {0x200000, 2048 * 1024} }, + + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 1024 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 2048 * 1024} }, + { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 4096 * 1024} }, + + { .m = { .sec = 1, .tb = 0 }, 0x1, {0x3ff000, 4 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x2, {0x3fe000, 8 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x3, {0x3fc000, 16 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x4, {0x3f8000, 32 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x5, {0x3f8000, 32 * 1024} }, + + { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, +}; + +static struct wp_range_descriptor w25q80_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */ + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0f0000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0e0000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0c0000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} }, + + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} }, + { .m = { .sec = X, .tb = X }, 0x6, {0x000000, 1024 * 1024} }, + { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 1024 * 1024} }, + + { .m = { .sec = 1, .tb = 0 }, 0x1, {0x1ff000, 4 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x2, {0x1fe000, 8 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x3, {0x1fc000, 16 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x4, {0x1f8000, 32 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x5, {0x1f8000, 32 * 1024} }, + + { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, +}; + +static struct wp_range_descriptor w25q64_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */ + + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x7e0000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x2, {0x7c0000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x3, {0x780000, 512 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x4, {0x700000, 1024 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x5, {0x600000, 2048 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x6, {0x400000, 4096 * 1024} }, + + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 512 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 1024 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 2048 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 4096 * 1024} }, + { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 8192 * 1024} }, + + { .m = { .sec = 1, .tb = 0 }, 0x1, {0x7ff000, 4 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x2, {0x7fe000, 8 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x3, {0x7fc000, 16 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x4, {0x7f8000, 32 * 1024} }, + { .m = { .sec = 1, .tb = 0 }, 0x5, {0x7f8000, 32 * 1024} }, + + { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, + { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, +}; + +static struct wp_range_descriptor w25rq128_cmp0_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* NONE */ + + { .m = { .sec = 0, .tb = 0 }, 0x1, {0xfc0000, 256 * 1024} }, /* Upper 1/64 */ + { .m = { .sec = 0, .tb = 0 }, 0x2, {0xf80000, 512 * 1024} }, /* Upper 1/32 */ + { .m = { .sec = 0, .tb = 0 }, 0x3, {0xf00000, 1024 * 1024} }, /* Upper 1/16 */ + { .m = { .sec = 0, .tb = 0 }, 0x4, {0xe00000, 2048 * 1024} }, /* Upper 1/8 */ + { .m = { .sec = 0, .tb = 0 }, 0x5, {0xc00000, 4096 * 1024} }, /* Upper 1/4 */ + { .m = { .sec = 0, .tb = 0 }, 0x6, {0x800000, 8192 * 1024} }, /* Upper 1/2 */ + + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 256 * 1024} }, /* Lower 1/64 */ + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 512 * 1024} }, /* Lower 1/32 */ + { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 1024 * 1024} }, /* Lower 1/16 */ + { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 2048 * 1024} }, /* Lower 1/8 */ + { .m = { .sec = 0, .tb = 1 }, 0x5, {0x000000, 4096 * 1024} }, /* Lower 1/4 */ + { .m = { .sec = 0, .tb = 1 }, 0x6, {0x000000, 8192 * 1024} }, /* Lower 1/2 */ + + { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 16384 * 1024} }, /* ALL */ + + { .m = { .sec = 1, .tb = 0 }, 0x1, {0xfff000, 4 * 1024} }, /* Upper 1/4096 */ + { .m = { .sec = 1, .tb = 0 }, 0x2, {0xffe000, 8 * 1024} }, /* Upper 1/2048 */ + { .m = { .sec = 1, .tb = 0 }, 0x3, {0xffc000, 16 * 1024} }, /* Upper 1/1024 */ + { .m = { .sec = 1, .tb = 0 }, 0x4, {0xff8000, 32 * 1024} }, /* Upper 1/512 */ + { .m = { .sec = 1, .tb = 0 }, 0x5, {0xff8000, 32 * 1024} }, /* Upper 1/512 */ + + { .m = { .sec = 1, .tb = 1 }, 0x1, {0x000000, 4 * 1024} }, /* Lower 1/4096 */ + { .m = { .sec = 1, .tb = 1 }, 0x2, {0x000000, 8 * 1024} }, /* Lower 1/2048 */ + { .m = { .sec = 1, .tb = 1 }, 0x3, {0x000000, 16 * 1024} }, /* Lower 1/1024 */ + { .m = { .sec = 1, .tb = 1 }, 0x4, {0x000000, 32 * 1024} }, /* Lower 1/512 */ + { .m = { .sec = 1, .tb = 1 }, 0x5, {0x000000, 32 * 1024} }, /* Lower 1/512 */ +}; + +static struct wp_range_descriptor w25rq128_cmp1_ranges[] = { + { .m = { .sec = X, .tb = X }, 0x0, {0x000000, 16 * 1024 * 1024} }, /* ALL */ + + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x000000, 16128 * 1024} }, /* Lower 63/64 */ + { .m = { .sec = 0, .tb = 0 }, 0x2, {0x000000, 15872 * 1024} }, /* Lower 31/32 */ + { .m = { .sec = 0, .tb = 0 }, 0x3, {0x000000, 15 * 1024 * 1024} }, /* Lower 15/16 */ + { .m = { .sec = 0, .tb = 0 }, 0x4, {0x000000, 14 * 1024 * 1024} }, /* Lower 7/8 */ + { .m = { .sec = 0, .tb = 0 }, 0x5, {0x000000, 12 * 1024 * 1024} }, /* Lower 3/4 */ + { .m = { .sec = 0, .tb = 0 }, 0x6, {0x000000, 8 * 1024 * 1024} }, /* Lower 1/2 */ + + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x040000, 16128 * 1024} }, /* Upper 63/64 */ + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x080000, 15872 * 1024} }, /* Upper 31/32 */ + { .m = { .sec = 0, .tb = 1 }, 0x3, {0x100000, 15 * 1024 * 1024} }, /* Upper 15/16 */ + { .m = { .sec = 0, .tb = 1 }, 0x4, {0x200000, 14 * 1024 * 1024} }, /* Upper 7/8 */ + { .m = { .sec = 0, .tb = 1 }, 0x5, {0x400000, 12 * 1024 * 1024} }, /* Upper 3/4 */ + { .m = { .sec = 0, .tb = 1 }, 0x6, {0x800000, 8 * 1024 * 1024} }, /* Upper 1/2 */ + + { .m = { .sec = X, .tb = X }, 0x7, {0x000000, 0} }, /* NONE */ + + { .m = { .sec = 1, .tb = 0 }, 0x1, {0x000000, 16380 * 1024} }, /* Lower 4095/4096 */ + { .m = { .sec = 1, .tb = 0 }, 0x2, {0x000000, 16376 * 1024} }, /* Lower 2048/2048 */ + { .m = { .sec = 1, .tb = 0 }, 0x3, {0x000000, 16368 * 1024} }, /* Lower 1023/1024 */ + { .m = { .sec = 1, .tb = 0 }, 0x4, {0x000000, 16352 * 1024} }, /* Lower 511/512 */ + { .m = { .sec = 1, .tb = 0 }, 0x5, {0x000000, 16352 * 1024} }, /* Lower 511/512 */ + + { .m = { .sec = 1, .tb = 1 }, 0x1, {0x001000, 16380 * 1024} }, /* Upper 4095/4096 */ + { .m = { .sec = 1, .tb = 1 }, 0x2, {0x002000, 16376 * 1024} }, /* Upper 2047/2048 */ + { .m = { .sec = 1, .tb = 1 }, 0x3, {0x004000, 16368 * 1024} }, /* Upper 1023/1024 */ + { .m = { .sec = 1, .tb = 1 }, 0x4, {0x008000, 16352 * 1024} }, /* Upper 511/512 */ + { .m = { .sec = 1, .tb = 1 }, 0x5, {0x008000, 16352 * 1024} }, /* Upper 511/512 */ +}; + +static struct wp_range_descriptor w25rq256_cmp0_ranges[] = { + { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 0x0000000} }, /* NONE */ + + { .m = { .sec = X, .tb = 0 }, 0x1, {0x1ff0000, 64 * 1 * 1024} }, /* Upper 1/512 */ + { .m = { .sec = X, .tb = 0 }, 0x2, {0x1fe0000, 64 * 2 * 1024} }, /* Upper 1/256 */ + { .m = { .sec = X, .tb = 0 }, 0x3, {0x1fc0000, 64 * 4 * 1024} }, /* Upper 1/128 */ + { .m = { .sec = X, .tb = 0 }, 0x4, {0x1f80000, 64 * 8 * 1024} }, /* Upper 1/64 */ + { .m = { .sec = X, .tb = 0 }, 0x5, {0x1f00000, 64 * 16 * 1024} }, /* Upper 1/32 */ + { .m = { .sec = X, .tb = 0 }, 0x6, {0x1e00000, 64 * 32 * 1024} }, /* Upper 1/16 */ + { .m = { .sec = X, .tb = 0 }, 0x7, {0x1c00000, 64 * 64 * 1024} }, /* Upper 1/8 */ + { .m = { .sec = X, .tb = 0 }, 0x8, {0x1800000, 64 * 128 * 1024} }, /* Upper 1/4 */ + { .m = { .sec = X, .tb = 0 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */ + + { .m = { .sec = X, .tb = 1 }, 0x1, {0x0000000, 64 * 1 * 1024} }, /* Lower 1/512 */ + { .m = { .sec = X, .tb = 1 }, 0x2, {0x0000000, 64 * 2 * 1024} }, /* Lower 1/256 */ + { .m = { .sec = X, .tb = 1 }, 0x3, {0x0000000, 64 * 4 * 1024} }, /* Lower 1/128 */ + { .m = { .sec = X, .tb = 1 }, 0x4, {0x0000000, 64 * 8 * 1024} }, /* Lower 1/64 */ + { .m = { .sec = X, .tb = 1 }, 0x5, {0x0000000, 64 * 16 * 1024} }, /* Lower 1/32 */ + { .m = { .sec = X, .tb = 1 }, 0x6, {0x0000000, 64 * 32 * 1024} }, /* Lower 1/16 */ + { .m = { .sec = X, .tb = 1 }, 0x7, {0x0000000, 64 * 64 * 1024} }, /* Lower 1/8 */ + { .m = { .sec = X, .tb = 1 }, 0x8, {0x0000000, 64 * 128 * 1024} }, /* Lower 1/4 */ + { .m = { .sec = X, .tb = 1 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */ + + { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 64 * 512 * 1024} }, /* ALL */ + { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 64 * 512 * 1024} }, /* ALL */ + { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 64 * 512 * 1024} }, /* ALL */ + { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 64 * 512 * 1024} }, /* ALL */ + { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 64 * 512 * 1024} }, /* ALL */ + { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 64 * 512 * 1024} }, /* ALL */ +}; + +static struct wp_range_descriptor w25rq256_cmp1_ranges[] = { + { .m = { .sec = X, .tb = X }, 0x0, {0x0000000, 64 * 512 * 1024} }, /* ALL */ + + { .m = { .sec = X, .tb = 0 }, 0x1, {0x0000000, 64 * 511 * 1024} }, /* Lower 511/512 */ + { .m = { .sec = X, .tb = 0 }, 0x2, {0x0000000, 64 * 510 * 1024} }, /* Lower 255/256 */ + { .m = { .sec = X, .tb = 0 }, 0x3, {0x0000000, 64 * 508 * 1024} }, /* Lower 127/128 */ + { .m = { .sec = X, .tb = 0 }, 0x4, {0x0000000, 64 * 504 * 1024} }, /* Lower 63/64 */ + { .m = { .sec = X, .tb = 0 }, 0x5, {0x0000000, 64 * 496 * 1024} }, /* Lower 31/32 */ + { .m = { .sec = X, .tb = 0 }, 0x6, {0x0000000, 64 * 480 * 1024} }, /* Lower 15/16 */ + { .m = { .sec = X, .tb = 0 }, 0x7, {0x0000000, 64 * 448 * 1024} }, /* Lower 7/8 */ + { .m = { .sec = X, .tb = 0 }, 0x8, {0x0000000, 64 * 384 * 1024} }, /* Lower 3/4 */ + { .m = { .sec = X, .tb = 0 }, 0x9, {0x0000000, 64 * 256 * 1024} }, /* Lower 1/2 */ + + { .m = { .sec = X, .tb = 1 }, 0x1, {0x0010000, 64 * 511 * 1024} }, /* Upper 511/512 */ + { .m = { .sec = X, .tb = 1 }, 0x2, {0x0020000, 64 * 510 * 1024} }, /* Upper 255/256 */ + { .m = { .sec = X, .tb = 1 }, 0x3, {0x0040000, 64 * 508 * 1024} }, /* Upper 127/128 */ + { .m = { .sec = X, .tb = 1 }, 0x4, {0x0080000, 64 * 504 * 1024} }, /* Upper 63/64 */ + { .m = { .sec = X, .tb = 1 }, 0x5, {0x0100000, 64 * 496 * 1024} }, /* Upper 31/32 */ + { .m = { .sec = X, .tb = 1 }, 0x6, {0x0200000, 64 * 480 * 1024} }, /* Upper 15/16 */ + { .m = { .sec = X, .tb = 1 }, 0x7, {0x0400000, 64 * 448 * 1024} }, /* Upper 7/8 */ + { .m = { .sec = X, .tb = 1 }, 0x8, {0x0800000, 64 * 384 * 1024} }, /* Upper 3/4 */ + { .m = { .sec = X, .tb = 1 }, 0x9, {0x1000000, 64 * 256 * 1024} }, /* Upper 1/2 */ + + { .m = { .sec = X, .tb = X }, 0xa, {0x0000000, 0x0000000} }, /* NONE */ + { .m = { .sec = X, .tb = X }, 0xb, {0x0000000, 0x0000000} }, /* NONE */ + { .m = { .sec = X, .tb = X }, 0xc, {0x0000000, 0x0000000} }, /* NONE */ + { .m = { .sec = X, .tb = X }, 0xd, {0x0000000, 0x0000000} }, /* NONE */ + { .m = { .sec = X, .tb = X }, 0xe, {0x0000000, 0x0000000} }, /* NONE */ + { .m = { .sec = X, .tb = X }, 0xf, {0x0000000, 0x0000000} }, /* NONE */ +}; + +struct wp_range_descriptor w25x10_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */ + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x010000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, + { .m = { .sec = X, .tb = X }, 0x2, {0x000000, 128 * 1024} }, + { .m = { .sec = X, .tb = X }, 0x3, {0x000000, 128 * 1024} }, +}; + +struct wp_range_descriptor w25x20_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */ + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x030000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x2, {0x020000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} }, + { .m = { .sec = 0, .tb = X }, 0x3, {0x000000, 256 * 1024} }, +}; + +struct wp_range_descriptor w25x40_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */ + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x070000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x2, {0x060000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x3, {0x040000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} }, + { .m = { .sec = 0, .tb = X }, 0x4, {0x000000, 512 * 1024} }, + { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 512 * 1024} }, + { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 512 * 1024} }, + { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 512 * 1024} }, +}; + +struct wp_range_descriptor w25x80_ranges[] = { + { .m = { .sec = X, .tb = X }, 0, {0, 0} }, /* none */ + { .m = { .sec = 0, .tb = 0 }, 0x1, {0x0F0000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x2, {0x0E0000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x3, {0x0C0000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 0 }, 0x4, {0x080000, 512 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x1, {0x000000, 64 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x2, {0x000000, 128 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x3, {0x000000, 256 * 1024} }, + { .m = { .sec = 0, .tb = 1 }, 0x4, {0x000000, 512 * 1024} }, + { .m = { .sec = 0, .tb = X }, 0x5, {0x000000, 1024 * 1024} }, + { .m = { .sec = 0, .tb = X }, 0x6, {0x000000, 1024 * 1024} }, + { .m = { .sec = 0, .tb = X }, 0x7, {0x000000, 1024 * 1024} }, +}; + static uint8_t do_read_status(const struct flashctx *flash) { if (flash->chip->read_status) @@ -116,6 +442,591 @@ return spi_write_status_register(flash, status); } +/* FIXME: Move to spi25.c if it's a JEDEC standard opcode */ +static uint8_t w25q_read_status_register_2(const struct flashctx *flash) +{ + static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { 0x35 }; + unsigned char readarr[2]; + int ret; + + /* Read Status Register */ + ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr); + if (ret) { + /* + * FIXME: make this a benign failure for now in case we are + * unable to execute the opcode + */ + msg_cdbg("RDSR2 failed!\n"); + readarr[0] = 0x00; + } + + return readarr[0]; +} + +/* Given a flash chip, this function returns its range table. */ +static int w25_range_table(const struct flashctx *flash, + struct wp_range_descriptor **descrs, + int *num_entries) +{ + *descrs = 0; + *num_entries = 0; + + switch (flash->chip->manufacture_id) { + case WINBOND_NEX_ID: + switch(flash->chip->model_id) { + case WINBOND_NEX_W25X10: + *descrs = w25x10_ranges; + *num_entries = ARRAY_SIZE(w25x10_ranges); + break; + case WINBOND_NEX_W25X20: + *descrs = w25x20_ranges; + *num_entries = ARRAY_SIZE(w25x20_ranges); + break; + case WINBOND_NEX_W25X40: + *descrs = w25x40_ranges; + *num_entries = ARRAY_SIZE(w25x40_ranges); + break; + case WINBOND_NEX_W25X80: + *descrs = w25x80_ranges; + *num_entries = ARRAY_SIZE(w25x80_ranges); + break; + case WINBOND_NEX_W25Q80_V: + *descrs = w25q80_ranges; + *num_entries = ARRAY_SIZE(w25q80_ranges); + break; + case WINBOND_NEX_W25Q16_V: + *descrs = w25q16_ranges; + *num_entries = ARRAY_SIZE(w25q16_ranges); + break; + case WINBOND_NEX_W25Q32_V: + case WINBOND_NEX_W25Q32_W: + //case WINBOND_NEX_W25Q32JW: + *descrs = w25q32_ranges; + *num_entries = ARRAY_SIZE(w25q32_ranges); + break; + case WINBOND_NEX_W25Q64_V: + case WINBOND_NEX_W25Q64_W: + *descrs = w25q64_ranges; + *num_entries = ARRAY_SIZE(w25q64_ranges); + break; + case WINBOND_NEX_W25Q128_DTR: + case WINBOND_NEX_W25Q128_V_M: + case WINBOND_NEX_W25Q128_V: + case WINBOND_NEX_W25Q128_W: + if (w25q_read_status_register_2(flash) & (1 << 6)) { + /* CMP == 1 */ + *descrs = w25rq128_cmp1_ranges; + *num_entries = ARRAY_SIZE(w25rq128_cmp1_ranges); + } else { + /* CMP == 0 */ + *descrs = w25rq128_cmp0_ranges; + *num_entries = ARRAY_SIZE(w25rq128_cmp0_ranges); + } + break; + case WINBOND_NEX_W25Q256JV_M: + if (w25q_read_status_register_2(flash) & (1 << 6)) { + /* CMP == 1 */ + *descrs = w25rq256_cmp1_ranges; + *num_entries = ARRAY_SIZE(w25rq256_cmp1_ranges); + } else { + /* CMP == 0 */ + *descrs = w25rq256_cmp0_ranges; + *num_entries = ARRAY_SIZE(w25rq256_cmp0_ranges); + } + break; + default: + msg_cerr("%s() %d: WINBOND flash chip mismatch (0x%04x)" + ", aborting\n", __func__, __LINE__, + flash->chip->model_id); + return -1; + } + break; + default: + msg_cerr("%s: flash vendor (0x%x) not found, aborting\n", + __func__, flash->chip->manufacture_id); + return -1; + } + + return 0; +} + +int w25_range_to_status(const struct flashctx *flash, + unsigned int start, unsigned int len, + struct w25q_status *status) +{ + struct wp_range_descriptor *descrs; + int i, range_found = 0; + int num_entries; + + if (w25_range_table(flash, &descrs, &num_entries)) + return -1; + + for (i = 0; i < num_entries; i++) { + struct wp_range *r = &descrs[i].range; + + msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n", + start, len, r->start, r->len); + if ((start == r->start) && (len == r->len)) { + status->bp0 = descrs[i].bp & 1; + status->bp1 = descrs[i].bp >> 1; + status->bp2 = descrs[i].bp >> 2; + status->tb = descrs[i].m.tb; + status->sec = descrs[i].m.sec; + + range_found = 1; + break; + } + } + + if (!range_found) { + msg_cerr("%s: matching range not found\n", __func__); + return -1; + } + + return 0; +} + +int w25_status_to_range(const struct flashctx *flash, + const struct w25q_status *status, + unsigned int *start, unsigned int *len) +{ + struct wp_range_descriptor *descrs; + int i, status_found = 0; + int num_entries; + + if (w25_range_table(flash, &descrs, &num_entries)) + return -1; + + for (i = 0; i < num_entries; i++) { + int bp; + int table_bp, table_tb, table_sec; + + bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2); + msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n", + bp, descrs[i].bp, + status->tb, descrs[i].m.tb, + status->sec, descrs[i].m.sec); + table_bp = descrs[i].bp; + table_tb = descrs[i].m.tb; + table_sec = descrs[i].m.sec; + if ((bp == table_bp || table_bp == X) && + (status->tb == table_tb || table_tb == X) && + (status->sec == table_sec || table_sec == X)) { + *start = descrs[i].range.start; + *len = descrs[i].range.len; + + status_found = 1; + break; + } + } + + if (!status_found) { + msg_cerr("matching status not found\n"); + return -1; + } + + return 0; +} + +/* Given a [start, len], this function calls w25_range_to_status() to convert + * it to flash-chip-specific range bits, then sets into status register. + */ +static int w25_set_range(const struct flashctx *flash, + unsigned int start, unsigned int len) +{ + struct w25q_status status; + int tmp = 0; + int expected = 0; + + memset(&status, 0, sizeof(status)); + tmp = do_read_status(flash); + memcpy(&status, &tmp, 1); + msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp); + + if (w25_range_to_status(flash, start, len, &status)) + return -1; + + msg_cdbg("status.busy: %x\n", status.busy); + msg_cdbg("status.wel: %x\n", status.wel); + msg_cdbg("status.bp0: %x\n", status.bp0); + msg_cdbg("status.bp1: %x\n", status.bp1); + msg_cdbg("status.bp2: %x\n", status.bp2); + msg_cdbg("status.tb: %x\n", status.tb); + msg_cdbg("status.sec: %x\n", status.sec); + msg_cdbg("status.srp0: %x\n", status.srp0); + + memcpy(&expected, &status, sizeof(status)); + do_write_status(flash, expected); + + tmp = do_read_status(flash); + msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp); + if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) { + msg_cerr("expected=0x%02x, but actual=0x%02x.\n", + expected, tmp); + return 1; + } + + return 0; +} + +/* Print out the current status register value with human-readable text. */ +static int w25_wp_status(const struct flashctx *flash) +{ + struct w25q_status status; + int tmp; + unsigned int start, len; + int ret = 0; + + memset(&status, 0, sizeof(status)); + tmp = do_read_status(flash); + memcpy(&status, &tmp, 1); + msg_cinfo("WP: status: 0x%02x\n", tmp); + msg_cinfo("WP: status.srp0: %x\n", status.srp0); + msg_cinfo("WP: write protect is %s.\n", + status.srp0 ? "enabled" : "disabled"); + + msg_cinfo("WP: write protect range: "); + if (w25_status_to_range(flash, &status, &start, &len)) { + msg_cinfo("(cannot resolve the range)\n"); + ret = -1; + } else { + msg_cinfo("start=0x%08x, len=0x%08x\n", start, len); + } + + return ret; +} + +static int w25q_large_range_to_status(const struct flashctx *flash, + unsigned int start, unsigned int len, + struct w25q_status_large *status) +{ + struct wp_range_descriptor *descrs; + int i, range_found = 0; + int num_entries; + + if (w25_range_table(flash, &descrs, &num_entries)) + return -1; + + for (i = 0; i < num_entries; i++) { + struct wp_range *r = &descrs[i].range; + + msg_cspew("comparing range 0x%x 0x%x / 0x%x 0x%x\n", + start, len, r->start, r->len); + if ((start == r->start) && (len == r->len)) { + status->bp0 = descrs[i].bp & 1; + status->bp1 = descrs[i].bp >> 1; + status->bp2 = descrs[i].bp >> 2; + status->bp3 = descrs[i].bp >> 3; + /* + * For MX25U12835E chip, Top/Bottom (T/B) bit is not + * part of status register and in that bit position is + * Quad Enable (QE) + */ + if (flash->chip->manufacture_id != MACRONIX_ID || + flash->chip->model_id != MACRONIX_MX25U12835E) + status->tb = descrs[i].m.tb; + + range_found = 1; + break; + } + } + + if (!range_found) { + msg_cerr("%s: matching range not found\n", __func__); + return -1; + } + + return 0; +} + +static int w25_large_status_to_range(const struct flashctx *flash, + const struct w25q_status_large *status, + unsigned int *start, unsigned int *len) +{ + struct wp_range_descriptor *descrs; + int i, status_found = 0; + int num_entries; + + if (w25_range_table(flash, &descrs, &num_entries)) + return -1; + + for (i = 0; i < num_entries; i++) { + int bp; + int table_bp, table_tb; + + bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2) | + (status->bp3 << 3); + msg_cspew("comparing 0x%x 0x%x / 0x%x 0x%x\n", + bp, descrs[i].bp, + status->tb, descrs[i].m.tb); + table_bp = descrs[i].bp; + table_tb = descrs[i].m.tb; + if ((bp == table_bp || table_bp == X) && + (status->tb == table_tb || table_tb == X)) { + *start = descrs[i].range.start; + *len = descrs[i].range.len; + + status_found = 1; + break; + } + } + + if (!status_found) { + msg_cerr("matching status not found\n"); + return -1; + } + + return 0; +} + +/* Given a [start, len], this function calls w25_range_to_status() to convert + * it to flash-chip-specific range bits, then sets into status register. + * Returns 0 if successful, -1 on error, and 1 if reading back was different. + */ +static int w25q_large_set_range(const struct flashctx *flash, + unsigned int start, unsigned int len) +{ + struct w25q_status_large status; + int tmp; + int expected = 0; + + memset(&status, 0, sizeof(status)); + tmp = do_read_status(flash); + memcpy(&status, &tmp, 1); + msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp); + + if (w25q_large_range_to_status(flash, start, len, &status)) + return -1; + + msg_cdbg("status.busy: %x\n", status.busy); + msg_cdbg("status.wel: %x\n", status.wel); + msg_cdbg("status.bp0: %x\n", status.bp0); + msg_cdbg("status.bp1: %x\n", status.bp1); + msg_cdbg("status.bp2: %x\n", status.bp2); + msg_cdbg("status.bp3: %x\n", status.bp3); + msg_cdbg("status.tb: %x\n", status.tb); + msg_cdbg("status.srp0: %x\n", status.srp0); + + memcpy(&expected, &status, sizeof(status)); + do_write_status(flash, expected); + + tmp = do_read_status(flash); + msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp); + if ((tmp & MASK_WP_AREA_LARGE) != (expected & MASK_WP_AREA_LARGE)) { + msg_cerr("expected=0x%02x, but actual=0x%02x.\n", + expected, tmp); + return 1; + } + + return 0; +} + +static int w25q_large_wp_status(const struct flashctx *flash) +{ + struct w25q_status_large sr1; + struct w25q_status_2 sr2; + uint8_t tmp[2]; + unsigned int start, len; + int ret = 0; + + memset(&sr1, 0, sizeof(sr1)); + tmp[0] = do_read_status(flash); + memcpy(&sr1, &tmp[0], 1); + + memset(&sr2, 0, sizeof(sr2)); + tmp[1] = w25q_read_status_register_2(flash); + memcpy(&sr2, &tmp[1], 1); + + msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]); + msg_cinfo("WP: status.srp0: %x\n", sr1.srp0); + msg_cinfo("WP: status.srp1: %x\n", sr2.srp1); + msg_cinfo("WP: write protect is %s.\n", + (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled"); + + msg_cinfo("WP: write protect range: "); + if (w25_large_status_to_range(flash, &sr1, &start, &len)) { + msg_cinfo("(cannot resolve the range)\n"); + ret = -1; + } else { + msg_cinfo("start=0x%08x, len=0x%08x\n", start, len); + } + + return ret; +} + +/* Set/clear the SRP0 bit in the status register. */ +static int w25_set_srp0(const struct flashctx *flash, int enable) +{ + struct w25q_status status; + int tmp = 0; + int expected = 0; + + memset(&status, 0, sizeof(status)); + tmp = do_read_status(flash); + /* FIXME: this is NOT endian-free copy. */ + memcpy(&status, &tmp, 1); + msg_cdbg("%s: old status: 0x%02x\n", __func__, tmp); + + status.srp0 = enable ? 1 : 0; + memcpy(&expected, &status, sizeof(status)); + do_write_status(flash, expected); + + tmp = do_read_status(flash); + msg_cdbg("%s: new status: 0x%02x\n", __func__, tmp); + if ((tmp & MASK_WP_AREA) != (expected & MASK_WP_AREA)) + return 1; + + return 0; +} + +static int w25_enable_writeprotect(const struct flashctx *flash, + enum wp_mode wp_mode) +{ + int ret; + + if (wp_mode != WP_MODE_HARDWARE) { + msg_cerr("%s(): unsupported write-protect mode\n", __func__); + return 1; + } + + ret = w25_set_srp0(flash, 1); + if (ret) + msg_cerr("%s(): error=%d.\n", __func__, ret); + return ret; +} + +static int w25_disable_writeprotect(const struct flashctx *flash) +{ + int ret; + + ret = w25_set_srp0(flash, 0); + if (ret) + msg_cerr("%s(): error=%d.\n", __func__, ret); + + return ret; +} + +static int w25_list_ranges(const struct flashctx *flash) +{ + struct wp_range_descriptor *descrs; + int i, num_entries; + + if (w25_range_table(flash, &descrs, &num_entries)) + return -1; + + for (i = 0; i < num_entries; i++) { + msg_cinfo("start: 0x%06x, length: 0x%06x\n", + descrs[i].range.start, + descrs[i].range.len); + } + + return 0; +} + +static int w25q_wp_status(const struct flashctx *flash) +{ + struct w25q_status sr1; + struct w25q_status_2 sr2; + uint8_t tmp[2]; + unsigned int start, len; + int ret = 0; + + memset(&sr1, 0, sizeof(sr1)); + tmp[0] = do_read_status(flash); + memcpy(&sr1, &tmp[0], 1); + + memset(&sr2, 0, sizeof(sr2)); + tmp[1] = w25q_read_status_register_2(flash); + memcpy(&sr2, &tmp[1], 1); + + msg_cinfo("WP: status: 0x%02x%02x\n", tmp[1], tmp[0]); + msg_cinfo("WP: status.srp0: %x\n", sr1.srp0); + msg_cinfo("WP: status.srp1: %x\n", sr2.srp1); + msg_cinfo("WP: write protect is %s.\n", + (sr1.srp0 || sr2.srp1) ? "enabled" : "disabled"); + + msg_cinfo("WP: write protect range: "); + if (w25_status_to_range(flash, &sr1, &start, &len)) { + msg_cinfo("(cannot resolve the range)\n"); + ret = -1; + } else { + msg_cinfo("start=0x%08x, len=0x%08x\n", start, len); + } + + return ret; +} + +/* + * W25Q adds an optional byte to the standard WRSR opcode. If /CS is + * de-asserted after the first byte, then it acts like a JEDEC-standard + * WRSR command. if /CS is asserted, then the next data byte is written + * into status register 2. + */ +#define W25Q_WRSR_OUTSIZE 0x03 +static int w25q_write_status_register_WREN(const struct flashctx *flash, uint8_t s1, uint8_t s2) +{ + int result; + struct spi_command cmds[] = { + { + /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */ + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = W25Q_WRSR_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WRSR, s1, s2 }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution\n", + __func__); + } + + /* WRSR performs a self-timed erase before the changes take effect. */ + programmer_delay(100 * 1000); + + return result; +} + +/* + * Set/clear the SRP1 bit in status register 2. + * FIXME: make this more generic if other chips use the same SR2 layout + */ +static int w25q_set_srp1(const struct flashctx *flash, int enable) +{ + struct w25q_status sr1; + struct w25q_status_2 sr2; + uint8_t tmp, expected; + + tmp = do_read_status(flash); + memcpy(&sr1, &tmp, 1); + tmp = w25q_read_status_register_2(flash); + memcpy(&sr2, &tmp, 1); + + msg_cdbg("%s: old status 2: 0x%02x\n", __func__, tmp); + + sr2.srp1 = enable ? 1 : 0; + + memcpy(&expected, &sr2, 1); + w25q_write_status_register_WREN(flash, *((uint8_t *)&sr1), *((uint8_t *)&sr2)); + + tmp = w25q_read_status_register_2(flash); + msg_cdbg("%s: new status 2: 0x%02x\n", __func__, tmp); + if ((tmp & MASK_WP2_AREA) != (expected & MASK_WP2_AREA)) + return 1; + + return 0; +} + enum wp_mode get_wp_mode(const char *mode_str) { enum wp_mode wp_mode = WP_MODE_UNKNOWN; @@ -130,6 +1041,158 @@ return wp_mode; } +static int w25q_disable_writeprotect(const struct flashctx *flash, + enum wp_mode wp_mode) +{ + int ret = 1; + struct w25q_status_2 sr2; + uint8_t tmp; + + switch (wp_mode) { + case WP_MODE_HARDWARE: + ret = w25_set_srp0(flash, 0); + break; + case WP_MODE_POWER_CYCLE: + tmp = w25q_read_status_register_2(flash); + memcpy(&sr2, &tmp, 1); + if (sr2.srp1) { + msg_cerr("%s(): must disconnect power to disable " + "write-protection\n", __func__); + } else { + ret = 0; + } + break; + case WP_MODE_PERMANENT: + msg_cerr("%s(): cannot disable permanent write-protection\n", + __func__); + break; + default: + msg_cerr("%s(): invalid mode specified\n", __func__); + break; + } + + if (ret) + msg_cerr("%s(): error=%d.\n", __func__, ret); + + return ret; +} + +static int w25q_disable_writeprotect_default(const struct flashctx *flash) +{ + return w25q_disable_writeprotect(flash, WP_MODE_HARDWARE); +} + +static int w25q_enable_writeprotect(const struct flashctx *flash, + enum wp_mode wp_mode) +{ + int ret = 1; + struct w25q_status sr1; + struct w25q_status_2 sr2; + uint8_t tmp; + + switch (wp_mode) { + case WP_MODE_HARDWARE: + if (w25q_disable_writeprotect(flash, WP_MODE_POWER_CYCLE)) { + msg_cerr("%s(): cannot disable power cycle WP mode\n", + __func__); + break; + } + + tmp = do_read_status(flash); + memcpy(&sr1, &tmp, 1); + if (sr1.srp0) + ret = 0; + else + ret = w25_set_srp0(flash, 1); + + break; + case WP_MODE_POWER_CYCLE: + if (w25q_disable_writeprotect(flash, WP_MODE_HARDWARE)) { + msg_cerr("%s(): cannot disable hardware WP mode\n", + __func__); + break; + } + + tmp = w25q_read_status_register_2(flash); + memcpy(&sr2, &tmp, 1); + if (sr2.srp1) + ret = 0; + else + ret = w25q_set_srp1(flash, 1); + + break; + case WP_MODE_PERMANENT: + tmp = do_read_status(flash); + memcpy(&sr1, &tmp, 1); + if (sr1.srp0 == 0) { + ret = w25_set_srp0(flash, 1); + if (ret) { + msg_perr("%s(): cannot enable SRP0 for " + "permanent WP\n", __func__); + break; + } + } + + tmp = w25q_read_status_register_2(flash); + memcpy(&sr2, &tmp, 1); + if (sr2.srp1 == 0) { + ret = w25q_set_srp1(flash, 1); + if (ret) { + msg_perr("%s(): cannot enable SRP1 for " + "permanent WP\n", __func__); + break; + } + } + + break; + default: + msg_perr("%s(): invalid mode %d\n", __func__, wp_mode); + break; + } + + if (ret) + msg_cerr("%s(): error=%d.\n", __func__, ret); + return ret; +} + +/* W25P, W25X, and many flash chips from various vendors */ +struct wp wp_w25 = { + .list_ranges = w25_list_ranges, + .set_range = w25_set_range, + .enable = w25_enable_writeprotect, + .disable = w25_disable_writeprotect, + .wp_status = w25_wp_status, + +}; + +/* W25Q series has features such as a second status register and SFDP */ +struct wp wp_w25q = { + .list_ranges = w25_list_ranges, + .set_range = w25_set_range, + .enable = w25q_enable_writeprotect, + /* + * By default, disable hardware write-protection. We may change + * this later if we want to add fine-grained write-protect disable + * as a command-line option. + */ + .disable = w25q_disable_writeprotect_default, + .wp_status = w25q_wp_status, +}; + +/* W25Q large series has 4 block-protect bits */ +struct wp wp_w25q_large = { + .list_ranges = w25_list_ranges, + .set_range = w25q_large_set_range, + .enable = w25q_enable_writeprotect, + /* + * By default, disable hardware write-protection. We may change + * this later if we want to add fine-grained write-protect disable + * as a command-line option. + */ + .disable = w25q_disable_writeprotect_default, + .wp_status = w25q_large_wp_status, +}; + /* Given a flash chip, this function returns its writeprotect info. */ static int generic_range_table(const struct flashctx *flash, struct wp_context **wp, diff --git a/writeprotect.h b/writeprotect.h index bded4c8..632f169 100644 --- a/writeprotect.h +++ b/writeprotect.h @@ -35,6 +35,11 @@ int (*wp_status)(const struct flashctx *flash); }; +/* winbond w25-series */ +extern struct wp wp_w25; /* older winbond chips (w25p, w25x, etc) */ +extern struct wp wp_w25q; +extern struct wp wp_w25q_large; /* large winbond chips (>= 32MB) */ + extern struct wp wp_generic; enum wp_mode get_wp_mode(const char *mode_str); -- To view, visit
https://review.coreboot.org/c/flashrom/+/45572
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I54eedaa8e24c69d6d088e9ebcfb9a284e0fbe893 Gerrit-Change-Number: 45572 Gerrit-PatchSet: 1 Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org> Gerrit-MessageType: newchange
1
5
0
0
Change in flashrom[master]: spi25_statusreg: implement w25q_write_status_register()
by Edward O'Callaghan (Code Review)
06 Dec '21
06 Dec '21
Edward O'Callaghan has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/45749
) Change subject: spi25_statusreg: implement w25q_write_status_register() ...................................................................... spi25_statusreg: implement w25q_write_status_register() Change-Id: Idd9f818cb3464b3221330e7aa66f44b2b6d2adf7 Signed-off-by: Edward O'Callaghan <quasisec(a)google.com> --- M chipdrivers.h M spi25_statusreg.c 2 files changed, 24 insertions(+), 2 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/49/45749/1 diff --git a/chipdrivers.h b/chipdrivers.h index 4e76588..c2764b9 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -65,6 +65,7 @@ uint8_t spi_read_status_register(const struct flashctx *flash); uint8_t spi_read_status_register_2(const struct flashctx *flash); int spi_write_status_register(const struct flashctx *flash, int status); +int w25q_write_status_register(const struct flashctx *flash, uint8_t s0, uint8_t s1); void spi_prettyprint_status_register_bit(uint8_t status, int bit); int spi_prettyprint_status_register_plain(struct flashctx *flash); int spi_prettyprint_status_register_default_welwip(struct flashctx *flash); diff --git a/spi25_statusreg.c b/spi25_statusreg.c index cad92e6..fb49c96 100644 --- a/spi25_statusreg.c +++ b/spi25_statusreg.c @@ -27,6 +27,15 @@ int result; int i = 0; /* + * W25Q adds an optional byte to the standard WRSR opcode. If /CS is + * de-asserted after the first byte, then it acts like a JEDEC-standard + * WRSR command. if /CS is asserted, then the next data byte is written + * into status register 2. + */ + uint8_t s0 = status & 0xff; + uint8_t s1 = (status >> 8) & 0xff; + + /* * WRSR requires either EWSR or WREN depending on chip type. * The code below relies on the fact hat EWSR and WREN have the same * INSIZE and OUTSIZE. @@ -38,8 +47,8 @@ .readcnt = 0, .readarr = NULL, }, { - .writecnt = JEDEC_WRSR_OUTSIZE, - .writearr = (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status }, + .writecnt = s1 > 0 ? (JEDEC_WRSR_OUTSIZE + 1) /* W25Q_WRSR_OUTSIZE */ : JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WRSR, s0, s1 }, .readcnt = 0, .readarr = NULL, }, { @@ -90,6 +99,18 @@ return ret; } +int w25q_write_status_register(const struct flashctx *flash, uint8_t s0, uint8_t s1) +{ + int feature_bits = flash->chip->feature_bits; + int ret = 1; + + if (!(feature_bits & FEATURE_WRSR_WREN)) + msg_cdbg("Missing status register write definition\n"); + if (feature_bits & FEATURE_WRSR_WREN) + ret = spi_write_status_register_flag(flash, ((s1 << 8) | s0), JEDEC_WREN); + return ret; +} + static uint8_t spi_read_status_register_generic(const struct flashctx *flash, uint8_t opcode) { const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { opcode }; -- To view, visit
https://review.coreboot.org/c/flashrom/+/45749
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: Idd9f818cb3464b3221330e7aa66f44b2b6d2adf7 Gerrit-Change-Number: 45749 Gerrit-PatchSet: 1 Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org> Gerrit-MessageType: newchange
1
1
0
0
Change in flashrom[master]: flashchips.c: Add writeprotect to selected chips
by Edward O'Callaghan (Code Review)
06 Dec '21
06 Dec '21
Edward O'Callaghan has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/45752
) Change subject: flashchips.c: Add writeprotect to selected chips ...................................................................... flashchips.c: Add writeprotect to selected chips These chips are the ones tested in the ChromiumOS Flashrom fork so we are just upstreaming those at the moment. BUG=b:153800563 BRANCH=none TEST=builds Change-Id: I90c34ff9c34ff40fa186244024614c40954f4498 Signed-off-by: Edward O'Callaghan <quasisec(a)google.com> --- M flashchips.c 1 file changed, 6 insertions(+), 0 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/52/45752/1 diff --git a/flashchips.c b/flashchips.c index 7d10abf..7ece354 100644 --- a/flashchips.c +++ b/flashchips.c @@ -21,6 +21,7 @@ #include "flash.h" #include "flashchips.h" #include "chipdrivers.h" +#include "writeprotect.h" /** * List of supported flash chips. @@ -5959,6 +5960,7 @@ .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {1695, 1950}, + .wp = &wp_generic, }, { @@ -6396,6 +6398,7 @@ .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, + .wp = &wp_generic, }, { @@ -8277,6 +8280,7 @@ .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) supported */ .voltage = {2700, 3600}, + .wp = &wp_generic, }, { @@ -8613,6 +8617,7 @@ .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) supported */ .voltage = {2700, 3600}, + .wp = &wp_generic, }, { @@ -8767,6 +8772,7 @@ .write = spi_chip_write_256, .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */ .voltage = {2700, 3600}, + .wp = &wp_generic, }, { -- To view, visit
https://review.coreboot.org/c/flashrom/+/45752
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I90c34ff9c34ff40fa186244024614c40954f4498 Gerrit-Change-Number: 45752 Gerrit-PatchSet: 1 Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org> Gerrit-MessageType: newchange
2
4
0
0
Change in flashrom[master]: spi25: Add 'spi_read_status_register_2()' helper
by Edward O'Callaghan (Code Review)
06 Dec '21
06 Dec '21
Edward O'Callaghan has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/45748
) Change subject: spi25: Add 'spi_read_status_register_2()' helper ...................................................................... spi25: Add 'spi_read_status_register_2()' helper Winbond chips have a WRSR opcode of 0x35 interestingly. Add helper to allow for reading it. This is later used by writeprotection support. BUG=b:153800563 BRANCH=none TEST=builds Change-Id: I2d25f22ced17ab94d122e86b270357723b45458e Signed-off-by: Edward O'Callaghan <quasisec(a)google.com> --- M chipdrivers.h M spi25_statusreg.c 2 files changed, 13 insertions(+), 2 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/48/45748/1 diff --git a/chipdrivers.h b/chipdrivers.h index cf03811..4e76588 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -63,6 +63,7 @@ /* spi25_statusreg.c */ uint8_t spi_read_status_register(const struct flashctx *flash); +uint8_t spi_read_status_register_2(const struct flashctx *flash); int spi_write_status_register(const struct flashctx *flash, int status); void spi_prettyprint_status_register_bit(uint8_t status, int bit); int spi_prettyprint_status_register_plain(struct flashctx *flash); diff --git a/spi25_statusreg.c b/spi25_statusreg.c index 34f9ad4..cad92e6 100644 --- a/spi25_statusreg.c +++ b/spi25_statusreg.c @@ -90,9 +90,9 @@ return ret; } -uint8_t spi_read_status_register(const struct flashctx *flash) +static uint8_t spi_read_status_register_generic(const struct flashctx *flash, uint8_t opcode) { - static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { JEDEC_RDSR }; + const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { opcode }; /* FIXME: No workarounds for driver/hardware bugs in generic code. */ unsigned char readarr[2]; /* JEDEC_RDSR_INSIZE=1 but wbsio needs 2 */ int ret; @@ -108,6 +108,16 @@ return readarr[0]; } +uint8_t spi_read_status_register(const struct flashctx *flash) +{ + return spi_read_status_register_generic(flash, JEDEC_RDSR); +} + +uint8_t spi_read_status_register_2(const struct flashctx *flash) +{ + return spi_read_status_register_generic(flash, 0x35); /* Unclear if this is a std JEDEC opcode. */ +} + /* A generic block protection disable. * Tests if a protection is enabled with the block protection mask (bp_mask) and returns success otherwise. * Tests if the register bits are locked with the lock_mask (lock_mask). -- To view, visit
https://review.coreboot.org/c/flashrom/+/45748
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I2d25f22ced17ab94d122e86b270357723b45458e Gerrit-Change-Number: 45748 Gerrit-PatchSet: 1 Gerrit-Owner: Edward O'Callaghan <quasisec(a)chromium.org> Gerrit-MessageType: newchange
3
5
0
0
Change in flashrom[master]: Add MEC1308 EC programmer
by Victor Ding (Code Review)
04 Nov '21
04 Nov '21
Victor Ding has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/44541
) Change subject: Add MEC1308 EC programmer ...................................................................... Add MEC1308 EC programmer Initial support of Microchip MEC1308 Embedded Controller. Signed-off-by: Victor Ding <victording(a)google.com> Change-Id: I2d51b4bdc0b38b6e488e71b9e774eb6232a2069e --- M Makefile M flashrom.c A mec1308.c M meson.build M meson_options.txt M programmer.h 6 files changed, 569 insertions(+), 0 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/41/44541/1 diff --git a/Makefile b/Makefile index 803529f..2acd656 100644 --- a/Makefile +++ b/Makefile @@ -175,6 +175,11 @@ else override CONFIG_FT2232_SPI = no endif +ifeq ($(CONFIG_MEC1308), yes) +UNSUPPORTED_FEATURES += CONFIG_MEC1308=yes +else +override CONFIG_MEC1308 = no +endif ifeq ($(CONFIG_USBBLASTER_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_USBBLASTER_SPI=yes else @@ -281,6 +286,11 @@ else override CONFIG_DRKAISER = no endif +ifeq ($(CONFIG_MEC1308), yes) +UNSUPPORTED_FEATURES += CONFIG_MEC1308=yes +else +override CONFIG_MEC1308 = no +endif ifeq ($(CONFIG_NICREALTEK), yes) UNSUPPORTED_FEATURES += CONFIG_NICREALTEK=yes else @@ -386,6 +396,11 @@ else override CONFIG_FT2232_SPI = no endif +ifeq ($(CONFIG_MEC1308), yes) +UNSUPPORTED_FEATURES += CONFIG_MEC1308=yes +else +override CONFIG_MEC1308 = no +endif ifeq ($(CONFIG_USBBLASTER_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_USBBLASTER_SPI=yes else @@ -674,6 +689,9 @@ # Always enable FT2232 SPI dongles for now. CONFIG_FT2232_SPI ?= yes +# Microchip MEC1308 Embedded Controller +CONFIG_MEC1308 ?= yes + # Always enable Altera USB-Blaster dongles for now. CONFIG_USBBLASTER_SPI ?= yes @@ -855,6 +873,11 @@ NEED_LIBPCI += CONFIG_INTERNAL endif +ifeq ($(CONFIG_MEC1308), yes) +FEATURE_CFLAGS += -D'CONFIG_MEC1308=1' +PROGRAMMER_OBJS += mec1308.o +endif + ifeq ($(CONFIG_SERPROG), yes) FEATURE_CFLAGS += -D'CONFIG_SERPROG=1' PROGRAMMER_OBJS += serprog.o diff --git a/flashrom.c b/flashrom.c index 4a30986..5a8e37a 100644 --- a/flashrom.c +++ b/flashrom.c @@ -84,6 +84,18 @@ }, #endif +#if CONFIG_MEC1308 == 1 + { + .name = "mec1308", + .type = OTHER, + .devs.note = "Microchip MEC1308 Embedded Controller.\n", + .init = mec1308_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + #if CONFIG_NIC3COM == 1 { .name = "nic3com", diff --git a/mec1308.c b/mec1308.c new file mode 100644 index 0000000..43bd2db --- /dev/null +++ b/mec1308.c @@ -0,0 +1,520 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2010 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Google or the names of contributors or + * licensors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * GOOGLE INC AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * GOOGLE OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#if defined(__i386__) || defined(__x86_64__) +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "flash.h" +#include "hwaccess.h" +#include "chipdrivers.h" +#include "programmer.h" +#include "spi.h" + +#define MEC1308_SIO_PORT1 0x2e +#define MEC1308_SIO_PORT2 0x4e +#define MEC1308_SIO_ENTRY_KEY 0x55 +#define MEC1308_SIO_EXIT_KEY 0xaa + +#define MEC1308_SIOCFG_LDN 0x07 /* LDN Bank Selector */ +#define MEC1308_DEVICE_ID_REG 0x20 /* Device ID Register */ +#define MEC1308_DEVICE_ID_VAL 0x4d /* Device ID Value for MEC1308 */ +#define MEC1310_DEVICE_ID_VAL 0x04 /* Device ID Value for MEC1310 */ +#define MEC1308_DEVICE_REV 0x21 /* Device Revision ID Register */ + +#define MEC1308_MBX_CMD 0x82 /* mailbox command register offset */ +#define MEC1308_MBX_EXT_CMD 0x83 /* mailbox ext. command reg offset */ +#define MEC1308_MBX_DATA_START 0x84 /* first mailbox data register offset */ +#define MEC1308_MBX_DATA_END 0x91 /* last mailbox data register offset */ + +static unsigned int mbx_data; /* Mailbox register interface data address*/ + +/* + * These command codes depend on EC firmware. The ones listed below are input + * using the mailbox interface, though others may be input using the ACPI + * interface. Some commands also have an output value (ie pass/failure code) + * which EC writes to the mailbox command register after completion. + */ +#define MEC1308_CMD_SMI_ENABLE 0x84 +#define MEC1308_CMD_SMI_DISABLE 0x85 +#define MEC1308_CMD_ACPI_ENABLE 0x86 +#define MEC1308_CMD_ACPI_DISABLE 0x87 + +/* + * Passthru commands are also input using the mailbox interface. Passthru mode + * enter/start/end commands are special since they require a command word to + * be written to the data registers. Other passthru commands are performed + * after passthru mode has been started. + * + * Multiple passthru mode commands may be issued before ending passthru mode. + * You do not need to enter, start, and end passthru mode for each SPI + * command. However, other mailbox commands might not work when passthru mode + * is enabled. For example, you may read all SPI chip content while in passthru + * mode, but you should exit passthru mode before performing other EC commands + * such as reading fan speed. + */ +#define MEC1308_CMD_PASSTHRU 0x55 /* force EC to process word */ +#define MEC1308_CMD_PASSTHRU_SUCCESS 0xaa /* success code for passthru */ +#define MEC1308_CMD_PASSTHRU_FAIL 0xfe /* failure code for passthru */ +#define MEC1308_CMD_PASSTHRU_ENTER "PathThruMode" /* not a typo... */ +#define MEC1308_CMD_PASSTHRU_START "Start" +#define MEC1308_CMD_PASSTHRU_EXIT "End_Mode" +#define MEC1308_CMD_PASSTHRU_CS_EN 0xf0 /* chip-select enable */ +#define MEC1308_CMD_PASSTHRU_CS_DIS 0xf1 /* chip-select disable */ +#define MEC1308_CMD_PASSTHRU_SEND 0xf2 /* send byte from data0 */ +#define MEC1308_CMD_PASSTHRU_READ 0xf3 /* read byte, place in data0 */ + +typedef struct +{ + unsigned int in_sio_cfgmode; + unsigned int mbx_idx; /* Mailbox register interface index address */ + unsigned int mbx_data; /* Mailbox register interface data address*/ +} mec1308_data_t; + +static void mec1308_sio_enter(mec1308_data_t *ctx_data, uint16_t port) +{ + if (ctx_data->in_sio_cfgmode) + return; + + OUTB(MEC1308_SIO_ENTRY_KEY, port); + ctx_data->in_sio_cfgmode = 1; +} + +static void mec1308_sio_exit(mec1308_data_t *ctx_data, uint16_t port) +{ + if (!ctx_data->in_sio_cfgmode) + return; + + OUTB(MEC1308_SIO_EXIT_KEY, port); + ctx_data->in_sio_cfgmode = 0; +} + +/** probe for super i/o index + * @port: allocated buffer to store port + * + * returns 0 to indicate success, <0 to indicate error + */ +static int mec1308_get_sio_index(mec1308_data_t *ctx_data, uint16_t *port) +{ + uint16_t ports[] = { MEC1308_SIO_PORT1, + MEC1308_SIO_PORT2, + }; + size_t i; + static uint16_t port_internal, port_found = 0; + + if (port_found) { + *port = port_internal; + return 0; + } + + if (rget_io_perms()) + return -1; + + for (i = 0; i < ARRAY_SIZE(ports); i++) { + uint8_t tmp8; + + /* + * Only after config mode has been successfully entered will the + * index port will read back the last value written to it. + * So we will attempt to enter config mode, set the index + * register, and see if the index register retains the value. + * + * Note: It seems to work "best" when using a device ID register + * as the index and reading from the data port before reading + * the index port. + */ + mec1308_sio_enter(ctx_data, ports[i]); + OUTB(MEC1308_DEVICE_ID_REG, ports[i]); + tmp8 = INB(ports[i] + 1); + tmp8 = INB(ports[i]); + if ((tmp8 != MEC1308_DEVICE_ID_REG)) { + ctx_data->in_sio_cfgmode = 0; + continue; + } + + port_internal = ports[i]; + port_found = 1; + break; + } + + if (!port_found) { + msg_cdbg("\nfailed to obtain super i/o index\n"); + return -1; + } + + msg_cdbg("\nsuper i/o index = 0x%04x\n", port_internal); + *port = port_internal; + return 0; +} + +static uint8_t mbx_read(mec1308_data_t *ctx_data, uint8_t idx) +{ + OUTB(idx, ctx_data->mbx_idx); + return INB(mbx_data); +} + +static int mbx_wait(mec1308_data_t *ctx_data) +{ + int i; + int max_attempts = 10000; + int rc = 0; + + for (i = 0; mbx_read(ctx_data, MEC1308_MBX_CMD); i++) { + if (i == max_attempts) { + rc = 1; + break; + } + /* FIXME: This delay adds determinism to the delay period. It + was chosen arbitrarily thru some experiments. */ + programmer_delay(2); + } + + return rc; +} + +static int mbx_write(mec1308_data_t *ctx_data, uint8_t idx, uint8_t data) +{ + int rc = 0; + + if (idx == MEC1308_MBX_CMD && mbx_wait(ctx_data)) { + msg_perr("%s: command register not clear\n", __func__); + return 1; + } + + OUTB(idx, ctx_data->mbx_idx); + OUTB(data, mbx_data); + + if (idx == MEC1308_MBX_CMD) + rc = mbx_wait(ctx_data); + + return rc; +} + +static void mbx_clear(mec1308_data_t *ctx_data) +{ + int reg; + + for (reg = MEC1308_MBX_DATA_START; reg < MEC1308_MBX_DATA_END; reg++) + mbx_write(ctx_data, reg, 0x00); + mbx_write(ctx_data, MEC1308_MBX_CMD, 0x00); +} + +static int mec1308_exit_passthru_mode(mec1308_data_t *ctx_data) +{ + uint8_t tmp8; + size_t i; + + /* exit passthru mode */ + for (i = 0; i < strlen(MEC1308_CMD_PASSTHRU_EXIT); i++) { + mbx_write(ctx_data, MEC1308_MBX_DATA_START + i, + MEC1308_CMD_PASSTHRU_EXIT[i]); + } + + if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU)) { + msg_pdbg("%s(): exit passthru command timed out\n", __func__); + return 1; + } + + tmp8 = mbx_read(ctx_data, MEC1308_MBX_DATA_START); + msg_pdbg("%s: result: 0x%02x ", __func__, tmp8); + if (tmp8 == MEC1308_CMD_PASSTHRU_SUCCESS) { + msg_pdbg("(exited passthru mode)\n"); + } else if (tmp8 == MEC1308_CMD_PASSTHRU_FAIL) { + msg_pdbg("(failed to exit passthru mode)\n"); + } + + return 0; +} + +static int enter_passthru_mode(mec1308_data_t *ctx_data) +{ + uint8_t tmp8; + size_t i; + + /* + * Enter passthru mode. If the EC does not successfully enter passthru + * mode the first time, we'll clear the mailbox and issue the "exit + * passthru mode" command sequence up to 3 times or until it arrives in + * a known state. + * + * Note: This workaround was developed experimentally. + */ + for (i = 0; i < 3; i++) { + size_t j; + + msg_pdbg("%s(): entering passthru mode, attempt %d out of 3\n", + __func__, (int)(i + 1)); + for (j = 0; j < strlen(MEC1308_CMD_PASSTHRU_ENTER); j++) { + mbx_write(ctx_data, MEC1308_MBX_DATA_START + j, + MEC1308_CMD_PASSTHRU_ENTER[j]); + } + + if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU)) + msg_pdbg("%s(): enter passthru command timed out\n", + __func__); + + tmp8 = mbx_read(ctx_data, MEC1308_MBX_DATA_START); + if (tmp8 == MEC1308_CMD_PASSTHRU_SUCCESS) + break; + + msg_pdbg("%s(): command failed, clearing data registers and " + "issuing full exit passthru command...\n", __func__); + mbx_clear(ctx_data); + mec1308_exit_passthru_mode(ctx_data); + } + + if (tmp8 != MEC1308_CMD_PASSTHRU_SUCCESS) { + msg_perr("%s(): failed to enter passthru mode, result=0x%02x\n", + __func__, tmp8); + return 1; + } + + msg_pdbg("%s(): enter passthru mode return code: 0x%02x\n", + __func__, tmp8); + + /* start passthru mode */ + for (i = 0; i < strlen(MEC1308_CMD_PASSTHRU_START); i++) + mbx_write(ctx_data, MEC1308_MBX_DATA_START + i, + MEC1308_CMD_PASSTHRU_START[i]); + if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU)) { + msg_pdbg("%s(): start passthru command timed out\n", __func__); + return 1; + } + tmp8 = mbx_read(ctx_data, MEC1308_MBX_DATA_START); + if (tmp8 != MEC1308_CMD_PASSTHRU_SUCCESS) { + msg_perr("%s(): failed to enter passthru mode, result=%02x\n", + __func__, tmp8); + return 1; + } + msg_pdbg("%s(): start passthru mode return code: 0x%02x\n", + __func__, tmp8); + + return 0; +} + +static int mec1308_shutdown(void *data) +{ + mec1308_data_t *ctx_data = (mec1308_data_t *)data; + + /* Exit passthru mode before performing commands which do not affect + the SPI ROM */ + mec1308_exit_passthru_mode(ctx_data); + + /* Re-enable SMI and ACPI. + FIXME: is there an ordering dependency? */ + if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_SMI_ENABLE)) + msg_pdbg("%s: unable to re-enable SMI\n", __func__); + if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_ACPI_ENABLE)) + msg_pdbg("%s: unable to re-enable ACPI\n", __func__); + + free(data); + return 0; +} + +static int mec1308_chip_select(mec1308_data_t *ctx_data) +{ + return mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_EN); +} + +static int mec1308_chip_deselect(mec1308_data_t *ctx_data) +{ + return mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_DIS); +} + +/* + * MEC1308 will not allow direct access to SPI chip from host if EC is + * connected to LPC bus. This function will forward commands issued thru + * mailbox interface to the SPI flash chip. + */ +static int mec1308_spi_send_command(const struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + int rc = 0; + size_t i; + mec1308_data_t *ctx_data = (mec1308_data_t *)flash->mst->spi.data; + + if (mec1308_chip_select(ctx_data)) + return 1; + + for (i = 0; i < writecnt; i++) { + if (mbx_write(ctx_data, MEC1308_MBX_DATA_START, writearr[i]) || + mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_SEND)) { + msg_pdbg("%s: failed to issue send command\n",__func__); + rc = 1; + goto mec1308_spi_send_command_exit; + } + } + + for (i = 0; i < readcnt; i++) { + if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_READ)) { + msg_pdbg("%s: failed to issue read command\n",__func__); + rc = 1; + goto mec1308_spi_send_command_exit; + } + readarr[i] = mbx_read(ctx_data, MEC1308_MBX_DATA_START); + } + +mec1308_spi_send_command_exit: + rc |= mec1308_chip_deselect(ctx_data); + return rc; +} + +static struct spi_master spi_master_mec1308 = { + .max_data_read = 256, /* FIXME: should be MAX_DATA_READ_UNLIMITED? */ + .max_data_write = 256, /* FIXME: should be MAX_DATA_WRITE_UNLIMITED? */ + .command = mec1308_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = default_spi_write_256, +}; + +int mec1308_init(void) +{ + uint16_t sio_port; + uint8_t device_id; + uint8_t tmp8; + int ret = 0; + char *p = NULL; + mec1308_data_t *ctx_data = NULL; + + msg_pdbg("%s(): entered\n", __func__); + + ctx_data = calloc(1, sizeof(mec1308_data_t)); + if (!ctx_data) { + msg_perr("Unable to allocate space for extra context data.\n"); + return 1; + } + + p = extract_programmer_param("type"); + if (p && strcmp(p, "ec")) { + msg_pdbg("mec1308 only supports \"ec\" type devices\n"); + ret = 1; + goto mec1308_init_exit; + } + + if (mec1308_get_sio_index(ctx_data, &sio_port) < 0) { + msg_pdbg("MEC1308 not found (probe failed).\n"); + ret = 1; + goto mec1308_init_exit; + } + device_id = sio_read(sio_port, MEC1308_DEVICE_ID_REG); + switch(device_id) { + case MEC1308_DEVICE_ID_VAL: + msg_pdbg("Found EC: MEC1308 (ID:0x%02x,Rev:0x%02x) on " + "sio_port:0x%x.\n", device_id, + sio_read(sio_port, MEC1308_DEVICE_REV), sio_port); + break; + case MEC1310_DEVICE_ID_VAL: + msg_pdbg("Found EC: MEC1310 (ID:0x%02x,Rev:0x%02x) on " + "sio_port:0x%x.\n", device_id, + sio_read(sio_port, MEC1308_DEVICE_REV), sio_port); + break; + default: + msg_pdbg("MEC1308 not found\n"); + ret = 1; + goto mec1308_init_exit; + } + + /* + * setup mailbox interface at LDN 9 + */ + sio_write(sio_port, MEC1308_SIOCFG_LDN, 0x09); + tmp8 = sio_read(sio_port, 0x30); + tmp8 |= 1; + sio_write(sio_port, 0x30, tmp8); /* activate logical device */ + + ctx_data->mbx_idx = (unsigned int)sio_read(sio_port, 0x60) << 8 | + sio_read(sio_port, 0x61); + mbx_data = ctx_data->mbx_idx + 1; + msg_pdbg("%s: mbx_idx: 0x%04x, mbx_data: 0x%04x\n", + __func__, ctx_data->mbx_idx, mbx_data); + + /* Exit Super I/O config mode */ + mec1308_sio_exit(ctx_data, sio_port); + + /* Now that we can read the mailbox, we will wait for any remaining + * command to finish.*/ + if (mbx_wait(ctx_data) != 0) { + msg_perr("%s: mailbox is not available\n", __func__); + ret = 1; + goto mec1308_init_exit; + } + + /* Further setup -- disable SMI and ACPI. + FIXME: is there an ordering dependency? */ + if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_ACPI_DISABLE)) { + msg_pdbg("%s: unable to disable ACPI\n", __func__); + ret = 1; + goto mec1308_init_exit; + } + + if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_SMI_DISABLE)) { + msg_pdbg("%s: unable to disable SMI\n", __func__); + ret = 1; + goto mec1308_init_exit; + } + + if (register_shutdown(mec1308_shutdown, ctx_data)) { + ret = 1; + goto mec1308_init_exit; + } + + /* + * Enter SPI Pass-Thru Mode after commands which do not require access + * to SPI ROM are complete. We'll start by doing the exit_passthru_mode + * sequence, which is benign if the EC is already in passthru mode. + */ + mec1308_exit_passthru_mode(ctx_data); + + if (enter_passthru_mode(ctx_data)) { + ret = 1; + goto mec1308_init_exit; + } + + internal_buses_supported |= BUS_LPC; /* for LPC <--> SPI bridging */ + spi_master_mec1308.data = ctx_data; + register_spi_master(&spi_master_mec1308); + msg_pdbg("%s(): successfully initialized mec1308\n", __func__); + +mec1308_init_exit: + free(p); + if (ret) + free(ctx_data); + return ret; +} +#endif diff --git a/meson.build b/meson.build index 04176f3..fc79c60 100644 --- a/meson.build +++ b/meson.build @@ -47,6 +47,7 @@ config_it8212 = get_option('config_it8212') config_linux_mtd = get_option('config_linux_mtd') config_linux_spi = get_option('config_linux_spi') +config_mec1308 = get_option('config_mec1308') config_mstarddc_spi = get_option('config_mstarddc_spi') config_nic3com = get_option('config_nic3com') config_nicintel_eeprom = get_option('config_nicintel_eeprom') @@ -214,6 +215,10 @@ srcs += 'linux_spi.c' cargs += '-DCONFIG_LINUX_SPI=1' endif +if config_mec1308 + srcs += 'mec1308.c' + cargs += '-DCONFIG_MEC1308=1' +endif if config_mstarddc_spi srcs += 'mstarddc_spi.c' cargs += '-DCONFIG_MSTARDDC_SPI=1' diff --git a/meson_options.txt b/meson_options.txt index ac48e4e..24a1911 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -19,6 +19,7 @@ option('config_it8212', type : 'boolean', value : true, description : 'ITE IT8212F PATA') option('config_linux_mtd', type : 'boolean', value : true, description : 'Linux MTD interfaces') option('config_linux_spi', type : 'boolean', value : true, description : 'Linux spidev interfaces') +option('config_mec1308', type : 'boolean', value : false, description : 'Microchip MEC1308 Embedded Controller') option('config_mstarddc_spi', type : 'boolean', value : false, description : 'MSTAR DDC support') option('config_nic3com', type : 'boolean', value : true, description : '3Com NICs') option('config_nicintel_eeprom', type : 'boolean', value : true, description : 'EEPROM on Intel NICs') diff --git a/programmer.h b/programmer.h index c5cab18..6422541 100644 --- a/programmer.h +++ b/programmer.h @@ -109,6 +109,9 @@ #if CONFIG_USBBLASTER_SPI == 1 PROGRAMMER_USBBLASTER_SPI, #endif +#if CONFIG_MEC1308 == 1 + PROGRAMMER_MEC1308, +#endif #if CONFIG_MSTARDDC_SPI == 1 PROGRAMMER_MSTARDDC_SPI, #endif @@ -689,6 +692,11 @@ /* mcp6x_spi.c */ int mcp6x_spi_init(int want_spi); +/* mec1308.c */ +#if CONFIG_MEC1308 == 1 +int mec1308_init(void); +#endif + /* sb600spi.c */ int sb600_probe_spi(struct pci_dev *dev); -- To view, visit
https://review.coreboot.org/c/flashrom/+/44541
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I2d51b4bdc0b38b6e488e71b9e774eb6232a2069e Gerrit-Change-Number: 44541 Gerrit-PatchSet: 1 Gerrit-Owner: Victor Ding <victording(a)google.com> Gerrit-MessageType: newchange
5
20
0
0
Change in flashrom[master]: Add ENE LPC programmer
by Victor Ding (Code Review)
04 Nov '21
04 Nov '21
Victor Ding has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/44580
) Change subject: Add ENE LPC programmer ...................................................................... Add ENE LPC programmer Initial support of ENE LPC interface keyboard controller. BUG=b:156140422 BRANCH=none Signed-off-by: Victor Ding <victording(a)google.com> Change-Id: I970afd8c1bd92c159c60e09f22e2f18c0433729d --- M Makefile A ene_lpc.c M flashrom.c M meson.build M meson_options.txt M programmer.h 6 files changed, 642 insertions(+), 0 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/80/44580/1 diff --git a/Makefile b/Makefile index 803529f..85b4850 100644 --- a/Makefile +++ b/Makefile @@ -170,6 +170,11 @@ else override CONFIG_DEVELOPERBOX_SPI = no endif +ifeq ($(CONFIG_ENE_LPC), yes) +UNSUPPORTED_FEATURES += CONFIG_ENE_LPC=yes +else +override CONFIG_ENE_LPC = no +endif ifeq ($(CONFIG_FT2232_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes else @@ -271,6 +276,11 @@ else override CONFIG_ATAPROMISE = no endif +ifeq ($(CONFIG_ENE_LPC), yes) +UNSUPPORTED_FEATURES += CONFIG_ENE_LPC=yes +else +override CONFIG_ENE_LPC = no +endif ifeq ($(CONFIG_IT8212), yes) UNSUPPORTED_FEATURES += CONFIG_IT8212=yes else @@ -381,6 +391,11 @@ else override CONFIG_DEVELOPERBOX_SPI = no endif +ifeq ($(CONFIG_ENE_LPC), yes) +UNSUPPORTED_FEATURES += CONFIG_ENE_LPC=yes +else +override CONFIG_ENE_LPC = no +endif ifeq ($(CONFIG_FT2232_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes else @@ -671,6 +686,9 @@ # Promise ATA controller support. CONFIG_ATAPROMISE ?= no +# ENE LPC interface keyboard controller +CONFIG_ENE_LPC ?= yes + # Always enable FT2232 SPI dongles for now. CONFIG_FT2232_SPI ?= yes @@ -855,6 +873,11 @@ NEED_LIBPCI += CONFIG_INTERNAL endif +ifeq ($(CONFIG_ENE_LPC), yes) +FEATURE_CFLAGS += -D'CONFIG_ENE_LPC=1' +PROGRAMMER_OBJS += ene_lpc.o +endif + ifeq ($(CONFIG_SERPROG), yes) FEATURE_CFLAGS += -D'CONFIG_SERPROG=1' PROGRAMMER_OBJS += serprog.o diff --git a/ene_lpc.c b/ene_lpc.c new file mode 100644 index 0000000..701e84e --- /dev/null +++ b/ene_lpc.c @@ -0,0 +1,593 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2012 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Google or the names of contributors or + * licensors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * GOOGLE INC AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * GOOGLE OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#if defined(__i386__) || defined(__x86_64__) +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> + +#include "chipdrivers.h" +#include "flash.h" +#include "programmer.h" +#include "hwaccess.h" +#include "spi.h" + +/* MCU registers */ +#define REG_EC_HWVER 0xff00 +#define REG_EC_FWVER 0xff01 +#define REG_EC_EDIID 0xff24 +#define REG_8051_CTRL 0xff14 +#define REG_EC_EXTCMD 0xff10 + +#define CPU_RESET 1 + +/* MCU SPI peripheral registers */ +#define REG_SPI_DATA 0xfeab +#define REG_SPI_COMMAND 0xfeac +#define REG_SPI_CONFIG 0xfead + +#define CFG_CSn_FORCE_LOW (1 << 4) +#define CFG_COMMAND_WRITE_ENABLE (1 << 3) +#define CFG_STATUS (1 << 1) +#define CFG_ENABLE_BUSY_STATUS_CHECK (1 << 0) + +/* Timeout */ +#define EC_COMMAND_TIMEOUT 4 +#define EC_RESTART_TIMEOUT 10 +#define ENE_SPI_DELAY_CYCLE 4 +#define EC_PAUSE_TIMEOUT 12 +#define EC_RESET_TRIES 3 + +#define ENE_KB94X_PAUSE_WAKEUP_PORT 0x64 + +#define MASK_INPUT_BUFFER_FULL 2 +#define MASK_OUTPUT_BUFFER_FULL 1 + +const int port_ene_bank = 1; +const int port_ene_offset = 2; +const int port_ene_data = 3; + +/* Supported ENE ECs, ENE_LAST should always be LAST member */ +enum ene_chip_id { + ENE_KB932 = 0, + ENE_KB94X, + ENE_LAST +}; + +/* EC state */ +enum ene_ec_state { + EC_STATE_NORMAL, + EC_STATE_IDLE, + EC_STATE_RESET, + EC_STATE_UNKNOWN +}; + +/* chip-specific parameters */ +typedef struct { + enum ene_chip_id chip_id; + uint8_t hwver; + uint8_t ediid; + uint32_t port_bios; + uint32_t port_ec_command; + uint32_t port_ec_data; + uint8_t ec_reset_cmd; + uint8_t ec_reset_data; + uint8_t ec_restart_cmd; + uint8_t ec_restart_data; + uint8_t ec_pause_cmd; + uint8_t ec_pause_data; + uint16_t ec_status_buf; + uint8_t ec_is_stopping; + uint8_t ec_is_running; + uint8_t ec_is_pausing; + uint32_t port_io_base; +} ene_chip_t; + +typedef struct +{ + /* pointer to table entry of identified chip */ + ene_chip_t *chip; + /* current ec state */ + enum ene_ec_state ec_state; + struct timeval pause_begin; +} ene_lpc_data_t; + +/* table of supported chips + parameters */ +static ene_chip_t ene_chips[] = { + { + ENE_KB932, /* chip_id */ + 0xa2, 0x02, /* hwver + ediid */ + 0x66, /* port_bios */ + 0x6c, 0x68, /* port_ec_{command,data} */ + 0x59, 0xf2, /* ec_reset_{cmd,data} */ + 0x59, 0xf9, /* ec_restart_{cmd,data} */ + 0x59, 0xf1, /* ec_pause_{cmd,data} */ + 0xf554, /* ec_status_buf */ + 0xa5, 0x00, /* ec_is_{stopping,running} masks */ + 0x33, /* ec_is_pausing mask */ + 0xfd60 /* port_io_base */ + }, + { + ENE_KB94X, /* chip_id */ + 0xa3, 0x05, /* hwver + ediid */ + 0x66, /* port_bios */ + 0x66, 0x68, /* port_ec_{command,data} */ + 0x7d, 0x10, /* ec_reset_{cmd,data} */ + 0x7f, 0x10, /* ec_restart_{cmd,data} */ + 0x7e, 0x10, /* ec_pause_{cmd,data} */ + 0xf710, /* ec_status_buf */ + 0x02, 0x00, /* ec_is_{stopping,running} masks */ + 0x01, /* ec_is_pausing mask */ + 0x0380 /* port_io_base */ + } +}; + +static void ec_command(const ene_chip_t *chip, uint8_t cmd, uint8_t data) +{ + struct timeval begin, now; + + /* Spin wait for EC input buffer empty */ + gettimeofday(&begin, NULL); + while (INB(chip->port_ec_command) & MASK_INPUT_BUFFER_FULL) { + gettimeofday(&now, NULL); + if ((now.tv_sec - begin.tv_sec) >= EC_COMMAND_TIMEOUT) { + msg_pdbg("%s: buf not empty\n", __func__); + return; + } + } + + /* Write command */ + OUTB(cmd, chip->port_ec_command); + + if (chip->chip_id == ENE_KB932) { + /* Spin wait for EC input buffer empty */ + gettimeofday(&begin, NULL); + while (INB(chip->port_ec_command) & + MASK_INPUT_BUFFER_FULL) { + gettimeofday(&now, NULL); + if ((now.tv_sec - begin.tv_sec) >= + EC_COMMAND_TIMEOUT) { + msg_pdbg("%s: buf not empty\n", __func__); + return; + } + } + /* Write data */ + OUTB(data, chip->port_ec_data); + } +} + +static uint8_t ene_read(const ene_chip_t *chip, uint16_t addr) +{ + uint8_t bank; + uint8_t offset; + uint8_t data; + uint32_t port_io_base; + + bank = addr >> 8; + offset = addr & 0xff; + port_io_base = chip->port_io_base; + + OUTB(bank, port_io_base + port_ene_bank); + OUTB(offset, port_io_base + port_ene_offset); + data = INB(port_io_base + port_ene_data); + + return data; +} + +static void ene_write(const ene_chip_t *chip, uint16_t addr, uint8_t data) +{ + uint8_t bank; + uint8_t offset; + uint32_t port_io_base; + + bank = addr >> 8; + offset = addr & 0xff; + port_io_base = chip->port_io_base; + + OUTB(bank, port_io_base + port_ene_bank); + OUTB(offset, port_io_base + port_ene_offset); + + OUTB(data, port_io_base + port_ene_data); +} + +/** + * wait_cycles, wait for n LPC bus clock cycles + * + * @param n: number of LPC cycles to wait + * @return void + */ +static void wait_cycles(const ene_chip_t *chip,int n) +{ + while (n--) + INB(chip->port_io_base + port_ene_bank); +} + +static int is_spicmd_write(uint8_t cmd) +{ + switch (cmd) { + case JEDEC_WREN: + /* Chip Write Enable */ + case JEDEC_EWSR: + /* Write Status Enable */ + case JEDEC_CE_60: + /* Chip Erase 0x60 */ + case JEDEC_CE_C7: + /* Chip Erase 0xc7 */ + case JEDEC_BE_52: + /* Block Erase 0x52 */ + case JEDEC_BE_D8: + /* Block Erase 0xd8 */ + case JEDEC_BE_D7: + /* Block Erase 0xd7 */ + case JEDEC_SE: + /* Sector Erase */ + case JEDEC_BYTE_PROGRAM: + /* Write memory byte */ + case JEDEC_AAI_WORD_PROGRAM: + /* Write AAI word */ + return 1; + } + return 0; +} + +static void ene_spi_start(const ene_chip_t *chip) +{ + int cfg; + + cfg = ene_read(chip, REG_SPI_CONFIG); + cfg |= CFG_CSn_FORCE_LOW; + cfg |= CFG_COMMAND_WRITE_ENABLE; + ene_write(chip, REG_SPI_CONFIG, cfg); + + wait_cycles(chip, ENE_SPI_DELAY_CYCLE); +} + +static void ene_spi_end(const ene_chip_t *chip) +{ + int cfg; + + cfg = ene_read(chip, REG_SPI_CONFIG); + cfg &= ~CFG_CSn_FORCE_LOW; + cfg |= CFG_COMMAND_WRITE_ENABLE; + ene_write(chip, REG_SPI_CONFIG, cfg); + + wait_cycles(chip, ENE_SPI_DELAY_CYCLE); +} + +static int ene_spi_wait(const ene_chip_t *chip) +{ + struct timeval begin, now; + + gettimeofday(&begin, NULL); + while(ene_read(chip, REG_SPI_CONFIG) & CFG_STATUS) { + gettimeofday(&now, NULL); + if ((now.tv_sec - begin.tv_sec) >= EC_COMMAND_TIMEOUT) { + msg_pdbg("%s: spi busy\n", __func__); + return 1; + } + } + return 0; +} + +static int ene_pause_ec(ene_lpc_data_t *ctx_data) +{ + struct timeval begin, now; + const ene_chip_t *chip = ctx_data->chip; + + if (!chip->ec_pause_cmd) + return -1; + + /* EC prepare pause */ + ec_command(chip, chip->ec_pause_cmd, chip->ec_pause_data); + + gettimeofday(&begin, NULL); + /* Spin wait for EC ready */ + while (ene_read(chip, chip->ec_status_buf) != + chip->ec_is_pausing) { + gettimeofday(&now, NULL); + if ((now.tv_sec - begin.tv_sec) >= + EC_COMMAND_TIMEOUT) { + msg_pdbg("%s: unable to pause ec\n", __func__); + return -1; + } + } + + + gettimeofday(&ctx_data->pause_begin, NULL); + ctx_data->ec_state = EC_STATE_IDLE; + return 0; +} + +static int ene_resume_ec(ene_lpc_data_t *ctx_data) +{ + struct timeval begin, now; + const ene_chip_t *chip = ctx_data->chip; + + if (chip->chip_id == ENE_KB94X) + OUTB(0xff, ENE_KB94X_PAUSE_WAKEUP_PORT); + else + /* Trigger 8051 interrupt to resume */ + ene_write(chip, REG_EC_EXTCMD, 0xff); + + gettimeofday(&begin, NULL); + while (ene_read(chip, chip->ec_status_buf) != + chip->ec_is_running) { + gettimeofday(&now, NULL); + if ((now.tv_sec - begin.tv_sec) >= + EC_COMMAND_TIMEOUT) { + msg_pdbg("%s: unable to resume ec\n", __func__); + return -1; + } + } + + ctx_data->ec_state = EC_STATE_NORMAL; + return 0; +} + +static int ene_pause_timeout_check(ene_lpc_data_t *ctx_data) +{ + struct timeval pause_now; + gettimeofday(&pause_now, NULL); + if ((pause_now.tv_sec - ctx_data->pause_begin.tv_sec) >= + EC_PAUSE_TIMEOUT) { + if(ene_resume_ec(ctx_data) == 0) + ene_pause_ec(ctx_data); + + } + return 0; +} + +static int ene_reset_ec(ene_lpc_data_t *ctx_data) +{ + uint8_t reg; + struct timeval begin, now; + const ene_chip_t *chip = ctx_data->chip; + + gettimeofday(&begin, NULL); + + /* EC prepare reset */ + ec_command(chip, chip->ec_reset_cmd, chip->ec_reset_data); + + /* Spin wait for EC ready */ + while (ene_read(chip, chip->ec_status_buf) != + chip->ec_is_stopping) { + gettimeofday(&now, NULL); + if ((now.tv_sec - begin.tv_sec) >= + EC_COMMAND_TIMEOUT) { + msg_pdbg("%s: unable to reset ec\n", __func__); + return -1; + } + } + + /* Wait 1 second */ + sleep(1); + + /* Reset 8051 */ + reg = ene_read(chip, REG_8051_CTRL); + reg |= CPU_RESET; + ene_write(chip, REG_8051_CTRL, reg); + + ctx_data->ec_state = EC_STATE_RESET; + return 0; +} + +static int ene_enter_flash_mode(ene_lpc_data_t *ctx_data) +{ + if (ene_pause_ec(ctx_data)) + return ene_reset_ec(ctx_data); + return 0; +} + +static int ene_spi_send_command(const struct flashctx *flash, + unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + unsigned int i; + int tries = EC_RESET_TRIES; + ene_lpc_data_t *ctx_data = (ene_lpc_data_t *)flash->mst->spi.data; + const ene_chip_t *chip = ctx_data->chip; + + if (ctx_data->ec_state == EC_STATE_IDLE && is_spicmd_write(writearr[0])) { + do { + /* Enter reset mode if we need to write/erase */ + if (ene_resume_ec(ctx_data)) + continue; + + if (!ene_reset_ec(ctx_data)) + break; + } while (--tries > 0); + + if (!tries) { + msg_perr("%s: EC failed reset, skipping write\n", __func__); + ctx_data->ec_state = EC_STATE_IDLE; + return 1; + } + } + else if(chip->chip_id == ENE_KB94X && ctx_data->ec_state == EC_STATE_IDLE) + ene_pause_timeout_check(ctx_data); + + ene_spi_start(chip); + + for (i = 0; i < writecnt; i++) { + ene_write(chip, REG_SPI_COMMAND, writearr[i]); + if (ene_spi_wait(chip)) { + msg_pdbg("%s: write count %d\n", __func__, i); + return 1; + } + } + + for (i = 0; i < readcnt; i++) { + /* Push data by clock the serial bus */ + ene_write(chip, REG_SPI_COMMAND, 0); + if (ene_spi_wait(chip)) { + msg_pdbg("%s: read count %d\n", __func__, i); + return 1; + } + readarr[i] = ene_read(chip, REG_SPI_DATA); + if (ene_spi_wait(chip)) { + msg_pdbg("%s: read count %d\n", __func__, i); + return 1; + } + } + + ene_spi_end(chip); + return 0; +} + +static int ene_leave_flash_mode(void *data) +{ + ene_lpc_data_t *ctx_data = (ene_lpc_data_t *)data; + const ene_chip_t *chip = ctx_data->chip; + int rv = 0; + uint8_t reg; + struct timeval begin, now; + + if (ctx_data->ec_state == EC_STATE_RESET) { + reg = ene_read(chip, REG_8051_CTRL); + reg &= ~CPU_RESET; + ene_write(chip, REG_8051_CTRL, reg); + + gettimeofday(&begin, NULL); + /* EC restart */ + while (ene_read(chip, chip->ec_status_buf) != + chip->ec_is_running) { + gettimeofday(&now, NULL); + if ((now.tv_sec - begin.tv_sec) >= + EC_RESTART_TIMEOUT) { + msg_pdbg("%s: ec restart busy\n", __func__); + rv = 1; + goto exit; + } + } + msg_pdbg("%s: send ec restart\n", __func__); + ec_command(chip, chip->ec_restart_cmd, + chip->ec_restart_data); + + ctx_data->ec_state = EC_STATE_NORMAL; + rv = 0; + goto exit; + } + + rv = ene_resume_ec(ctx_data); + +exit: + /* + * Trigger ec interrupt after pause/reset by sending 0x80 + * to bios command port. + */ + OUTB(0x80, chip->port_bios); + free(data); + return rv; +} + +static struct spi_master spi_master_ene = { + .max_data_read = 256, + .max_data_write = 256, + .command = ene_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = default_spi_write_256, +}; + +int ene_lpc_init() +{ + uint8_t hwver, ediid, i; + int ret = 0; + char *p = NULL; + ene_lpc_data_t *ctx_data = NULL; + + msg_pdbg("%s\n", __func__); + + ctx_data = calloc(1, sizeof(ene_lpc_data_t)); + if (!ctx_data) { + msg_perr("Unable to allocate space for extra context data.\n"); + return 1; + } + ctx_data->ec_state = EC_STATE_NORMAL; + + p = extract_programmer_param("type"); + if (p && strcmp(p, "ec")) { + msg_pdbg("ene_lpc only supports \"ec\" type devices\n"); + ret = 1; + goto ene_probe_spi_flash_exit; + } + + for (i = 0; i < ENE_LAST; ++i) { + ctx_data->chip = &ene_chips[i]; + + hwver = ene_read(ctx_data->chip, REG_EC_HWVER); + ediid = ene_read(ctx_data->chip, REG_EC_EDIID); + + if(hwver == ene_chips[i].hwver && + ediid == ene_chips[i].ediid) { + break; + } + } + + if (i == ENE_LAST) { + msg_pdbg("ENE EC not found (probe failed)\n"); + ret = 1; + goto ene_probe_spi_flash_exit; + } + + /* TODO: probe the EC stop protocol + * + * Compal - ec_command(0x41, 0xa1) returns 43 4f 4d 50 41 4c 9c + */ + + + if (register_shutdown(ene_leave_flash_mode, ctx_data)) { + ret = 1; + goto ene_probe_spi_flash_exit; + } + + ene_enter_flash_mode(ctx_data); + + internal_buses_supported |= BUS_LPC; + spi_master_ene.data = ctx_data; + register_spi_master(&spi_master_ene); + msg_pdbg("%s: successfully initialized ene\n", __func__); + +ene_probe_spi_flash_exit: + free(p); + if (ret) + free(ctx_data); + return ret; +} + +#endif /* __i386__ || __x86_64__ */ diff --git a/flashrom.c b/flashrom.c index 4a30986..35ac6f5 100644 --- a/flashrom.c +++ b/flashrom.c @@ -279,6 +279,18 @@ }, #endif +#if CONFIG_ENE_LPC == 1 + { + .name = "ene_lpc", + .type = OTHER, + .devs.note = "ENE LPC interface keyboard controller\n", + .init = ene_lpc_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + #if CONFIG_RAYER_SPI == 1 { .name = "rayer_spi", diff --git a/meson.build b/meson.build index 04176f3..180b5dd 100644 --- a/meson.build +++ b/meson.build @@ -40,6 +40,7 @@ config_digilent_spi = get_option('config_digilent_spi') config_drkaiser = get_option('config_drkaiser') config_dummy = get_option('config_dummy') +config_ene_lpc = get_option('config_ene_lpc') config_ft2232_spi = get_option('config_ft2232_spi') config_gfxnvidia = get_option('config_gfxnvidia') config_raiden = get_option('config_raiden') @@ -202,6 +203,10 @@ cargs += '-DCONFIG_INTERNAL_DMI=1' endif endif +if config_ene_lpc + srcs += 'ene_lpc.c' + cargs += '-DCONFIG_ENE_LPC=1' +endif if config_it8212 srcs += 'it8212.c' cargs += '-DCONFIG_IT8212=1' diff --git a/meson_options.txt b/meson_options.txt index ac48e4e..ba5130b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,6 +11,7 @@ option('config_digilent_spi', type : 'boolean', value : true, description : 'Digilent Development board JTAG') option('config_drkaiser', type : 'boolean', value : true, description : 'Dr. Kaiser') option('config_dummy', type : 'boolean', value : true, description : 'dummy tracing') +option('config_ene_lpc', type : 'boolean', value : true, description : 'ENE LPC interface keyboard controller') option('config_ft2232_spi', type : 'boolean', value : true, description : 'FT2232 SPI dongles') option('config_gfxnvidia', type : 'boolean', value : true, description : 'NVIDIA graphics cards') option('config_raiden', type : 'boolean', value : true, description : 'ChromiumOS Servo DUT debug board') diff --git a/programmer.h b/programmer.h index c5cab18..b3bc700 100644 --- a/programmer.h +++ b/programmer.h @@ -61,6 +61,9 @@ #if CONFIG_ATAPROMISE == 1 PROGRAMMER_ATAPROMISE, #endif +#if CONFIG_ENE_LPC == 1 + PROGRAMMER_ENE_LPC, +#endif #if CONFIG_IT8212 == 1 PROGRAMMER_IT8212, #endif @@ -578,6 +581,11 @@ extern const struct dev_entry devs_digilent_spi[]; #endif +/* ene_lpc.c */ +#if CONFIG_ENE_LPC == 1 +int ene_lpc_init(void); +#endif + /* jlink_spi.c */ #if CONFIG_JLINK_SPI == 1 int jlink_spi_init(void); -- To view, visit
https://review.coreboot.org/c/flashrom/+/44580
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I970afd8c1bd92c159c60e09f22e2f18c0433729d Gerrit-Change-Number: 44580 Gerrit-PatchSet: 1 Gerrit-Owner: Victor Ding <victording(a)google.com> Gerrit-MessageType: newchange
4
14
0
0
Change in flashrom[master]: chipset_enable.c: Mark Intel Q67 as DEP
by Angel Pons (Code Review)
01 Nov '21
01 Nov '21
Angel Pons has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/47797
) Change subject: chipset_enable.c: Mark Intel Q67 as DEP ...................................................................... chipset_enable.c: Mark Intel Q67 as DEP Tested reading, writing and erasing the internal flash chip using an HP Elite 8200 mainboard with an Intel Q67 PCH. However, since ME-enabled chipsets are marked as DEP instead of OK, this one shall also be. Change-Id: I2bd431c5c72824654b6b5b840f9af55dfe9d3554 Signed-off-by: Angel Pons <th3fanbus(a)gmail.com> --- M chipset_enable.c 1 file changed, 1 insertion(+), 1 deletion(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/97/47797/1 diff --git a/chipset_enable.c b/chipset_enable.c index 04ff9d8..5e4a547 100644 --- a/chipset_enable.c +++ b/chipset_enable.c @@ -1829,7 +1829,7 @@ {0x8086, 0x1c4b, B_FS, NT, "Intel", "HM67", enable_flash_pch6}, {0x8086, 0x1c4c, B_FS, NT, "Intel", "Q65", enable_flash_pch6}, {0x8086, 0x1c4d, B_FS, NT, "Intel", "QS67", enable_flash_pch6}, - {0x8086, 0x1c4e, B_FS, NT, "Intel", "Q67", enable_flash_pch6}, + {0x8086, 0x1c4e, B_FS, DEP, "Intel", "Q67", enable_flash_pch6}, {0x8086, 0x1c4f, B_FS, DEP, "Intel", "QM67", enable_flash_pch6}, {0x8086, 0x1c50, B_FS, NT, "Intel", "B65", enable_flash_pch6}, {0x8086, 0x1c52, B_FS, NT, "Intel", "C202", enable_flash_pch6}, -- To view, visit
https://review.coreboot.org/c/flashrom/+/47797
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I2bd431c5c72824654b6b5b840f9af55dfe9d3554 Gerrit-Change-Number: 47797 Gerrit-PatchSet: 1 Gerrit-Owner: Angel Pons <th3fanbus(a)gmail.com> Gerrit-MessageType: newchange
4
5
0
0
Change in ...flashrom[master]: freebsd_spi: Use malloc() instead of alloca()
by David Hendricks (Code Review)
10 Oct '21
10 Oct '21
David Hendricks has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/30765
Change subject: freebsd_spi: Use malloc() instead of alloca() ...................................................................... freebsd_spi: Use malloc() instead of alloca() Change-Id: I03ff2bd1e20bea014333945771b560f17387ebd6 Signed-off-by: David Hendricks <david.hendricks(a)gmail.com> --- M freebsd_spi.c 1 file changed, 7 insertions(+), 1 deletion(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/65/30765/1 diff --git a/freebsd_spi.c b/freebsd_spi.c index 215be20..7514912 100644 --- a/freebsd_spi.c +++ b/freebsd_spi.c @@ -145,7 +145,11 @@ /* FreeBSD uses a single buffer for rx and tx. Allocate a temporary one to avoid overwriting anything. */ size_t tmpcnt = readcnt + writecnt; - unsigned char *tmpbuf = alloca(tmpcnt); + unsigned char *tmpbuf = malloc(tmpcnt); + if (!tmpbuf) { + msg_perr("Out of memory!\n"); + return -1; + } bzero(tmpbuf, tmpcnt); memcpy(tmpbuf, txbuf, writecnt); @@ -164,12 +168,14 @@ if (ioctl(fd, SPIGENIOC_TRANSFER, &msg) == -1) { msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno)); + free(tmpbuf); return -1; } if (rxbuf) memcpy(rxbuf, tmpbuf + writecnt, readcnt); + free(tmpbuf); return 0; } -- To view, visit
https://review.coreboot.org/c/flashrom/+/30765
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I03ff2bd1e20bea014333945771b560f17387ebd6 Gerrit-Change-Number: 30765 Gerrit-PatchSet: 1 Gerrit-Owner: David Hendricks <david.hendricks(a)gmail.com> Gerrit-MessageType: newchange
1
1
0
0
Change in ...flashrom[master]: Add freebsd_spi programmer
by David Hendricks (Code Review)
10 Oct '21
10 Oct '21
David Hendricks has uploaded this change for review. (
https://review.coreboot.org/c/flashrom/+/30764
Change subject: Add freebsd_spi programmer ...................................................................... Add freebsd_spi programmer Based on linux_spi, using FreeBSD's spigen(4) interface. Change-Id: I4e1689416fbb309df94807f51635bc1f4b53e0c8 Signed-off-by: Greg V <greg(a)unrelenting.technology> --- M Makefile M flashrom.8.tmpl M flashrom.c A freebsd_spi.c M programmer.h 5 files changed, 246 insertions(+), 0 deletions(-) git pull ssh://review.coreboot.org:29418/flashrom refs/changes/64/30764/1 diff --git a/Makefile b/Makefile index 1ff578c..7e709dc 100644 --- a/Makefile +++ b/Makefile @@ -379,6 +379,14 @@ endif endif +ifneq ($(TARGET_OS), FreeBSD) +ifeq ($(CONFIG_FREEBSD_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_FREEBSD_SPI=yes +else +override CONFIG_FREEBSD_SPI = no +endif +endif + ############################################################################### # General architecture-specific settings. # Like above for the OS, below we verify user-supplied options depending on the target architecture. @@ -647,6 +655,9 @@ CONFIG_LINUX_MTD ?= yes CONFIG_LINUX_SPI ?= yes +# Enable FreeBSD spigen interface by default. We disable them on non-FreeBSD targets. +CONFIG_FREEBSD_SPI ?= yes + # Always enable ITE IT8212F PATA controllers for now. CONFIG_IT8212 ?= yes @@ -945,6 +956,11 @@ PROGRAMMER_OBJS += linux_spi.o endif +ifeq ($(CONFIG_FREEBSD_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_FREEBSD_SPI=1' +PROGRAMMER_OBJS += freebsd_spi.o +endif + ifeq ($(CONFIG_MSTARDDC_SPI), yes) # This is a totally ugly hack. FEATURE_CFLAGS += $(call debug_shell,grep -q "LINUX_I2C_SUPPORT := yes" .features && printf "%s" "-D'CONFIG_MSTARDDC_SPI=1'") diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index c557af7..092f070 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -319,6 +319,8 @@ .sp .BR "* linux_spi" " (for SPI flash ROMs accessible via /dev/spidevX.Y on Linux)" .sp +.BR "* freebsd_spi" " (for SPI flash ROMs accessible via /dev/spigenX.Y on FreeBSD)" +.sp .BR "* usbblaster_spi" " (for SPI flash ROMs attached to an Altera USB-Blaster compatible cable)" .sp .BR "* nicintel_eeprom" " (for SPI EEPROMs on Intel Gigabit network cards)" @@ -1065,6 +1067,25 @@ .sp Please note that the linux_spi driver only works on Linux. .SS +.BR "freebsd_spi " programmer +.IP +You have to specify the SPI controller to use with the +.sp +.B " flashrom \-p freebsd_spi:dev=/dev/spigenX.Y" +.sp +syntax where +.B /dev/spigenX.Y +is the FreeBSD device node for your SPI controller. +.sp +In case the device supports it, you can set the SPI clock frequency with the optional +.B spispeed +parameter. The frequency is parsed as kilohertz. +Example that sets the frequency to 8 MHz: +.sp +.B " flashrom \-p freebsd_spi:dev=/dev/spigenX.Y,spispeed=8000" +.sp +Please note that the freebsd_spi driver only works on FreeBSD. +.SS .BR "mstarddc_spi " programmer .IP The Display Data Channel (DDC) is an I2C bus present on VGA and DVI connectors, that allows exchanging diff --git a/flashrom.c b/flashrom.c index 59a7531..624bcfb 100644 --- a/flashrom.c +++ b/flashrom.c @@ -377,6 +377,18 @@ }, #endif +#if CONFIG_FREEBSD_SPI == 1 + { + .name = "freebsd_spi", + .type = OTHER, + .devs.note = "Device files /dev/spigen*.*\n", + .init = freebsd_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + #if CONFIG_USBBLASTER_SPI == 1 { .name = "usbblaster_spi", diff --git a/freebsd_spi.c b/freebsd_spi.c new file mode 100644 index 0000000..215be20 --- /dev/null +++ b/freebsd_spi.c @@ -0,0 +1,186 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2011 Sven Schnelle <svens(a)stackframe.org> + * Copyright (C) 2018 Greg V <greg(a)unrelenting.technology> + * + * 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; version 2 of the License. + * + * 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. + */ + +#if CONFIG_FREEBSD_SPI == 1 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/ioccom.h> +#include <sys/spigenio.h> +#include "flash.h" +#include "chipdrivers.h" +#include "programmer.h" +#include "spi.h" + +/* Tested on: + * Xunlong Orange Pi PC (Allwinner H3) */ + +/* Same as in spi(8) */ +#define DEFAULT_BUFFER_SIZE 8192 + +static int fd = -1; + +static int freebsd_spi_shutdown(void *data); +static int freebsd_spi_send_command(struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *txbuf, + unsigned char *rxbuf); +static int freebsd_spi_read(struct flashctx *flash, uint8_t *buf, + unsigned int start, unsigned int len); +static int freebsd_spi_write_256(struct flashctx *flash, const uint8_t *buf, + unsigned int start, unsigned int len); + +static const struct spi_master spi_master_freebsd = { + .type = SPI_CONTROLLER_FREEBSD, + .features = SPI_MASTER_4BA, + .max_data_read = MAX_DATA_UNSPECIFIED, /* TODO? */ + .max_data_write = MAX_DATA_UNSPECIFIED, /* TODO? */ + .command = freebsd_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = freebsd_spi_read, + .write_256 = freebsd_spi_write_256, + .write_aai = default_spi_write_aai, +}; + +int freebsd_spi_init(void) +{ + char *p, *endp, *dev; + uint32_t speed_hz = 0; + /* FIXME: make the following configurable by CLI options. */ + /* SPI mode 0 (beware this also includes: MSB first, CS active low and others */ + const uint8_t mode = 0; + + p = extract_programmer_param("spispeed"); + if (p && strlen(p)) { + speed_hz = (uint32_t)strtoul(p, &endp, 10) * 1000; + if (p == endp) { + msg_perr("%s: invalid clock: %s kHz\n", __func__, p); + free(p); + return 1; + } + } + free(p); + + dev = extract_programmer_param("dev"); + if (!dev || !strlen(dev)) { + msg_perr("No SPI device given. Use flashrom -p " + "freebsd_spi:dev=/dev/spigenX.Y\n"); + free(dev); + return 1; + } + + msg_pdbg("Using device %s\n", dev); + if ((fd = open(dev, O_RDWR)) == -1) { + msg_perr("%s: failed to open %s: %s\n", __func__, + dev, strerror(errno)); + free(dev); + return 1; + } + free(dev); + + if (register_shutdown(freebsd_spi_shutdown, NULL)) + return 1; + /* We rely on the shutdown function for cleanup from here on. */ + + if (speed_hz > 0) { + if (ioctl(fd, SPIGENIOC_SET_CLOCK_SPEED, &speed_hz) == -1) { + msg_perr("%s: failed to set speed to %d Hz: %s\n", + __func__, speed_hz, strerror(errno)); + return 1; + } + + msg_pdbg("Using %d kHz clock\n", speed_hz/1000); + } + + if (ioctl(fd, SPIGENIOC_SET_SPI_MODE, &mode) == -1) { + msg_perr("%s: failed to set SPI mode to 0x%02x: %s\n", + __func__, mode, strerror(errno)); + return 1; + } + + register_spi_master(&spi_master_freebsd); + return 0; +} + +static int freebsd_spi_shutdown(void *data) +{ + if (fd != -1) { + close(fd); + fd = -1; + } + return 0; +} + +static int freebsd_spi_send_command(struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *txbuf, + unsigned char *rxbuf) +{ + if (fd == -1) + return -1; + /* The implementation currently does not support requests that + don't start with sending a command. */ + if (writecnt == 0) + return SPI_INVALID_LENGTH; + + /* FreeBSD uses a single buffer for rx and tx. Allocate a temporary one to avoid overwriting anything. */ + size_t tmpcnt = readcnt + writecnt; + unsigned char *tmpbuf = alloca(tmpcnt); + + bzero(tmpbuf, tmpcnt); + memcpy(tmpbuf, txbuf, writecnt); + + /* Command/data separation is pretty useless, spi(8) only uses the command. */ + struct spigen_transfer msg = { + .st_command = { + .iov_base = (void*)tmpbuf, + .iov_len = tmpcnt, + }, + .st_data = { + .iov_base = NULL, + .iov_len = 0, + }, + }; + + if (ioctl(fd, SPIGENIOC_TRANSFER, &msg) == -1) { + msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno)); + return -1; + } + + if (rxbuf) + memcpy(rxbuf, tmpbuf + writecnt, readcnt); + + return 0; +} + +static int freebsd_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len) +{ + return spi_read_chunked(flash, buf, start, len, DEFAULT_BUFFER_SIZE); +} + +static int freebsd_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len) +{ + return spi_write_chunked(flash, buf, start, len, DEFAULT_BUFFER_SIZE); +} + +#endif // CONFIG_FREEBSD_SPI == 1 diff --git a/programmer.h b/programmer.h index 311992a..30687ed 100644 --- a/programmer.h +++ b/programmer.h @@ -103,6 +103,9 @@ #if CONFIG_LINUX_SPI == 1 PROGRAMMER_LINUX_SPI, #endif +#if CONFIG_FREEBSD_SPI == 1 + PROGRAMMER_FREEBSD_SPI, +#endif #if CONFIG_USBBLASTER_SPI == 1 PROGRAMMER_USBBLASTER_SPI, #endif @@ -548,6 +551,11 @@ int linux_spi_init(void); #endif +/* freebsd_spi.c */ +#if CONFIG_FREEBSD_SPI == 1 +int freebsd_spi_init(void); +#endif + /* dediprog.c */ #if CONFIG_DEDIPROG == 1 int dediprog_init(void); @@ -623,6 +631,9 @@ #if CONFIG_LINUX_SPI == 1 SPI_CONTROLLER_LINUX, #endif +#if CONFIG_FREEBSD_SPI == 1 + SPI_CONTROLLER_FREEBSD, +#endif #if CONFIG_SERPROG == 1 SPI_CONTROLLER_SERPROG, #endif -- To view, visit
https://review.coreboot.org/c/flashrom/+/30764
To unsubscribe, or for help writing mail filters, visit
https://review.coreboot.org/settings
Gerrit-Project: flashrom Gerrit-Branch: master Gerrit-Change-Id: I4e1689416fbb309df94807f51635bc1f4b53e0c8 Gerrit-Change-Number: 30764 Gerrit-PatchSet: 1 Gerrit-Owner: David Hendricks <david.hendricks(a)gmail.com> Gerrit-MessageType: newchange
1
2
0
0
← Newer
1
2
3
4
5
6
...
16
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Results per page:
10
25
50
100
200