diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 011a0b0fc22..3bf76ca5f02 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: vendor: [ novacustom ] - model: [ nv4x_adl, ns5x_adl, nv4x_tgl, ns5x_tgl, v540tu, v560tu ] + model: [ nv4x_adl, ns5x_adl, nv4x_tgl, ns5x_tgl, v540tu, v560tu, v540tnx, v560tnx ] steps: - name: Checkout EC uses: actions/checkout@v4 @@ -33,6 +33,7 @@ jobs: retention-days: 30 build_novacustom: runs-on: ubuntu-22.04 + needs: build_novacustom_ec container: image: coreboot/coreboot-sdk:2023-11-24_2731fa619b options: --user 1001 @@ -51,6 +52,10 @@ jobs: fetch-depth: 0 - name: Checkout all submodules run: git submodule update --init --recursive --checkout + - name: Obtain EC + uses: actions/download-artifact@v4 + with: + name: "dasharo-${{ matrix.vendor }}-${{ matrix.model }}-ec" - name: Obtain blobs uses: actions/checkout@v4 with: @@ -60,6 +65,7 @@ jobs: token: ${{ secrets.NCM_BLOBS_TOKEN }} - name: Build Dasharo run: | + mv ${{ matrix.vendor }}_${{ matrix.model }}_ec.rom ec.rom cp configs/config.${{ matrix.vendor }}_${{ matrix.model }} .config make olddefconfig make diff --git a/configs/config.novacustom_ns5x_adl b/configs/config.novacustom_ns5x_adl index 0d31078f730..f0c8f4db55f 100644 --- a/configs/config.novacustom_ns5x_adl +++ b/configs/config.novacustom_ns5x_adl @@ -14,6 +14,7 @@ CONFIG_EDK2_BOOTSPLASH_FILE="3rdparty/dasharo-blobs/novacustom/bootsplash.bmp" CONFIG_TPM_MEASURED_BOOT=y CONFIG_POWER_STATE_OFF_AFTER_FAILURE=y CONFIG_ENABLE_EARLY_DMA_PROTECTION=y +CONFIG_EC_SYSTEM76_EC_UPDATE=y CONFIG_HAVE_ME_BIN=y CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G=y CONFIG_PCIEXP_HOTPLUG_IO=0x2000 diff --git a/configs/config.novacustom_ns5x_tgl b/configs/config.novacustom_ns5x_tgl index 5aec6eb5562..8a053427cd4 100644 --- a/configs/config.novacustom_ns5x_tgl +++ b/configs/config.novacustom_ns5x_tgl @@ -14,6 +14,7 @@ CONFIG_TPM_MEASURED_BOOT=y CONFIG_POWER_STATE_OFF_AFTER_FAILURE=y CONFIG_INTEL_TME=y CONFIG_ENABLE_EARLY_DMA_PROTECTION=y +CONFIG_EC_SYSTEM76_EC_UPDATE=y CONFIG_HAVE_ME_BIN=y CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G=y CONFIG_PCIEXP_HOTPLUG_IO=0x2000 diff --git a/configs/config.novacustom_nv4x_adl b/configs/config.novacustom_nv4x_adl index 573818de1ef..59a8afc651f 100644 --- a/configs/config.novacustom_nv4x_adl +++ b/configs/config.novacustom_nv4x_adl @@ -15,6 +15,7 @@ CONFIG_EDK2_BOOTSPLASH_FILE="3rdparty/dasharo-blobs/novacustom/bootsplash.bmp" CONFIG_TPM_MEASURED_BOOT=y CONFIG_POWER_STATE_OFF_AFTER_FAILURE=y CONFIG_ENABLE_EARLY_DMA_PROTECTION=y +CONFIG_EC_SYSTEM76_EC_UPDATE=y CONFIG_HAVE_ME_BIN=y CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G=y CONFIG_PCIEXP_HOTPLUG_IO=0x2000 diff --git a/configs/config.novacustom_nv4x_tgl b/configs/config.novacustom_nv4x_tgl index 370286398ec..2cccba271b4 100644 --- a/configs/config.novacustom_nv4x_tgl +++ b/configs/config.novacustom_nv4x_tgl @@ -14,6 +14,7 @@ CONFIG_TPM_MEASURED_BOOT=y CONFIG_POWER_STATE_OFF_AFTER_FAILURE=y CONFIG_INTEL_TME=y CONFIG_ENABLE_EARLY_DMA_PROTECTION=y +CONFIG_EC_SYSTEM76_EC_UPDATE=y CONFIG_HAVE_ME_BIN=y CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G=y CONFIG_PCIEXP_HOTPLUG_IO=0x2000 diff --git a/src/ec/system76/ec/system76_ec.c b/src/ec/system76/ec/system76_ec.c index 7f9378dcbab..1449be2d7e8 100644 --- a/src/ec/system76/ec/system76_ec.c +++ b/src/ec/system76/ec/system76_ec.c @@ -27,6 +27,22 @@ // When command register is 0, command is complete #define CMD_FINISHED 0 +#define SPI_WREN 0x06 /* Write Enable */ +#define SPI_WRDI 0x04 /* Write Disable */ +#define SPI_RDSR 0x05 /* Read Status Register */ +#define SPI_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define SPI_AAI_WP 0xad /* Auto Address Increment Word Program */ +#define SPI_BE 0xd7 /* 1K Block Erase */ + +#define SPI_SR_WIP (1 << 0) /* Write-in-Progress */ +#define SPI_SR_WEL (1 << 1) /* Write enable */ + +#define SPI_TIMEOUT_10MS 10000 + +#define SPI_ACCESS_SIZE 252 + +#define MAX_WRITE_RETRY 3 + static inline uint8_t system76_ec_read(uint8_t addr) { return inb(SYSTEM76_EC_BASE + (uint16_t)addr); @@ -50,7 +66,7 @@ void system76_ec_flush(void) // Wait for command completion, for up to 10 milliseconds, with a // test period of 1 microsecond - wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED); + wait_us(SPI_TIMEOUT_10MS, system76_ec_read(REG_CMD) == CMD_FINISHED); system76_ec_write(CMD_PRINT_REG_LEN, 0); } @@ -70,23 +86,25 @@ uint8_t system76_ec_smfi_cmd(uint8_t cmd, uint8_t len, uint8_t *data) { int i; - if (len > SYSTEM76_EC_SIZE - 2) + if (len > SYSTEM76_EC_SIZE - 2) { + printk(BIOS_ERR, "system76_ec: Invalid command length\n"); return -1; + } // Wait for previous command completion, for up to 10 milliseconds, with a // test period of 1 microsecond - wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED); + wait_us(SPI_TIMEOUT_10MS, system76_ec_read(REG_CMD) == CMD_FINISHED); // Write data first for (i = 0; i < len; ++i) - system76_ec_write(REG_CMD + 2 + i, data[i]); + system76_ec_write(REG_DATA + i, data[i]); // Write command register, which starts command system76_ec_write(REG_CMD, cmd); // Wait for previous command completion, for up to 10 milliseconds, with a // test period of 1 microsecond - wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED); + wait_us(SPI_TIMEOUT_10MS, system76_ec_read(REG_CMD) == CMD_FINISHED); return (system76_ec_read(REG_RESULT)); } @@ -101,14 +119,14 @@ uint8_t system76_ec_read_version(uint8_t *data) // Wait for previous command completion, for up to 10 milliseconds, with a // test period of 1 microsecond - wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED); + wait_us(SPI_TIMEOUT_10MS, system76_ec_read(REG_CMD) == CMD_FINISHED); // Write command register, which starts command system76_ec_write(REG_CMD, CMD_VERSION); // Wait for previous command completion, for up to 10 milliseconds, with a // test period of 1 microsecond - wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED); + wait_us(SPI_TIMEOUT_10MS, system76_ec_read(REG_CMD) == CMD_FINISHED); result = system76_ec_read(REG_RESULT); @@ -135,14 +153,14 @@ uint8_t system76_ec_read_board(uint8_t *data) // Wait for previous command completion, for up to 10 milliseconds, with a // test period of 1 microsecond - wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED); + wait_us(SPI_TIMEOUT_10MS, system76_ec_read(REG_CMD) == CMD_FINISHED); // Write command register, which starts command system76_ec_write(REG_CMD, CMD_BOARD); // Wait for previous command completion, for up to 10 milliseconds, with a // test period of 1 microsecond - wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED); + wait_us(SPI_TIMEOUT_10MS, system76_ec_read(REG_CMD) == CMD_FINISHED); result = system76_ec_read(REG_RESULT); @@ -224,13 +242,11 @@ static uint8_t ec_spi_reset(void) { uint8_t reset_cmd = CMD_SPI_FLAG_DISABLE | CMD_SPI_FLAG_SCRATCH; - return system76_ec_smfi_cmd(CMD_SPI, 1, &reset_cmd); + return system76_ec_smfi_cmd(CMD_SPI, sizeof(reset_cmd), &reset_cmd); } -#define SPI_ACCESS_SIZE 252 - /* - * Read len bytes from EC SPI bus into dest + * Read len bytes from EC SPI bus into dest. * Returns 0 on success, <0 on error */ static int ec_spi_bus_read(uint8_t *dest, uint32_t len) @@ -245,11 +261,15 @@ static int ec_spi_bus_read(uint8_t *dest, uint32_t len) for (addr = 0; addr + SPI_ACCESS_SIZE < len; addr += SPI_ACCESS_SIZE) { read_cmd[1] = SPI_ACCESS_SIZE; - if ((rv = system76_ec_smfi_cmd(CMD_SPI, ARRAY_SIZE(read_cmd), read_cmd))) + if ((rv = system76_ec_smfi_cmd(CMD_SPI, ARRAY_SIZE(read_cmd), read_cmd))) { + printk(BIOS_ERR, "system76_ec: Failed to send read SPI bus command\n"); return -rv; + } - if (system76_ec_read(REG_DATA + 1) != read_cmd[1]) + if (system76_ec_read(REG_DATA + 1) != read_cmd[1]) { + printk(BIOS_ERR, "system76_ec: SPI bus read insufficient bytes\n"); return -1; + } for (i = 0; i < read_cmd[1]; i++) dest[addr + i] = system76_ec_read(REG_DATA + 2 + i); @@ -260,11 +280,15 @@ static int ec_spi_bus_read(uint8_t *dest, uint32_t len) read_cmd[1] = len % SPI_ACCESS_SIZE; - if ((rv = system76_ec_smfi_cmd(CMD_SPI, ARRAY_SIZE(read_cmd), read_cmd))) + if ((rv = system76_ec_smfi_cmd(CMD_SPI, ARRAY_SIZE(read_cmd), read_cmd))) { + printk(BIOS_ERR, "system76_ec: Failed to send read SPI bus command (remainder)\n"); return -rv; + } - if (system76_ec_read(REG_DATA + 1) != read_cmd[1]) + if (system76_ec_read(REG_DATA + 1) != read_cmd[1]) { + printk(BIOS_ERR, "system76_ec: SPI bus read remainder insufficient bytes\n"); return -1; + } for (i = 0; i < read_cmd[1]; i++) dest[addr + i] = system76_ec_read(REG_DATA + 2 + i); @@ -273,14 +297,14 @@ static int ec_spi_bus_read(uint8_t *dest, uint32_t len) } /* - * Write len bytes from data to EC SPI bus + * Write len bytes from data to EC SPI bus with flags. * Returns 0 on success, <0 on error */ -static int ec_spi_bus_write(uint8_t *data, uint8_t len) +static int ec_spi_bus_write_with_flags(uint8_t *data, uint8_t len, uint8_t flags) { uint32_t addr, i, rv; uint8_t write_cmd[2] = { - [0] = CMD_SPI_FLAG_SCRATCH, + [0] = CMD_SPI_FLAG_SCRATCH | flags, [1] = len, }; @@ -288,13 +312,18 @@ static int ec_spi_bus_write(uint8_t *data, uint8_t len) write_cmd[1] = SPI_ACCESS_SIZE; for (i = 0; i < write_cmd[1]; ++i) - system76_ec_write(REG_DATA + 2 + i, data[addr + i]); + system76_ec_write(REG_DATA + ARRAY_SIZE(write_cmd) + i, data[addr + i]); - if ((rv = system76_ec_smfi_cmd(CMD_SPI, ARRAY_SIZE(write_cmd), write_cmd))) + rv = system76_ec_smfi_cmd(CMD_SPI, ARRAY_SIZE(write_cmd), write_cmd); + if (rv) { + printk(BIOS_ERR, "system76_ec: Failed to send write SPI bus command\n"); return -rv; + } - if (system76_ec_read(REG_DATA + 1) != write_cmd[1]) + if (system76_ec_read(REG_DATA + 1) != write_cmd[1]) { + printk(BIOS_ERR, "system76_ec: SPI bus write insufficient bytes\n"); return -1; + } } if (addr == len) @@ -303,196 +332,455 @@ static int ec_spi_bus_write(uint8_t *data, uint8_t len) write_cmd[1] = len % SPI_ACCESS_SIZE; for (i = 0; i < write_cmd[1]; i++) - system76_ec_write(REG_DATA + 2 + i, data[addr + i]); + system76_ec_write(REG_DATA + ARRAY_SIZE(write_cmd) + i, data[addr + i]); - if ((rv = system76_ec_smfi_cmd(CMD_SPI, ARRAY_SIZE(write_cmd), write_cmd))) + rv = system76_ec_smfi_cmd(CMD_SPI, ARRAY_SIZE(write_cmd), write_cmd); + if (rv) { + printk(BIOS_ERR, "system76_ec: Failed to send write SPI bus command (remainder)\n"); return -rv; + } - if (system76_ec_read(REG_DATA + 1) != write_cmd[1]) + if (system76_ec_read(REG_DATA + 1) != write_cmd[1]) { + printk(BIOS_ERR, "system76_ec: SPI bus write remainder insufficient bytes\n"); return -1; + } return 0; } -static int ec_spi_cmd_status(void) +/* + * Write len bytes from data to EC SPI bus + * Returns 0 on success, <0 on error + */ +static int ec_spi_bus_write(uint8_t *data, uint8_t len) +{ + return ec_spi_bus_write_with_flags(data, len, 0); +} + +static int ec_spi_wait_status(uint8_t mask, uint8_t value, const char *func, + const unsigned int line) { + struct stopwatch sw; + uint8_t status; int rv; - uint8_t cmd; + uint8_t cmd = SPI_RDSR; - if ((rv = ec_spi_reset())) + rv = ec_spi_reset(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_reset failed\n", __func__); return rv; + } - cmd = 0x05; /* SPI Read Status */ + rv = ec_spi_bus_write(&cmd, sizeof(cmd)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_write failed\n", __func__); + return rv; + } + + stopwatch_init_usecs_expire(&sw, SPI_TIMEOUT_10MS); - if ((rv = ec_spi_bus_write(&cmd, 1))) + rv = ec_spi_bus_read(&status, sizeof(status)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_read failed\n", __func__); return rv; + } - if ((rv = ec_spi_bus_read(&cmd, 1))) + while ((status & mask) != value) { + if (stopwatch_expired(&sw)) { + printk(BIOS_ERR, "%s: Timeout at %s:%u\n", __func__, func, line); + ec_spi_reset(); + return -1; + } + + /* + * Status Register can be constantly read from SPI bus. + * No need to start new RDSR command. + */ + rv = ec_spi_bus_read(&status, sizeof(status)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_read failed\n", __func__); + ec_spi_reset(); + return rv; + } + } + + rv = ec_spi_reset(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_reset failed\n", __func__); return rv; + } - return cmd; + return 0; } static int ec_spi_cmd_write_enable(void) { - int status, rv; - uint8_t cmd; + int rv; + uint8_t cmd = SPI_WREN;; - if ((rv = ec_spi_reset())) + rv = ec_spi_wait_status(SPI_SR_WIP, 0, __func__, __LINE__); + if (rv) { + printk(BIOS_ERR, "%s: flash not ready\n", __func__); return rv; + } - cmd = 0x06; /* SPI Write Enable */ - - if ((rv = ec_spi_bus_write(&cmd, 1))) + rv = ec_spi_bus_write(&cmd, sizeof(cmd)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_write failed\n", __func__); return rv; + } - do { - status = ec_spi_cmd_status(); - } while (status > 0 && (status & 3) != 2); - - return 0; + return ec_spi_wait_status(SPI_SR_WIP | SPI_SR_WEL, SPI_SR_WEL, __func__, __LINE__); } static int ec_spi_cmd_write_disable(void) { - int status, rv; - uint8_t cmd; + int rv; + uint8_t cmd = SPI_WRDI; - if ((rv = ec_spi_reset())) + rv = ec_spi_reset(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_reset failed\n", __func__); return rv; + } - cmd = 0x04; /* SPI Write Disable */ - - if ((rv = ec_spi_bus_write(&cmd, 1))) + rv = ec_spi_bus_write(&cmd, sizeof(cmd)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_write failed\n", __func__); return rv; - - do { - status = ec_spi_cmd_status(); - } while (status > 0 && (status & 3) != 0); - - return 0; + } + return ec_spi_wait_status(SPI_SR_WIP | SPI_SR_WEL, 0, __func__, __LINE__); } /* Erase a sector at addr. Returns 0 on success <0 on error. */ static int ec_spi_erase_sector(uint32_t addr) { - int status, rv; + int rv; uint8_t buf[4] = { - [0] = 0xD7, /* SPI Sector Erase */ + [0] = SPI_BE, [1] = (addr >> 16) & 0xFF, [2] = (addr >> 8) & 0xFF, [3] = addr & 0xFF, }; - if ((rv = ec_spi_cmd_write_enable())) + rv = ec_spi_cmd_write_enable(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_cmd_write_enable failed\n", __func__); return rv; + } - if ((rv = ec_spi_reset())) + rv = ec_spi_reset(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_reset failed\n", __func__); return rv; + } - if ((rv = ec_spi_bus_write(buf, 4))) + rv = ec_spi_bus_write(buf, ARRAY_SIZE(buf)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_write failed\n", __func__); return rv; + } - do { - status = ec_spi_cmd_status(); - } while (status > 0 && (status & 1) != 0); + rv = ec_spi_wait_status(SPI_SR_WIP, 0, __func__, __LINE__); + if (rv) + return rv; - if ((rv = ec_spi_cmd_write_disable())) + rv = ec_spi_cmd_write_disable(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_cmd_write_disable failed\n", __func__); return rv; + } return 0; } -/* - * Erase the entire chip sector by sector. - * Chip / bulk erase doesn't work on this hardware. - * Returns erased byte count on success and <0 on error. - */ -static int ec_spi_erase_chip(void) +/* Read a sector into dest. Returns 0 on success and <0 on error. */ +static int ec_spi_read_sector(uint8_t *dest, uint32_t addr) { int rv; - uint32_t addr; + uint8_t buf[5] = { + [0] = SPI_FAST_READ, + [1] = (addr >> 16) & 0xFF, + [2] = (addr >> 8) & 0xFF, + [3] = addr & 0xFF, + [4] = 0, + }; + + rv = ec_spi_wait_status(SPI_SR_WIP, 0, __func__, __LINE__); + if (rv) + return rv; + + rv = ec_spi_bus_write(buf, ARRAY_SIZE(buf)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_write failed\n", __func__); + return rv; + } + + rv = ec_spi_bus_read(dest, SPI_SECTOR_SIZE); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_read failed\n", __func__); + return rv; + } + + rv = ec_spi_reset(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_reset failed\n", __func__); + return rv; + } + + return 0; +} + +/* Read a sector and verify if it has been erased. Returns 0 on success and <0 on error. */ +static int ec_spi_verify_erased_sector(uint32_t addr) +{ + int rv, i; + uint8_t buf[5] = { + [0] = SPI_FAST_READ, /* SPI Read */ + [1] = (addr >> 16) & 0xFF, + [2] = (addr >> 8) & 0xFF, + [3] = addr & 0xFF, + [4] = 0, + }; + uint8_t data; + + rv = ec_spi_wait_status(SPI_SR_WIP, 0, __func__, __LINE__); + if (rv) + return rv; + + rv = ec_spi_bus_write(buf, ARRAY_SIZE(buf)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_write failed\n", __func__); + return rv; + } - for (addr = 0; addr < CONFIG_EC_SYSTEM76_EC_FLASH_SIZE; addr += SPI_SECTOR_SIZE) - if ((rv = ec_spi_erase_sector(addr))) + for (i = 0; i < SPI_SECTOR_SIZE; i++) { + /* Read the sector byte after byte and compare. */ + rv = ec_spi_bus_read(&data, sizeof(data)); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_read failed\n", __func__); return rv; + } - return addr; + if (data != 0xff) { + printk(BIOS_ERR, "%s: sector at %x is not erased\n", __func__, addr); + return -1; + } + } + + rv = ec_spi_reset(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_reset failed\n", __func__); + return rv; + } + + return 0; } /* - * Program an entire chip with a given image. + * Program a chip with a given image at given address and size of data. * Returns written byte count on success and <0 on error. */ -static int ec_spi_image_write(uint8_t *image, ssize_t size) +static int ec_spi_write_at(uint32_t start, uint8_t *image, size_t size) { - int status, rv; + int rv; uint32_t addr; uint8_t buf[6] = {0}; - if ((rv = ec_spi_cmd_write_enable())) + buf[0] = SPI_AAI_WP; + buf[1] = (start >> 16) & 0xff; + buf[2] = (start >> 8) & 0xff; + buf[3] = start & 0xff; + + rv = ec_spi_cmd_write_enable(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_cmd_write_enable failed\n", __func__); return rv; + } - /* SPI AAI Word Program */ - buf[0] = 0xAD; + for (addr = start; addr < start + size; addr += 2) { - for (addr = 0; addr < size; addr += 2) { - if ((rv = ec_spi_reset())) - return rv; + /* + * ec_spi_cmd_write_enable ends with ec_spi_wait_status which always calls + * ec_spi_reset. No need to do it here again. We also reset the SPI + * (make the CS# go high) after AAI WP command* by passing the + * CMD_SPI_FLAG_DISABLE to ec_spi_bus_write_with_flags. + */ - if (addr == 0) { + if (addr == start) { /* 1st cmd bytes 1,2,3 are the address */ - buf[4] = image[0]; - buf[5] = image[1]; + buf[4] = image[addr]; + buf[5] = image[addr + 1]; - rv = ec_spi_bus_write(buf, 6); + rv = ec_spi_bus_write_with_flags(buf, 6, CMD_SPI_FLAG_DISABLE); } else { buf[1] = image[addr]; buf[2] = image[addr + 1]; - rv = ec_spi_bus_write(buf, 3); + rv = ec_spi_bus_write_with_flags(buf, 3, CMD_SPI_FLAG_DISABLE); } - if (rv) + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_bus_write failed, addr 0x%06x\n", + __func__, addr); return rv; + } - do { - status = ec_spi_cmd_status(); - } while (status > 0 && (status & 1) != 0); + /* + * From the experiments it looked like the busy bit is never set. + * It is still dangerous to not probe the bit, however, AAI programming + * is quite picky and stops after first 2 bytes if we interrupt the + * process with different command than AAI word program. + * + * rv = ec_spi_wait_status(SPI_SR_WIP, 0, __func__, __LINE__); + * if (rv) return rv; + */ } - if ((rv = ec_spi_cmd_write_disable())) + rv = ec_spi_cmd_write_disable(); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_cmd_write_disable failed\n", __func__); return rv; + } - return addr; + return addr - start; } -/* Read a sector into dest. Returns 0 on success and <0 on error. */ -static int ec_spi_read_sector(uint8_t *dest, uint32_t addr) +/* + * Program an entire chip with a given image. + * Returns written byte count on success and <0 on error. + */ +static int ec_spi_image_write(uint8_t *image, size_t size) { - int status, rv; - uint8_t buf[5] = { - [0] = 0x0B, /* SPI Read */ - [1] = (addr >> 16) & 0xFF, - [2] = (addr >> 8) & 0xFF, - [3] = addr & 0xFF, - [4] = 0, - }; + uint8_t *sector; + uint32_t addr; + uint32_t erase_addr = CONFIG_EC_SYSTEM76_EC_FLASH_SIZE; + size_t length; + int rv; - if ((rv = ec_spi_reset())) - return rv; + sector = malloc(SPI_SECTOR_SIZE); + if (!sector) + return -1; - do { - status = ec_spi_cmd_status(); - } while (status > 0 && (status & 1) != 0); + rv = 0; + addr = 0; + + while ((addr < CONFIG_EC_SYSTEM76_EC_FLASH_SIZE) && + ((addr + SPI_SECTOR_SIZE) < size)) { + rv = ec_spi_read_sector(sector, addr); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_read_sector failed, addr 0x%06x\n", + __func__, addr); + goto cleanup; + } - if ((rv = ec_spi_bus_write(buf, 5))) - return rv; + if (!memcmp(sector, image + addr, SPI_SECTOR_SIZE)) { + printk(BIOS_SPEW, "%s: Skipping identical sector, addr 0x%06x\n", + __func__, addr); + addr += SPI_SECTOR_SIZE; + continue; + } - if ((rv = ec_spi_bus_read(dest, SPI_SECTOR_SIZE))) - return rv; + rv = ec_spi_erase_sector(addr); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_erase_sector failed, addr 0x%06x\n", + __func__, addr); + if (CONFIG(VBOOT)) + vboot_fail_and_reboot(vboot_get_context(), + VB2_RECOVERY_EC_SOFTWARE_SYNC, + EC_UPDATE_ERR_ERASE); + goto cleanup; + } - return 0; + rv = ec_spi_verify_erased_sector(addr); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_verify_erased_sector failed, addr 0x%06x\n", + __func__, addr); + if (CONFIG(VBOOT)) + vboot_fail_and_reboot(vboot_get_context(), + VB2_RECOVERY_EC_SOFTWARE_SYNC, + EC_UPDATE_ERR_ERASE); + goto cleanup; + } + + rv = ec_spi_write_at(addr, image, SPI_SECTOR_SIZE); + if (rv < 0) { + printk(BIOS_ERR, "%s: ec_spi_write_at failed, addr 0x%06x (%d)\n", + __func__, addr, rv); + if (CONFIG(VBOOT)) + vboot_fail_and_reboot(vboot_get_context(), + VB2_RECOVERY_EC_SOFTWARE_SYNC, + EC_UPDATE_ERR_PROGRAM); + goto cleanup; + } + + addr += SPI_SECTOR_SIZE; + } + + /* Update any remainder bytes */ + rv = ec_spi_read_sector(sector, addr); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_read_sector failed, last addr 0x%06x\n", + __func__, addr); + goto cleanup; + } + + erase_addr = addr + SPI_SECTOR_SIZE; + + if (size % SPI_SECTOR_SIZE == 0 && addr < CONFIG_EC_SYSTEM76_EC_FLASH_SIZE) + length = SPI_SECTOR_SIZE; + else + length = size % SPI_SECTOR_SIZE; + + if (memcmp(sector, image + addr, length)) { + rv = ec_spi_erase_sector(addr); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_erase_sector failed, addr 0x%06x\n", + __func__, addr); + if (CONFIG(VBOOT)) + vboot_fail_and_reboot(vboot_get_context(), + VB2_RECOVERY_EC_SOFTWARE_SYNC, + EC_UPDATE_ERR_ERASE); + goto cleanup; + } + + rv = ec_spi_write_at(addr, image, length); + if (rv < 0) { + printk(BIOS_ERR, "%s: ec_spi_write_at failed, addr 0x%06x (%d)\n", + __func__, addr, rv); + if (CONFIG(VBOOT)) + vboot_fail_and_reboot(vboot_get_context(), + VB2_RECOVERY_EC_SOFTWARE_SYNC, + EC_UPDATE_ERR_PROGRAM); + goto cleanup; + } + + addr += length; + } else { + printk(BIOS_SPEW, "%s: Skipping identical sector, addr 0x%06x\n", + __func__, addr); + addr += length; + } + + /* Erase remaining sectors if any */ + while (erase_addr < CONFIG_EC_SYSTEM76_EC_FLASH_SIZE) { + rv = ec_spi_erase_sector(erase_addr); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_erase_sector failed, addr 0x%06x\n", + __func__, erase_addr); + if (CONFIG(VBOOT)) + vboot_fail_and_reboot(vboot_get_context(), + VB2_RECOVERY_EC_SOFTWARE_SYNC, + EC_UPDATE_ERR_ERASE); + goto cleanup; + } + erase_addr += SPI_SECTOR_SIZE; + } + +cleanup: + free(sector); + + return rv ? rv : addr; } /* Verify an image sector by sector. Returns 0 on success and <0 on error. */ @@ -501,33 +789,57 @@ static int ec_spi_image_verify(uint8_t *image, ssize_t image_sz) uint8_t *sector; uint32_t addr; int rv; + int error = 0; sector = malloc(SPI_SECTOR_SIZE); if (!sector) return -1; + rv = 0; + addr = 0; + + while ((addr < CONFIG_EC_SYSTEM76_EC_FLASH_SIZE) && + ((addr + SPI_SECTOR_SIZE) < image_sz)) { + rv = ec_spi_read_sector(sector, addr); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_read_sector failed, addr 0x%06x\n", + __func__, addr); + return rv; + } - for (addr = 0; - addr < CONFIG_EC_SYSTEM76_EC_FLASH_SIZE && addr + SPI_SECTOR_SIZE < image_sz; - addr += SPI_SECTOR_SIZE) { - if ((rv = ec_spi_read_sector(sector, addr))) - goto exit; + rv = -memcmp(sector, image + addr, SPI_SECTOR_SIZE); + if (rv) { + printk(BIOS_ERR, "%s: failed to verify sector, addr 0x%06x\n", + __func__, addr); + error = rv; + } - if ((rv = -memcmp(sector, image + addr, SPI_SECTOR_SIZE))) - goto exit; + addr += SPI_SECTOR_SIZE; } if (addr == image_sz) goto exit; - if ((rv = ec_spi_read_sector(sector, addr))) + rv = ec_spi_read_sector(sector, addr); + if (rv) { + printk(BIOS_ERR, "%s: ec_spi_read_sector failed, last addr 0x%06x\n", + __func__, addr); goto exit; + } - if ((rv = -memcmp(sector, image + addr, image_sz % SPI_SECTOR_SIZE))) + rv = -memcmp(sector, image + addr, image_sz % SPI_SECTOR_SIZE); + if (rv) { + printk(BIOS_ERR, "%s: failed to verify last sector, addr 0x%06x size 0x%lx\n", + __func__, addr, image_sz % SPI_SECTOR_SIZE); goto exit; + } exit: free(sector); + + if (error) + return error; + return rv; } @@ -541,13 +853,13 @@ static void system76_ec_fw_sync(void *unused) char cur_board_str[64]; char cur_version_str[64]; uint8_t smfi_cmd; - int status, rv; + int rv; if (!CONFIG(EC_SYSTEM76_EC_UPDATE)) return; if (vboot_recovery_mode_enabled()) { - printk(BIOS_DEBUG, "EC: skipping update in recovery mode.\n"); + printk(BIOS_INFO, "EC: skipping update in recovery mode.\n"); return; } @@ -557,15 +869,16 @@ static void system76_ec_fw_sync(void *unused) return; } - if (!(image_sz = cbfs_load("ec.rom", image, CONFIG_EC_SYSTEM76_EC_FLASH_SIZE))) { + image_sz = cbfs_load("ec.rom", image, CONFIG_EC_SYSTEM76_EC_FLASH_SIZE); + if (!image_sz) { printk(BIOS_ERR, "EC: failed to load update from CBFS."); goto cleanup; } - printk(BIOS_DEBUG, "EC: update found (%ld bytes)\n", image_sz); + printk(BIOS_INFO, "EC: update found (%ld bytes)\n", image_sz); /* Sanity checks */ - if (!image_sz || image_sz % 2 || image_sz > CONFIG_EC_SYSTEM76_EC_FLASH_SIZE) { + if (image_sz % 2 || image_sz > CONFIG_EC_SYSTEM76_EC_FLASH_SIZE) { printk(BIOS_ERR, "EC: incorrect update size.\n"); goto cleanup; } @@ -582,8 +895,8 @@ static void system76_ec_fw_sync(void *unused) goto cleanup; } - printk(BIOS_DEBUG, "EC: update target: %s\n", img_board_str); - printk(BIOS_DEBUG, "EC: update version: %s\n", img_version_str); + printk(BIOS_INFO, "EC: update target: %s\n", img_board_str); + printk(BIOS_INFO, "EC: update version: %s\n", img_version_str); system76_ec_read_board((uint8_t *)cur_board_str); system76_ec_read_version((uint8_t *)cur_version_str); @@ -594,9 +907,9 @@ static void system76_ec_fw_sync(void *unused) goto cleanup; } - printk(BIOS_DEBUG, "EC: current version: %s\n", cur_version_str); + printk(BIOS_INFO, "EC: current version: %s\n", cur_version_str); if (!strcmp(img_version_str, cur_version_str)) { - printk(BIOS_DEBUG, "EC: update not needed.\n"); + printk(BIOS_INFO, "EC: update not needed.\n"); goto cleanup; } else { printk(BIOS_INFO, "EC: update required!\n"); @@ -613,7 +926,7 @@ static void system76_ec_fw_sync(void *unused) /* Jump to Scratch ROM */ smfi_cmd = CMD_SPI_FLAG_SCRATCH; - if (system76_ec_smfi_cmd(CMD_SPI, 1, &smfi_cmd)) { + if (system76_ec_smfi_cmd(CMD_SPI, sizeof(smfi_cmd), &smfi_cmd)) { /* If we failed to jump to scratch ROM, then we can probably continue booting. */ printk(BIOS_ERR, "EC: failed to jump to scratch ROM!\n"); if (CONFIG(VBOOT)) @@ -623,63 +936,49 @@ static void system76_ec_fw_sync(void *unused) goto cleanup; } - rv = ec_spi_erase_chip(); - if (rv < 0) { - /* - * Best case, nothing was erased. - * Worst case, everything is erased and EC will boot from backup. - */ - printk(BIOS_CRIT, "EC: erase failed!\n"); - if (CONFIG(VBOOT)) - vboot_fail_and_reboot(vboot_get_context(), - VB2_RECOVERY_EC_SOFTWARE_SYNC, - EC_UPDATE_ERR_ERASE); - goto cleanup; + for (int i = 0; i < MAX_WRITE_RETRY; i++) { + rv = ec_spi_image_write((uint8_t *)image, image_sz); + if (rv < 0) { + /* EC is now in an unknown state. It may still boot from backup. */ + printk(BIOS_ALERT, "EC: update failed!\n"); + } else { + printk(BIOS_INFO, "EC: wrote %x bytes\n", rv); + } + + rv = ec_spi_image_verify((uint8_t *)image, image_sz); + if (rv < 0) { + printk(BIOS_ALERT, "EC: update verification failed! (try: %d)\n", + i + 1); + } else { + printk(BIOS_INFO, "EC: update verified.\n"); + rv = 0; + break; + } } - printk(BIOS_DEBUG, "EC: erased %d bytes\n", rv); - rv = ec_spi_image_write((uint8_t *)image, image_sz); if (rv < 0) { /* EC is now in an unknown state. It may still boot from backup. */ printk(BIOS_ALERT, "EC: update failed!\n"); if (CONFIG(VBOOT)) vboot_fail_and_reboot(vboot_get_context(), - VB2_RECOVERY_EC_SOFTWARE_SYNC, - EC_UPDATE_ERR_PROGRAM); + VB2_RECOVERY_EC_SOFTWARE_SYNC, + EC_UPDATE_ERR_PROGRAM); goto cleanup; } - printk(BIOS_DEBUG, "EC: wrote %d bytes\n", rv); - - rv = ec_spi_image_verify((uint8_t *)image, image_sz); - if (rv < 0) { - printk(BIOS_ALERT, "EC: update verification failed!\n"); - } else { - printk(BIOS_DEBUG, "EC: update verified.\n"); - } smfi_cmd = CMD_SPI_FLAG_DISABLE; - if (system76_ec_smfi_cmd(CMD_SPI, 1, &smfi_cmd)) { + if (system76_ec_smfi_cmd(CMD_SPI, sizeof(smfi_cmd), &smfi_cmd)) { printk(BIOS_ERR, "EC: failed to disable SPI bus!\n"); goto cleanup; } - /* Wait for busy bit to clear */ - do { - status = ec_spi_cmd_status(); - } while (status > 0 && (status & 1) != 0); - - /* Wait 5 seconds for good measure */ - mdelay(5000); - smfi_cmd = 0; - if (system76_ec_smfi_cmd(CMD_RESET, 1, &smfi_cmd)) { + if (system76_ec_smfi_cmd(CMD_RESET, sizeof(smfi_cmd), &smfi_cmd)) printk(BIOS_ERR, "EC: failed to trigger reset!\n"); - goto cleanup; - } cleanup: free(image); return; } -BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE, BS_ON_ENTRY, system76_ec_fw_sync, NULL); +BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_ENTRY, system76_ec_fw_sync, NULL); diff --git a/src/mainboard/clevo/adl-p/devicetree.cb b/src/mainboard/clevo/adl-p/devicetree.cb index 39135a8f36c..2420dd51a62 100644 --- a/src/mainboard/clevo/adl-p/devicetree.cb +++ b/src/mainboard/clevo/adl-p/devicetree.cb @@ -77,6 +77,9 @@ chip soc/intel/alderlake device generic 0 on end end end + device ref uart0 on # BIOS Debug Port + register "serial_io_uart_mode[PchSerialIoIndexUART0]" = "PchSerialIoPci" + end device ref i2c0 on # Touchpad I2C bus register "serial_io_i2c_mode[PchSerialIoIndexI2C0]" = "PchSerialIoPci" diff --git a/src/mainboard/clevo/adl-p/variants/ns50pu/gpio.c b/src/mainboard/clevo/adl-p/variants/ns50pu/gpio.c index 8b4726ba2af..ed1b050cf58 100644 --- a/src/mainboard/clevo/adl-p/variants/ns50pu/gpio.c +++ b/src/mainboard/clevo/adl-p/variants/ns50pu/gpio.c @@ -419,12 +419,14 @@ static const struct pad_config gpio_table[] = { /* GPP_H10 - GPIO */ /* DW0: 0x44000102, DW1: 0x00000000 */ /* DW0: (1 << 1) - IGNORED */ - PAD_CFG_GPI_TRIG_OWN(GPP_H10, NONE, DEEP, OFF, ACPI), + /* PAD_CFG_GPI_TRIG_OWN(GPP_H10, NONE, DEEP, OFF, ACPI), */ + PAD_CFG_NF(GPP_H10, NONE, DEEP, NF2), /* UART0_RXD */ /* GPP_H11 - GPIO */ /* DW0: 0x44000102, DW1: 0x00000000 */ /* DW0: (1 << 1) - IGNORED */ - PAD_CFG_GPI_TRIG_OWN(GPP_H11, NONE, DEEP, OFF, ACPI), + /* PAD_CFG_GPI_TRIG_OWN(GPP_H11, NONE, DEEP, OFF, ACPI), */ + PAD_CFG_NF(GPP_H11, NONE, DEEP, NF2), /* UART0_TXD */ /* GPP_H12 - DEVSLP0B */ /* DW0: 0x44001500, DW1: 0x00000000 */ diff --git a/src/mainboard/clevo/adl-p/variants/ns50pu/gpio_early.c b/src/mainboard/clevo/adl-p/variants/ns50pu/gpio_early.c index c80c798b040..616d17da2b8 100644 --- a/src/mainboard/clevo/adl-p/variants/ns50pu/gpio_early.c +++ b/src/mainboard/clevo/adl-p/variants/ns50pu/gpio_early.c @@ -4,8 +4,8 @@ #include static const struct pad_config early_gpio_table[] = { - PAD_CFG_NF(GPP_H10, NONE, DEEP, NF1), // UART0_RX - PAD_CFG_NF(GPP_H11, NONE, DEEP, NF1), // UART0_TX + PAD_CFG_NF(GPP_H10, NONE, DEEP, NF2), // UART0_RX + PAD_CFG_NF(GPP_H11, NONE, DEEP, NF2), // UART0_TX }; void mainboard_configure_early_gpios(void) diff --git a/src/mainboard/clevo/adl-p/variants/nv40pz/gpio_early.c b/src/mainboard/clevo/adl-p/variants/nv40pz/gpio_early.c index 66577bd2458..8503bb7925d 100644 --- a/src/mainboard/clevo/adl-p/variants/nv40pz/gpio_early.c +++ b/src/mainboard/clevo/adl-p/variants/nv40pz/gpio_early.c @@ -6,8 +6,8 @@ static const struct pad_config early_gpio_table[] = { PAD_CFG_GPO(GPP_A14, 0, DEEP), // DGPU_PWR_EN PAD_CFG_GPO(GPP_B2, 0, DEEP), // DGPU_RST#_PCH - PAD_CFG_NF(GPP_H10, NONE, DEEP, NF1), // UART2_RXD (actually UART0) - PAD_CFG_NF(GPP_H11, NONE, DEEP, NF1), // UART2_TXD (actually UART0) + PAD_CFG_NF(GPP_H10, NONE, DEEP, NF2), // UART0_RXD + PAD_CFG_NF(GPP_H11, NONE, DEEP, NF2), // UART0_TXD }; void mainboard_configure_early_gpios(void) diff --git a/src/mainboard/clevo/mtl-h/Kconfig b/src/mainboard/clevo/mtl-h/Kconfig index f5513f3e176..144bc3a0632 100644 --- a/src/mainboard/clevo/mtl-h/Kconfig +++ b/src/mainboard/clevo/mtl-h/Kconfig @@ -12,6 +12,7 @@ config BOARD_CLEVO_MTLH_COMMON select HAVE_CMOS_DEFAULT select HAVE_OPTION_TABLE select INTEL_GMA_HAVE_VBT + select INTEL_LPSS_UART_FOR_CONSOLE select MAINBOARD_HAS_TPM2 select MEMORY_MAPPED_TPM select NO_UART_ON_SUPERIO diff --git a/src/mainboard/clevo/mtl-h/bootblock.c b/src/mainboard/clevo/mtl-h/bootblock.c index 6601fc81260..7bd8493df5c 100644 --- a/src/mainboard/clevo/mtl-h/bootblock.c +++ b/src/mainboard/clevo/mtl-h/bootblock.c @@ -1,8 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include -#include +#include + +static const struct pad_config early_uart_gpio_table[] = { + /* UART0 */ + PAD_CFG_NF(GPP_H08, NONE, DEEP, NF1), /* UART0_RXD */ + PAD_CFG_NF(GPP_H09, NONE, DEEP, NF1), /* UART0_TXD */ +}; void bootblock_mainboard_early_init(void) { + gpio_configure_pads(early_uart_gpio_table, ARRAY_SIZE(early_uart_gpio_table)); } diff --git a/src/mainboard/clevo/mtl-h/variants/dgpu/gpio.c b/src/mainboard/clevo/mtl-h/variants/dgpu/gpio.c index 88217cea8e9..84811c4df2a 100644 --- a/src/mainboard/clevo/mtl-h/variants/dgpu/gpio.c +++ b/src/mainboard/clevo/mtl-h/variants/dgpu/gpio.c @@ -130,8 +130,8 @@ static const struct pad_config gpio_table[] = { PAD_CFG_NF(GPP_H05, NONE, DEEP, NF2), /* CNV_MFUART2_TXD */ PAD_CFG_NF(GPP_H06, NONE, DEEP, NF1), /* I2C3_SDA */ PAD_CFG_NF(GPP_H07, NONE, DEEP, NF1), /* I2C3_SCL */ - PAD_CFG_GPO(GPP_H08, 0, DEEP), /* GPIO */ - PAD_CFG_GPO(GPP_H09, 0, DEEP), /* GPIO */ + PAD_CFG_NF(GPP_H08, NONE, DEEP, NF1), /* UART0_RXD */ + PAD_CFG_NF(GPP_H09, NONE, DEEP, NF1), /* UART0_TXD */ PAD_CFG_GPO(GPP_H10, 0, DEEP), /* GPIO */ PAD_CFG_GPO(GPP_H11, 0, DEEP), /* GPIO */ PAD_CFG_GPO(GPP_H12, 0, DEEP), /* GPIO */ diff --git a/src/mainboard/clevo/mtl-h/variants/igpu/gpio.c b/src/mainboard/clevo/mtl-h/variants/igpu/gpio.c index bb48d64c4af..8083d301e32 100644 --- a/src/mainboard/clevo/mtl-h/variants/igpu/gpio.c +++ b/src/mainboard/clevo/mtl-h/variants/igpu/gpio.c @@ -544,11 +544,13 @@ static const struct pad_config gpio_table[] = { /* GPP_H08 - GPIO */ /* DW0: 0x44000200, DW1: 0x00000000 */ - PAD_CFG_GPO(GPP_H08, 0, DEEP), + /* PAD_CFG_GPO(GPP_H08, 0, DEEP), */ + PAD_CFG_NF(GPP_H08, NONE, DEEP, NF1), /* UART0_RXD */ /* GPP_H09 - GPIO */ /* DW0: 0x44000200, DW1: 0x00000000 */ - PAD_CFG_GPO(GPP_H09, 0, DEEP), + /* PAD_CFG_GPO(GPP_H09, 0, DEEP), */ + PAD_CFG_NF(GPP_H09, NONE, DEEP, NF1), /* UART0_TXD */ /* GPP_H10 - GPIO */ /* DW0: 0x44000200, DW1: 0x00000000 */