diff options
Diffstat (limited to 'ichspi.c')
-rw-r--r-- | ichspi.c | 1365 |
1 files changed, 844 insertions, 521 deletions
@@ -19,13 +19,12 @@ * GNU General Public License for more details. */ -#if defined(__i386__) || defined(__x86_64__) - #include <string.h> +#include <stdbool.h> #include <stdlib.h> #include "flash.h" #include "programmer.h" -#include "hwaccess.h" +#include "hwaccess_physmap.h" #include "spi.h" #include "ich_descriptors.h" @@ -47,12 +46,41 @@ * however we still treat them separately in order to reuse code. */ -/* Changed HSFC Control bits */ -#define PCH100_HSFC_FCYCLE_OFF (17 - 16) /* 1-4: FLASH Cycle */ -#define PCH100_HSFC_FCYCLE (0xf << PCH100_HSFC_FCYCLE_OFF) +/* + * HSFC Control bits + * + * FCYCLE is a 2 bit field (HSFC bits 1-2) on ICH9 and 4 bit field + * (HSFC bits 1-4) on PCH100. + * + * ICH9 and PCH100 use the same FCYCLE values for flash operations, + * however FCYCLE values above 3 are only supported by PCH100. + * + * 0: SPI Read + * 2: SPI Write + * 3: SPI Erase 4K + * 4: SPI Erase 64K + * 6: SPI RDID + * 7: SPI Write Status + * 8: SPI Read Status + */ +#define HSFC_FGO_OFF 0 +#define HSFC_FGO (0x1 << HSFC_FGO_OFF) +#define HSFC_FCYCLE_MASK(n) ((n) << HSFC_FCYCLE_OFF) +#define HSFC_FCYCLE_OFF 1 +#define HSFC_CYCLE_READ HSFC_FCYCLE_MASK(0x0) +#define HSFC_CYCLE_WRITE HSFC_FCYCLE_MASK(0x2) +#define HSFC_CYCLE_BLOCK_ERASE HSFC_FCYCLE_MASK(0x3) +#define HSFC_CYCLE_RDID HSFC_FCYCLE_MASK(0x6) +#define HSFC_CYCLE_WR_STATUS HSFC_FCYCLE_MASK(0x7) +#define HSFC_CYCLE_RD_STATUS HSFC_FCYCLE_MASK(0x8) + +/* PCH100 controller register definition */ +#define PCH100_HSFC_FCYCLE_OFF 1 +#define PCH100_HSFC_FCYCLE_BIT_WIDTH 0xf +#define PCH100_HSFC_FCYCLE HSFC_FCYCLE_MASK(PCH100_HSFC_FCYCLE_BIT_WIDTH) /* New HSFC Control bit */ -#define HSFC_WET_OFF (21 - 16) /* 5: Write Enable Type */ -#define HSFC_WET (0x1 << HSFC_WET_OFF) +#define PCH100_HSFC_WET_OFF (21 - 16) /* 5: Write Enable Type */ +#define PCH100_HSFC_WET (0x1 << PCH100_HSFC_WET_OFF) #define PCH100_FADDR_FLA 0x07ffffff @@ -107,13 +135,16 @@ #define HSFS_FLOCKDN (0x1 << HSFS_FLOCKDN_OFF) #define ICH9_REG_HSFC 0x06 /* 16 Bits Hardware Sequencing Flash Control */ -#define HSFC_FGO_OFF 0 /* 0: Flash Cycle Go */ -#define HSFC_FGO (0x1 << HSFC_FGO_OFF) -#define HSFC_FCYCLE_OFF 1 /* 1-2: FLASH Cycle */ -#define HSFC_FCYCLE (0x3 << HSFC_FCYCLE_OFF) + + /* 0: Flash Cycle Go */ + /* 1-2: FLASH Cycle */ +#define ICH9_HSFC_FCYCLE_OFF 1 +#define ICH9_HSFC_FCYCLE_BIT_WIDTH 3 +#define ICH9_HSFC_FCYCLE HSFC_FCYCLE_MASK(ICH9_HSFC_FCYCLE_BIT_WIDTH) /* 3-7: reserved */ #define HSFC_FDBC_OFF 8 /* 8-13: Flash Data Byte Count */ #define HSFC_FDBC (0x3f << HSFC_FDBC_OFF) +#define HSFC_FDBC_VAL(n) (((n) << HSFC_FDBC_OFF) & HSFC_FDBC) /* 14: reserved */ #define HSFC_SME_OFF 15 /* 15: SPI SMI# Enable */ #define HSFC_SME (0x1 << HSFC_SME_OFF) @@ -232,7 +263,7 @@ enum ich_access_protection { }; /* ICH SPI configuration lock-down. May be set during chipset enabling. */ -static int ichspi_lock = 0; +static bool ichspi_lock = false; static enum ich_chipset ich_generation = CHIPSET_ICH_UNKNOWN; static uint32_t ichspi_bbar; @@ -414,99 +445,105 @@ static void prettyprint_opcodes(OPCODES *ops) ops->preop[1]); } -#define _pprint_reg(bit, mask, off, val, sep) msg_pdbg("%s=%d" sep, #bit, (val & mask) >> off) -#define pprint_reg(reg, bit, val, sep) _pprint_reg(bit, reg##_##bit, reg##_##bit##_OFF, val, sep) +#define pprint_reg16(reg, bit, val, sep) msg_pdbg("%s=%"PRId16"" sep, #bit, (val & reg##_##bit) >> reg##_##bit##_OFF) +#define pprint_reg32(reg, bit, val, sep) msg_pdbg("%s=%"PRId32"" sep, #bit, (val & reg##_##bit) >> reg##_##bit##_OFF) static void prettyprint_ich9_reg_hsfs(uint16_t reg_val, enum ich_chipset ich_gen) { msg_pdbg("HSFS: "); - pprint_reg(HSFS, FDONE, reg_val, ", "); - pprint_reg(HSFS, FCERR, reg_val, ", "); - pprint_reg(HSFS, AEL, reg_val, ", "); + pprint_reg16(HSFS, FDONE, reg_val, ", "); + pprint_reg16(HSFS, FCERR, reg_val, ", "); + pprint_reg16(HSFS, AEL, reg_val, ", "); switch (ich_gen) { case CHIPSET_100_SERIES_SUNRISE_POINT: case CHIPSET_C620_SERIES_LEWISBURG: case CHIPSET_300_SERIES_CANNON_POINT: case CHIPSET_400_SERIES_COMET_POINT: + case CHIPSET_500_SERIES_TIGER_POINT: + case CHIPSET_ELKHART_LAKE: break; default: - pprint_reg(HSFS, BERASE, reg_val, ", "); + pprint_reg16(HSFS, BERASE, reg_val, ", "); break; } - pprint_reg(HSFS, SCIP, reg_val, ", "); + pprint_reg16(HSFS, SCIP, reg_val, ", "); switch (ich_gen) { case CHIPSET_100_SERIES_SUNRISE_POINT: case CHIPSET_C620_SERIES_LEWISBURG: case CHIPSET_300_SERIES_CANNON_POINT: case CHIPSET_400_SERIES_COMET_POINT: - pprint_reg(HSFS, PRR34_LOCKDN, reg_val, ", "); - pprint_reg(HSFS, WRSDIS, reg_val, ", "); + case CHIPSET_500_SERIES_TIGER_POINT: + case CHIPSET_ELKHART_LAKE: + pprint_reg16(HSFS, PRR34_LOCKDN, reg_val, ", "); + pprint_reg16(HSFS, WRSDIS, reg_val, ", "); break; default: break; } - pprint_reg(HSFS, FDOPSS, reg_val, ", "); - pprint_reg(HSFS, FDV, reg_val, ", "); - pprint_reg(HSFS, FLOCKDN, reg_val, "\n"); + pprint_reg16(HSFS, FDOPSS, reg_val, ", "); + pprint_reg16(HSFS, FDV, reg_val, ", "); + pprint_reg16(HSFS, FLOCKDN, reg_val, "\n"); } static void prettyprint_ich9_reg_hsfc(uint16_t reg_val, enum ich_chipset ich_gen) { msg_pdbg("HSFC: "); - pprint_reg(HSFC, FGO, reg_val, ", "); + pprint_reg16(HSFC, FGO, reg_val, ", "); switch (ich_gen) { case CHIPSET_100_SERIES_SUNRISE_POINT: case CHIPSET_C620_SERIES_LEWISBURG: case CHIPSET_300_SERIES_CANNON_POINT: case CHIPSET_400_SERIES_COMET_POINT: - _pprint_reg(HSFC, PCH100_HSFC_FCYCLE, PCH100_HSFC_FCYCLE_OFF, reg_val, ", "); - pprint_reg(HSFC, WET, reg_val, ", "); + case CHIPSET_500_SERIES_TIGER_POINT: + case CHIPSET_ELKHART_LAKE: + pprint_reg16(PCH100_HSFC, FCYCLE, reg_val, ", "); + pprint_reg16(PCH100_HSFC, WET, reg_val, ", "); break; default: - pprint_reg(HSFC, FCYCLE, reg_val, ", "); + pprint_reg16(ICH9_HSFC, FCYCLE, reg_val, ", "); break; } - pprint_reg(HSFC, FDBC, reg_val, ", "); - pprint_reg(HSFC, SME, reg_val, "\n"); + pprint_reg16(HSFC, FDBC, reg_val, ", "); + pprint_reg16(HSFC, SME, reg_val, "\n"); } static void prettyprint_ich9_reg_ssfs(uint32_t reg_val) { msg_pdbg("SSFS: "); - pprint_reg(SSFS, SCIP, reg_val, ", "); - pprint_reg(SSFS, FDONE, reg_val, ", "); - pprint_reg(SSFS, FCERR, reg_val, ", "); - pprint_reg(SSFS, AEL, reg_val, "\n"); + pprint_reg32(SSFS, SCIP, reg_val, ", "); + pprint_reg32(SSFS, FDONE, reg_val, ", "); + pprint_reg32(SSFS, FCERR, reg_val, ", "); + pprint_reg32(SSFS, AEL, reg_val, "\n"); } static void prettyprint_ich9_reg_ssfc(uint32_t reg_val) { msg_pdbg("SSFC: "); - pprint_reg(SSFC, SCGO, reg_val, ", "); - pprint_reg(SSFC, ACS, reg_val, ", "); - pprint_reg(SSFC, SPOP, reg_val, ", "); - pprint_reg(SSFC, COP, reg_val, ", "); - pprint_reg(SSFC, DBC, reg_val, ", "); - pprint_reg(SSFC, SME, reg_val, ", "); - pprint_reg(SSFC, SCF, reg_val, "\n"); + pprint_reg32(SSFC, SCGO, reg_val, ", "); + pprint_reg32(SSFC, ACS, reg_val, ", "); + pprint_reg32(SSFC, SPOP, reg_val, ", "); + pprint_reg32(SSFC, COP, reg_val, ", "); + pprint_reg32(SSFC, DBC, reg_val, ", "); + pprint_reg32(SSFC, SME, reg_val, ", "); + pprint_reg32(SSFC, SCF, reg_val, "\n"); } static void prettyprint_pch100_reg_dlock(const uint32_t reg_val) { msg_pdbg("DLOCK: "); - pprint_reg(DLOCK, BMWAG_LOCKDN, reg_val, ", "); - pprint_reg(DLOCK, BMRAG_LOCKDN, reg_val, ", "); - pprint_reg(DLOCK, SBMWAG_LOCKDN, reg_val, ", "); - pprint_reg(DLOCK, SBMRAG_LOCKDN, reg_val, ",\n "); - pprint_reg(DLOCK, PR0_LOCKDN, reg_val, ", "); - pprint_reg(DLOCK, PR1_LOCKDN, reg_val, ", "); - pprint_reg(DLOCK, PR2_LOCKDN, reg_val, ", "); - pprint_reg(DLOCK, PR3_LOCKDN, reg_val, ", "); - pprint_reg(DLOCK, PR4_LOCKDN, reg_val, ",\n "); - pprint_reg(DLOCK, SSEQ_LOCKDN, reg_val, "\n"); + pprint_reg32(DLOCK, BMWAG_LOCKDN, reg_val, ", "); + pprint_reg32(DLOCK, BMRAG_LOCKDN, reg_val, ", "); + pprint_reg32(DLOCK, SBMWAG_LOCKDN, reg_val, ", "); + pprint_reg32(DLOCK, SBMRAG_LOCKDN, reg_val, ",\n "); + pprint_reg32(DLOCK, PR0_LOCKDN, reg_val, ", "); + pprint_reg32(DLOCK, PR1_LOCKDN, reg_val, ", "); + pprint_reg32(DLOCK, PR2_LOCKDN, reg_val, ", "); + pprint_reg32(DLOCK, PR3_LOCKDN, reg_val, ", "); + pprint_reg32(DLOCK, PR4_LOCKDN, reg_val, ",\n "); + pprint_reg32(DLOCK, SSEQ_LOCKDN, reg_val, "\n"); } -static struct { +static struct swseq_data { size_t reg_ssfsc; size_t reg_preop; size_t reg_optype; @@ -555,7 +592,7 @@ static int program_opcodes(OPCODES *op, int enable_undo, enum ich_chipset ich_ge opmenu[1] |= ((uint32_t) op->opcode[a].opcode) << ((a - 4) * 8); } - msg_pdbg2("\n%s: preop=%04x optype=%04x opmenu=%08x%08x\n", __func__, preop, optype, opmenu[0], opmenu[1]); + msg_pdbg2("\n%s: preop=%04x optype=%04x opmenu=%08"PRIx32"%08"PRIx32"\n", __func__, preop, optype, opmenu[0], opmenu[1]); switch (ich_gen) { case CHIPSET_ICH7: case CHIPSET_TUNNEL_CREEK: @@ -677,7 +714,7 @@ static void ich_set_bbar(uint32_t min_addr, enum ich_chipset ich_gen) ichspi_bbar = mmio_readl(ich_spibar + bbar_off) & ~BBAR_MASK; if (ichspi_bbar) { - msg_pdbg("Reserved bits in BBAR not zero: 0x%08x\n", + msg_pdbg("Reserved bits in BBAR not zero: 0x%08"PRIx32"\n", ichspi_bbar); } min_addr &= BBAR_MASK; @@ -689,7 +726,7 @@ static void ich_set_bbar(uint32_t min_addr, enum ich_chipset ich_gen) * failed, the restore will fail as well, so no problem there. */ if (ichspi_bbar != min_addr) - msg_perr("Setting BBAR to 0x%08x failed! New value: 0x%08x.\n", + msg_perr("Setting BBAR to 0x%08"PRIx32" failed! New value: 0x%08"PRIx32".\n", min_addr, ichspi_bbar); } @@ -831,7 +868,7 @@ static void ich_read_data(uint8_t *data, int len, int reg0_off) static int ich7_run_opcode(OPCODE op, uint32_t offset, uint8_t datalength, uint8_t * data, int maxdata) { - int write_cmd = 0; + bool write_cmd = false; int timeout; uint32_t temp32; uint16_t temp16; @@ -841,12 +878,12 @@ static int ich7_run_opcode(OPCODE op, uint32_t offset, /* Is it a write command? */ if ((op.spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS) || (op.spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS)) { - write_cmd = 1; + write_cmd = true; } timeout = 100 * 60; /* 60 ms are 9.6 million cycles at 16 MHz. */ while ((REGREAD16(ICH7_REG_SPIS) & SPIS_SCIP) && --timeout) { - programmer_delay(10); + default_delay(10); } if (!timeout) { msg_perr("Error: SCIP never cleared!\n"); @@ -922,11 +959,10 @@ static int ich7_run_opcode(OPCODE op, uint32_t offset, /* Wait for Cycle Done Status or Flash Cycle Error. */ while (((REGREAD16(ICH7_REG_SPIS) & (SPIS_CDS | SPIS_FCERR)) == 0) && --timeout) { - programmer_delay(10); + default_delay(10); } if (!timeout) { - msg_perr("timeout, ICH7_REG_SPIS=0x%04x\n", - REGREAD16(ICH7_REG_SPIS)); + msg_perr("timeout, ICH7_REG_SPIS=0x%04x\n", REGREAD16(ICH7_REG_SPIS)); return 1; } @@ -949,7 +985,7 @@ static int ich7_run_opcode(OPCODE op, uint32_t offset, static int ich9_run_opcode(OPCODE op, uint32_t offset, uint8_t datalength, uint8_t * data) { - int write_cmd = 0; + bool write_cmd = false; int timeout; uint32_t temp32; uint64_t opmenu; @@ -958,12 +994,12 @@ static int ich9_run_opcode(OPCODE op, uint32_t offset, /* Is it a write command? */ if ((op.spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS) || (op.spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS)) { - write_cmd = 1; + write_cmd = true; } timeout = 100 * 60; /* 60 ms are 9.6 million cycles at 16 MHz. */ while ((REGREAD8(swseq_data.reg_ssfsc) & SSFS_SCIP) && --timeout) { - programmer_delay(10); + default_delay(10); } if (!timeout) { msg_perr("Error: SCIP never cleared!\n"); @@ -971,7 +1007,7 @@ static int ich9_run_opcode(OPCODE op, uint32_t offset, } /* Program offset in flash into FADDR while preserve the reserved bits - * and clearing the 25. address bit which is only useable in hwseq. */ + * and clearing the 25. address bit which is only usable in hwseq. */ temp32 = REGREAD32(ICH9_REG_FADDR) & ~0x01FFFFFF; REGWRITE32(ICH9_REG_FADDR, (offset & 0x00FFFFFF) | temp32); @@ -994,8 +1030,7 @@ static int ich9_run_opcode(OPCODE op, uint32_t offset, if (datalength != 0) { uint32_t datatemp; temp32 |= SSFC_DS; - datatemp = ((((uint32_t)datalength - 1) << SSFC_DBC_OFF) & - SSFC_DBC); + datatemp = ((((uint32_t)datalength - 1) << SSFC_DBC_OFF) & SSFC_DBC); temp32 |= datatemp; } @@ -1044,11 +1079,10 @@ static int ich9_run_opcode(OPCODE op, uint32_t offset, /* Wait for Cycle Done Status or Flash Cycle Error. */ while (((REGREAD32(swseq_data.reg_ssfsc) & (SSFS_FDONE | SSFS_FCERR)) == 0) && --timeout) { - programmer_delay(10); + default_delay(10); } if (!timeout) { - msg_perr("timeout, REG_SSFS=0x%08x\n", - REGREAD32(swseq_data.reg_ssfsc)); + msg_perr("timeout, REG_SSFS=0x%08"PRIx32"\n", REGREAD32(swseq_data.reg_ssfsc)); return 1; } @@ -1122,8 +1156,7 @@ static int ich_spi_send_command(const struct flashctx *flash, unsigned int write msg_pdbg("OPCODE 0x%02x has unsupported length, will not execute.\n", cmd); return SPI_INVALID_LENGTH; } else if (opcode_index == -1) { - msg_pdbg("Invalid OPCODE 0x%02x, will not execute.\n", - cmd); + msg_pdbg("Invalid OPCODE 0x%02x, will not execute.\n", cmd); return SPI_INVALID_OPCODE; } } @@ -1140,30 +1173,26 @@ static int ich_spi_send_command(const struct flashctx *flash, unsigned int write if ((opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS) && (writecnt != 4)) { msg_perr("%s: Internal command size error for opcode " - "0x%02x, got writecnt=%i, want =4\n", __func__, cmd, - writecnt); + "0x%02x, got writecnt=%i, want =4\n", __func__, cmd, writecnt); return SPI_INVALID_LENGTH; } if ((opcode->spi_type == SPI_OPCODE_TYPE_READ_NO_ADDRESS) && (writecnt != 1)) { msg_perr("%s: Internal command size error for opcode " - "0x%02x, got writecnt=%i, want =1\n", __func__, cmd, - writecnt); + "0x%02x, got writecnt=%i, want =1\n", __func__, cmd, writecnt); return SPI_INVALID_LENGTH; } if ((opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) && (writecnt < 4)) { msg_perr("%s: Internal command size error for opcode " - "0x%02x, got writecnt=%i, want >=4\n", __func__, cmd, - writecnt); + "0x%02x, got writecnt=%i, want >=4\n", __func__, cmd, writecnt); return SPI_INVALID_LENGTH; } if (((opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) || (opcode->spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS)) && (readcnt)) { msg_perr("%s: Internal command size error for opcode " - "0x%02x, got readcnt=%i, want =0\n", __func__, cmd, - readcnt); + "0x%02x, got readcnt=%i, want =0\n", __func__, cmd, readcnt); return SPI_INVALID_LENGTH; } @@ -1202,7 +1231,7 @@ static int ich_spi_send_command(const struct flashctx *flash, unsigned int write if (addr < valid_base || addr_end < addr || /* integer overflow check */ addr_end > valid_end) { - msg_perr("%s: Addressed region 0x%06x-0x%06x not in allowed range 0x%06x-0x%06x\n", + msg_perr("%s: Addressed region 0x%06"PRIx32"-0x%06"PRIx32" not in allowed range 0x%06"PRIx32"-0x%06"PRIx32"\n", __func__, addr, addr_end - 1, valid_base, valid_end - 1); return SPI_INVALID_ADDRESS; } @@ -1214,7 +1243,7 @@ static int ich_spi_send_command(const struct flashctx *flash, unsigned int write msg_pdbg("Running OPCODE 0x%02x failed ", opcode->opcode); if ((opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) || (opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS)) { - msg_pdbg("at address 0x%06x ", addr); + msg_pdbg("at address 0x%06"PRIx32" ", addr); } msg_pdbg("(payload length was %d).\n", count); @@ -1226,7 +1255,7 @@ static int ich_spi_send_command(const struct flashctx *flash, unsigned int write int i; msg_pspew("The data was:\n"); for (i = 0; i < count; i++){ - msg_pspew("%3d: 0x%02x\n", i, data[i]); + msg_pspew("%3d: 0x%02"PRIx8"\n", i, data[i]); } } } @@ -1234,19 +1263,34 @@ static int ich_spi_send_command(const struct flashctx *flash, unsigned int write return result; } -static struct hwseq_data { +#define MAX_FD_REGIONS 16 +struct fd_region { + const char* name; + enum ich_access_protection level; + uint32_t base; + uint32_t limit; +}; + +struct hwseq_data { uint32_t size_comp0; uint32_t size_comp1; uint32_t addr_mask; bool only_4k; uint32_t hsfc_fcycle; -} hwseq_data; -/* Sets FLA in FADDR to (addr & hwseq_data.addr_mask) without touching other bits. */ -static void ich_hwseq_set_addr(uint32_t addr) + struct fd_region fd_regions[MAX_FD_REGIONS]; +}; + +static struct hwseq_data *get_hwseq_data_from_context(const struct flashctx *flash) { - uint32_t addr_old = REGREAD32(ICH9_REG_FADDR) & ~hwseq_data.addr_mask; - REGWRITE32(ICH9_REG_FADDR, (addr & hwseq_data.addr_mask) | addr_old); + return flash->mst->opaque.data; +} + +/* Sets FLA in FADDR to (addr & hwseq_data->addr_mask) without touching other bits. */ +static void ich_hwseq_set_addr(uint32_t addr, uint32_t mask) +{ + uint32_t addr_old = REGREAD32(ICH9_REG_FADDR) & ~mask; + REGWRITE32(ICH9_REG_FADDR, (addr & mask) | addr_old); } /* Sets FADDR.FLA to 'addr' and returns the erase block size in bytes @@ -1256,7 +1300,7 @@ static void ich_hwseq_set_addr(uint32_t addr) * by UVSCC and LVSCC respectively. An alternative to implement this method * would be by querying FPB and the respective VSCC register directly. */ -static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr) +static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr, uint32_t addr_mask, bool only_4k) { uint8_t enc_berase; static const uint32_t dec_berase[4] = { @@ -1266,11 +1310,11 @@ static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr) 64 * 1024 }; - if (hwseq_data.only_4k) { + if (only_4k) { return 4 * 1024; } - ich_hwseq_set_addr(addr); + ich_hwseq_set_addr(addr, addr_mask); enc_berase = (REGREAD16(ICH9_REG_HSFS) & HSFS_BERASE) >> HSFS_BERASE_OFF; return dec_berase[enc_berase]; } @@ -1279,24 +1323,27 @@ static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr) Resets all error flags in HSFS. Returns 0 if the cycle completes successfully without errors within timeout us, 1 on errors. */ -static int ich_hwseq_wait_for_cycle_complete(unsigned int timeout, - unsigned int len, - enum ich_chipset ich_gen) +static int ich_hwseq_wait_for_cycle_complete(unsigned int len, enum ich_chipset ich_gen, uint32_t addr_mask) { + /* + * The SPI bus may be busy due to performing operations from other masters, hence + * introduce the long timeout of 30s to cover the worst case scenarios as well. + */ + unsigned int timeout_us = 30 * 1000 * 1000; uint16_t hsfs; uint32_t addr; - timeout /= 8; /* scale timeout duration to counter */ + timeout_us /= 8; /* scale timeout duration to counter */ while ((((hsfs = REGREAD16(ICH9_REG_HSFS)) & (HSFS_FDONE | HSFS_FCERR)) == 0) && - --timeout) { - programmer_delay(8); + --timeout_us) { + default_delay(8); } REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS)); - if (!timeout) { - addr = REGREAD32(ICH9_REG_FADDR) & hwseq_data.addr_mask; - msg_perr("Timeout error between offset 0x%08x and " - "0x%08x (= 0x%08x + %d)!\n", + if (!timeout_us) { + addr = REGREAD32(ICH9_REG_FADDR) & addr_mask; + msg_perr("Timeout error between offset 0x%08"PRIx32" and " + "0x%08"PRIx32" (= 0x%08"PRIx32" + %d)!\n", addr, addr + len - 1, addr, len - 1); prettyprint_ich9_reg_hsfs(hsfs, ich_gen); prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC), ich_gen); @@ -1304,9 +1351,9 @@ static int ich_hwseq_wait_for_cycle_complete(unsigned int timeout, } if (hsfs & HSFS_FCERR) { - addr = REGREAD32(ICH9_REG_FADDR) & hwseq_data.addr_mask; - msg_perr("Transaction error between offset 0x%08x and " - "0x%08x (= 0x%08x + %d)!\n", + addr = REGREAD32(ICH9_REG_FADDR) & addr_mask; + msg_perr("Transaction error between offset 0x%08"PRIx32" and " + "0x%08"PRIx32" (= 0x%08"PRIx32" + %d)!\n", addr, addr + len - 1, addr, len - 1); prettyprint_ich9_reg_hsfs(hsfs, ich_gen); prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC), ich_gen); @@ -1315,59 +1362,287 @@ static int ich_hwseq_wait_for_cycle_complete(unsigned int timeout, return 0; } +/* Fire up a transfer using the hardware sequencer. */ +static void ich_start_hwseq_xfer(const struct flashctx *flash, + uint32_t hsfc_cycle, uint32_t flash_addr, size_t len, + uint32_t addr_mask) +{ + /* make sure HSFC register is cleared before initiate any operation */ + uint16_t hsfc = 0; + + /* Sets flash_addr in FADDR */ + ich_hwseq_set_addr(flash_addr, addr_mask); + + /* make sure FDONE, FCERR, AEL are cleared by writing 1 to them */ + REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS)); + + /* Set up transaction parameters. */ + hsfc |= hsfc_cycle; + /* + * The number of bytes transferred is the value of `FDBC` plus 1, hence, + * subtracted 1 from the length field. + * As per Intel EDS, `0b` in the FDBC represents 1 byte while `0x3f` + * represents 64-bytes to be transferred. + */ + hsfc |= HSFC_FDBC_VAL(len - 1); + hsfc |= HSFC_FGO; /* start */ + prettyprint_ich9_reg_hsfc(hsfc, ich_generation); + REGWRITE16(ICH9_REG_HSFC, hsfc); +} + +static int ich_wait_for_hwseq_spi_cycle_complete(void) +{ + if (REGREAD8(ICH9_REG_HSFS) & HSFS_SCIP) { + msg_perr("Error: SCIP bit is unexpectedly set.\n"); + return 1; + } + return 0; +} + +/* Execute SPI flash transfer */ +static int ich_exec_sync_hwseq_xfer(const struct flashctx *flash, uint32_t hsfc_cycle, uint32_t flash_addr, + size_t len, enum ich_chipset ich_gen, uint32_t addr_mask) +{ + if (ich_wait_for_hwseq_spi_cycle_complete()) { + msg_perr("SPI Transaction Timeout due to previous operation in process!\n"); + return 1; + } + + ich_start_hwseq_xfer(flash, hsfc_cycle, flash_addr, len, addr_mask); + return ich_hwseq_wait_for_cycle_complete(len, ich_gen, addr_mask); +} + +static void ich_get_region(const struct flashctx *flash, unsigned int addr, struct flash_region *region) +{ + struct ich_descriptors desc = { 0 }; + const ssize_t nr = ich_number_of_regions(ich_generation, &desc.content); + const struct hwseq_data *hwseq_data = get_hwseq_data_from_context(flash); + const struct fd_region *fd_regions = hwseq_data->fd_regions; + + /* + * Set default values for the region. If no flash descriptor containing + * addr is found, these values will be used instead. + * + * The region start and end are constrained so that they do not overlap + * any flash descriptor regions. + */ + const char *name = ""; + region->read_prot = false; + region->write_prot = false; + region->start = 0; + region->end = flashrom_flash_getsize(flash); + + for (ssize_t i = 0; i < nr; i++) { + uint32_t base = fd_regions[i].base; + uint32_t limit = fd_regions[i].limit; + enum ich_access_protection level = fd_regions[i].level; + + if (addr < base) { + /* + * fd_regions[i] starts after addr, constrain + * region->end so that it does not overlap. + */ + region->end = min(region->end, base); + } else if (addr > limit) { + /* + * fd_regions[i] ends before addr, constrain + * region->start so that it does not overlap. + */ + region->start = max(region->start, limit + 1); + } else { + /* fd_regions[i] contains addr, copy to *region. */ + name = fd_regions[i].name; + region->start = base; + region->end = limit + 1; + region->read_prot = (level == LOCKED) || (level == READ_PROT); + region->write_prot = (level == LOCKED) || (level == WRITE_PROT); + break; + } + } + + region->name = strdup(name); +} + +/* Given RDID info, return pointer to entry in flashchips[] */ +static const struct flashchip *flash_id_to_entry(uint32_t mfg_id, uint32_t model_id) +{ + const struct flashchip *chip; + + for (chip = &flashchips[0]; chip->vendor; chip++) { + if ((chip->manufacture_id == mfg_id) && + (chip->model_id == model_id) && + (chip->probe == PROBE_SPI_RDID) && + ((chip->bustype & BUS_SPI) == BUS_SPI)) + return chip; + } + + return NULL; +} + +static int ich_hwseq_read_status(const struct flashctx *flash, enum flash_reg reg, uint8_t *value) +{ + const int len = 1; + const struct hwseq_data *hwseq_data = get_hwseq_data_from_context(flash); + + if (reg != STATUS1) { + msg_pdbg("%s: only supports STATUS1\n", __func__); + /* + * Return SPI_INVALID_OPCODE to be consistent with spi_read_register() + * and make error handling simpler even though this isn't a SPI master. + */ + return SPI_INVALID_OPCODE; + } + msg_pdbg("Reading Status register\n"); + + if (ich_exec_sync_hwseq_xfer(flash, HSFC_CYCLE_RD_STATUS, 1, len, ich_generation, + hwseq_data->addr_mask)) { + msg_perr("Reading Status register failed\n!!"); + return -1; + } + ich_read_data(value, len, ICH9_REG_FDATA0); + + return 0; +} + +static int ich_hwseq_write_status(const struct flashctx *flash, enum flash_reg reg, uint8_t value) +{ + const int len = 1; + const struct hwseq_data *hwseq_data = get_hwseq_data_from_context(flash); + + if (reg != STATUS1) { + msg_pdbg("%s: only supports STATUS1\n", __func__); + /* + * Return SPI_INVALID_OPCODE to be consistent with spi_write_register() + * and make error handling simpler even though this isn't a SPI master. + */ + return SPI_INVALID_OPCODE; + } + msg_pdbg("Writing status register\n"); + + ich_fill_data(&value, len, ICH9_REG_FDATA0); + + if (ich_exec_sync_hwseq_xfer(flash, HSFC_CYCLE_WR_STATUS, 1, len, ich_generation, + hwseq_data->addr_mask)) { + msg_perr("Writing Status register failed\n!!"); + return -1; + } + + return 0; +} + +static void ich_hwseq_get_flash_id(struct flashctx *flash, enum ich_chipset ich_gen) +{ + const struct hwseq_data *hwseq_data = get_hwseq_data_from_context(flash); + if (hwseq_data->size_comp1 != 0) { + msg_pinfo("Multiple flash components detected, skipping flash identification.\n"); + return; + } + + /* PCH100 or above is required for RDID, ICH9 does not support it. */ + if (hwseq_data->hsfc_fcycle != PCH100_HSFC_FCYCLE) { + msg_pinfo("RDID cycle not supported, skipping flash identification.\n"); + return; + } + + /* + * RDID gives 3 byte output: + * Byte 0: Manufacturer ID + * Byte 1: Model ID (MSB) + * Byte 2: Model ID (LSB) + */ + const int len = 3; + uint8_t data[len]; + + if (ich_exec_sync_hwseq_xfer(flash, HSFC_CYCLE_RDID, 1, len, ich_gen, + hwseq_data->addr_mask)) { + msg_perr("Timed out waiting for RDID to complete.\n"); + } + + ich_read_data(data, len, ICH9_REG_FDATA0); + uint32_t mfg_id = data[0]; + uint32_t model_id = (data[1] << 8) | data[2]; + + const struct flashchip *entry = flash_id_to_entry(mfg_id, model_id); + if (!entry) { + msg_pwarn("Unable to identify chip, mfg_id: 0x%02"PRIx32", " + "model_id: 0x%02"PRIx32"\n", mfg_id, model_id); + return; + } + + msg_pdbg("Chip identified: %s\n", entry->name); + + /* Update informational flash chip entries only */ + flash->chip->vendor = entry->vendor; + flash->chip->name = entry->name; + flash->chip->manufacture_id = entry->manufacture_id; + flash->chip->model_id = entry->model_id; + /* total_size read from flash descriptor */ + flash->chip->page_size = entry->page_size; + flash->chip->feature_bits = entry->feature_bits; + flash->chip->tested = entry->tested; + /* Support writeprotect */ + flash->chip->reg_bits = entry->reg_bits; + flash->chip->decode_range = entry->decode_range; +} + static int ich_hwseq_probe(struct flashctx *flash) { uint32_t total_size, boundary; uint32_t erase_size_low, size_low, erase_size_high, size_high; struct block_eraser *eraser; + const struct hwseq_data *hwseq_data = get_hwseq_data_from_context(flash); - total_size = hwseq_data.size_comp0 + hwseq_data.size_comp1; + total_size = hwseq_data->size_comp0 + hwseq_data->size_comp1; msg_cdbg("Hardware sequencing reports %d attached SPI flash chip", - (hwseq_data.size_comp1 != 0) ? 2 : 1); - if (hwseq_data.size_comp1 != 0) + (hwseq_data->size_comp1 != 0) ? 2 : 1); + if (hwseq_data->size_comp1 != 0) msg_cdbg("s with a combined"); else msg_cdbg(" with a"); - msg_cdbg(" density of %d kB.\n", total_size / 1024); + msg_cdbg(" density of %"PRId32" kB.\n", total_size / 1024); flash->chip->total_size = total_size / 1024; eraser = &(flash->chip->block_erasers[0]); - if (!hwseq_data.only_4k) + if (!hwseq_data->only_4k) boundary = (REGREAD32(ICH9_REG_FPB) & FPB_FPBA) << 12; else boundary = 0; size_high = total_size - boundary; - erase_size_high = ich_hwseq_get_erase_block_size(boundary); + erase_size_high = ich_hwseq_get_erase_block_size(boundary, hwseq_data->addr_mask, hwseq_data->only_4k); if (boundary == 0) { msg_cdbg2("There is only one partition containing the whole " - "address space (0x%06x - 0x%06x).\n", 0, size_high-1); + "address space (0x%06x - 0x%06"PRIx32").\n", 0, size_high-1); eraser->eraseblocks[0].size = erase_size_high; eraser->eraseblocks[0].count = size_high / erase_size_high; - msg_cdbg2("There are %d erase blocks with %d B each.\n", + msg_cdbg2("There are %"PRId32" erase blocks with %"PRId32" B each.\n", size_high / erase_size_high, erase_size_high); } else { - msg_cdbg2("The flash address space (0x%06x - 0x%06x) is divided " - "at address 0x%06x in two partitions.\n", + msg_cdbg2("The flash address space (0x%06x - 0x%06"PRIx32") is divided " + "at address 0x%06"PRIx32" in two partitions.\n", 0, total_size-1, boundary); size_low = total_size - size_high; - erase_size_low = ich_hwseq_get_erase_block_size(0); + erase_size_low = ich_hwseq_get_erase_block_size(0, hwseq_data->addr_mask, hwseq_data->only_4k); eraser->eraseblocks[0].size = erase_size_low; eraser->eraseblocks[0].count = size_low / erase_size_low; - msg_cdbg("The first partition ranges from 0x%06x to 0x%06x.\n", - 0, size_low-1); - msg_cdbg("In that range are %d erase blocks with %d B each.\n", + msg_cdbg("The first partition ranges from 0x%06x to 0x%06"PRIx32".\n", 0, size_low-1); + msg_cdbg("In that range are %"PRId32" erase blocks with %"PRId32" B each.\n", size_low / erase_size_low, erase_size_low); eraser->eraseblocks[1].size = erase_size_high; eraser->eraseblocks[1].count = size_high / erase_size_high; - msg_cdbg("The second partition ranges from 0x%06x to 0x%06x.\n", + msg_cdbg("The second partition ranges from 0x%06"PRIx32" to 0x%06"PRIx32".\n", boundary, total_size-1); - msg_cdbg("In that range are %d erase blocks with %d B each.\n", + msg_cdbg("In that range are %"PRId32" erase blocks with %"PRId32" B each.\n", size_high / erase_size_high, erase_size_high); } - flash->chip->tested = TEST_OK_PREW; + + /* May be overwritten by ich_hwseq_get_flash_id(). */ + flash->chip->tested = TEST_OK_PREWB; + + ich_hwseq_get_flash_id(flash, ich_generation); + return 1; } @@ -1375,12 +1650,11 @@ static int ich_hwseq_block_erase(struct flashctx *flash, unsigned int addr, unsigned int len) { uint32_t erase_block; - uint16_t hsfc; - uint32_t timeout = 5000 * 1000; /* 5 s for max 64 kB */ + const struct hwseq_data *hwseq_data = get_hwseq_data_from_context(flash); - erase_block = ich_hwseq_get_erase_block_size(addr); + erase_block = ich_hwseq_get_erase_block_size(addr, hwseq_data->addr_mask, hwseq_data->only_4k); if (len != erase_block) { - msg_cerr("Erase block size for address 0x%06x is %d B, " + msg_cerr("Erase block size for address 0x%06x is %"PRId32" B, " "but requested erase block size is %d B. " "Not erasing anything.\n", addr, erase_block, len); return -1; @@ -1390,33 +1664,21 @@ static int ich_hwseq_block_erase(struct flashctx *flash, unsigned int addr, * containing the address) we play safe here. */ if (addr % erase_block != 0) { msg_cerr("Erase address 0x%06x is not aligned to the erase " - "block boundary (any multiple of %d). " + "block boundary (any multiple of %"PRId32"). " "Not erasing anything.\n", addr, erase_block); return -1; } if (addr + len > flash->chip->total_size * 1024) { msg_perr("Request to erase some inaccessible memory address(es)" - " (addr=0x%x, len=%d). " - "Not erasing anything.\n", addr, len); + " (addr=0x%x, len=%d). Not erasing anything.\n", addr, len); return -1; } msg_pdbg("Erasing %d bytes starting at 0x%06x.\n", len, addr); - ich_hwseq_set_addr(addr); - - /* make sure FDONE, FCERR, AEL are cleared by writing 1 to them */ - REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS)); - - hsfc = REGREAD16(ICH9_REG_HSFC); - hsfc &= ~hwseq_data.hsfc_fcycle; /* clear operation */ - hsfc |= (0x3 << HSFC_FCYCLE_OFF); /* set erase operation */ - hsfc |= HSFC_FGO; /* start */ - msg_pdbg("HSFC used for block erasing: "); - prettyprint_ich9_reg_hsfc(hsfc, ich_generation); - REGWRITE16(ICH9_REG_HSFC, hsfc); - if (ich_hwseq_wait_for_cycle_complete(timeout, len, ich_generation)) + if (ich_exec_sync_hwseq_xfer(flash, HSFC_CYCLE_BLOCK_ERASE, addr, 1, ich_generation, + hwseq_data->addr_mask)) return -1; return 0; } @@ -1424,9 +1686,8 @@ static int ich_hwseq_block_erase(struct flashctx *flash, unsigned int addr, static int ich_hwseq_read(struct flashctx *flash, uint8_t *buf, unsigned int addr, unsigned int len) { - uint16_t hsfc; - uint16_t timeout = 100 * 60; uint8_t block_len; + const struct hwseq_data *hwseq_data = get_hwseq_data_from_context(flash); if (addr + len > flash->chip->total_size * 1024) { msg_perr("Request to read from an inaccessible memory address " @@ -1444,16 +1705,8 @@ static int ich_hwseq_read(struct flashctx *flash, uint8_t *buf, /* as well as flash chip page borders as demanded in the Intel datasheets. */ block_len = min(block_len, 256 - (addr & 0xFF)); - ich_hwseq_set_addr(addr); - hsfc = REGREAD16(ICH9_REG_HSFC); - hsfc &= ~hwseq_data.hsfc_fcycle; /* set read operation */ - hsfc &= ~HSFC_FDBC; /* clear byte count */ - /* set byte count */ - hsfc |= (((block_len - 1) << HSFC_FDBC_OFF) & HSFC_FDBC); - hsfc |= HSFC_FGO; /* start */ - REGWRITE16(ICH9_REG_HSFC, hsfc); - - if (ich_hwseq_wait_for_cycle_complete(timeout, block_len, ich_generation)) + if (ich_exec_sync_hwseq_xfer(flash, HSFC_CYCLE_READ, addr, block_len, ich_generation, + hwseq_data->addr_mask)) return 1; ich_read_data(buf, block_len, ICH9_REG_FDATA0); addr += block_len; @@ -1465,9 +1718,8 @@ static int ich_hwseq_read(struct flashctx *flash, uint8_t *buf, static int ich_hwseq_write(struct flashctx *flash, const uint8_t *buf, unsigned int addr, unsigned int len) { - uint16_t hsfc; - uint16_t timeout = 100 * 60; uint8_t block_len; + const struct hwseq_data *hwseq_data = get_hwseq_data_from_context(flash); if (addr + len > flash->chip->total_size * 1024) { msg_perr("Request to write to an inaccessible memory address " @@ -1480,22 +1732,14 @@ static int ich_hwseq_write(struct flashctx *flash, const uint8_t *buf, unsigned REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS)); while (len > 0) { - ich_hwseq_set_addr(addr); /* Obey programmer limit... */ block_len = min(len, flash->mst->opaque.max_data_write); /* as well as flash chip page borders as demanded in the Intel datasheets. */ block_len = min(block_len, 256 - (addr & 0xFF)); ich_fill_data(buf, block_len, ICH9_REG_FDATA0); - hsfc = REGREAD16(ICH9_REG_HSFC); - hsfc &= ~hwseq_data.hsfc_fcycle; /* clear operation */ - hsfc |= (0x2 << HSFC_FCYCLE_OFF); /* set write operation */ - hsfc &= ~HSFC_FDBC; /* clear byte count */ - /* set byte count */ - hsfc |= (((block_len - 1) << HSFC_FDBC_OFF) & HSFC_FDBC); - hsfc |= HSFC_FGO; /* start */ - REGWRITE16(ICH9_REG_HSFC, hsfc); - - if (ich_hwseq_wait_for_cycle_complete(timeout, block_len, ich_generation)) + + if (ich_exec_sync_hwseq_xfer(flash, HSFC_CYCLE_WRITE, addr, block_len, ich_generation, + hwseq_data->addr_mask)) return -1; addr += block_len; buf += block_len; @@ -1504,6 +1748,12 @@ static int ich_hwseq_write(struct flashctx *flash, const uint8_t *buf, unsigned return 0; } +static int ich_hwseq_shutdown(void *data) +{ + free(data); + return 0; +} + static int ich_spi_send_multicommand(const struct flashctx *flash, struct spi_command *cmds) { @@ -1564,6 +1814,11 @@ static int ich_spi_send_multicommand(const struct flashctx *flash, return ret; } +static bool ich_spi_probe_opcode(const struct flashctx *flash, uint8_t opcode) +{ + return find_opcode(curopcodes, opcode) >= 0; +} + #define ICH_BMWAG(x) ((x >> 24) & 0xff) #define ICH_BMRAG(x) ((x >> 16) & 0xff) #define ICH_BRWA(x) ((x >> 8) & 0xff) @@ -1573,12 +1828,12 @@ static const enum ich_access_protection access_perms_to_protection[] = { LOCKED, WRITE_PROT, READ_PROT, NO_PROT }; static const char *const access_names[] = { - "locked", "read-only", "write-only", "read-write" + "read-write", "write-only", "read-only", "locked" }; -static enum ich_access_protection ich9_handle_frap(uint32_t frap, unsigned int i) +static enum ich_access_protection ich9_handle_frap(struct fd_region *fd_regions, + uint32_t frap, unsigned int i) { - const int rwperms_unknown = ARRAY_SIZE(access_names); static const char *const region_names[] = { "Flash Descriptor", "BIOS", "Management Engine", "Gigabit Ethernet", "Platform Data", "Device Expansion", @@ -1587,46 +1842,46 @@ static enum ich_access_protection ich9_handle_frap(uint32_t frap, unsigned int i const char *const region_name = i < ARRAY_SIZE(region_names) ? region_names[i] : "unknown"; uint32_t base, limit; - int rwperms; + unsigned int rwperms_idx; + enum ich_access_protection rwperms; const int offset = i < 12 ? ICH9_REG_FREG0 + i * 4 : APL_REG_FREG12 + (i - 12) * 4; uint32_t freg = mmio_readl(ich_spibar + offset); - if (i < 8) { - rwperms = (((ICH_BRWA(frap) >> i) & 1) << 1) | - (((ICH_BRRA(frap) >> i) & 1) << 0); - } else { - /* Datasheets don't define any access bits for regions > 7. We - can't rely on the actual descriptor settings either as there - are several overrides for them (those by other masters are - not even readable by us, *shrug*). */ - rwperms = rwperms_unknown; - } - base = ICH_FREG_BASE(freg); limit = ICH_FREG_LIMIT(freg); if (base > limit || (freg == 0 && i > 0)) { /* this FREG is disabled */ - msg_pdbg2("0x%02X: 0x%08x FREG%u: %s region is unused.\n", + msg_pdbg2("0x%02X: 0x%08"PRIx32" FREG%u: %s region is unused.\n", offset, freg, i, region_name); return NO_PROT; } - msg_pdbg("0x%02X: 0x%08x ", offset, freg); - if (rwperms == 0x3) { - msg_pdbg("FREG%u: %s region (0x%08x-0x%08x) is %s.\n", i, - region_name, base, limit, access_names[rwperms]); - return NO_PROT; - } - if (rwperms == rwperms_unknown) { - msg_pdbg("FREG%u: %s region (0x%08x-0x%08x) has unknown permissions.\n", - i, region_name, base, limit); + msg_pdbg("0x%02X: 0x%08"PRIx32" ", offset, freg); + + if (i < 8) { + rwperms_idx = (((ICH_BRWA(frap) >> i) & 1) << 1) | + (((ICH_BRRA(frap) >> i) & 1) << 0); + rwperms = access_perms_to_protection[rwperms_idx]; + } else { + /* Datasheets don't define any access bits for regions > 7. We + can't rely on the actual descriptor settings either as there + are several overrides for them (those by other masters are + not even readable by us, *shrug*). */ + msg_pdbg("FREG%u: %s region (0x%08"PRIx32"-0x%08"PRIx32") has unknown permissions.\n", + i, region_name, base, limit); return NO_PROT; } - - msg_pinfo("FREG%u: %s region (0x%08x-0x%08x) is %s.\n", i, + msg_pinfo("FREG%u: %s region (0x%08"PRIx32"-0x%08"PRIx32") is %s.\n", i, region_name, base, limit, access_names[rwperms]); - return access_perms_to_protection[rwperms]; + + /* Save region attributes for use by ich_get_region(). */ + fd_regions[i].base = base; + fd_regions[i].limit = limit; + fd_regions[i].level = rwperms; + fd_regions[i].name = region_name; + + return rwperms; } /* In contrast to FRAP and the master section of the descriptor the bits @@ -1642,22 +1897,24 @@ static enum ich_access_protection ich9_handle_pr(const size_t reg_pr0, unsigned { uint8_t off = reg_pr0 + (i * 4); uint32_t pr = mmio_readl(ich_spibar + off); - unsigned int rwperms = ICH_PR_PERMS(pr); + unsigned int rwperms_idx = ICH_PR_PERMS(pr); + enum ich_access_protection rwperms = access_perms_to_protection[rwperms_idx]; /* From 5 on we have GPR registers and start from 0 again. */ const char *const prefix = i >= 5 ? "G" : ""; if (i >= 5) i -= 5; - if (rwperms == 0x3) { - msg_pdbg2("0x%02X: 0x%08x (%sPR%u is unused)\n", off, pr, prefix, i); + if (rwperms == NO_PROT) { + msg_pdbg2("0x%02"PRIX8": 0x%08"PRIx32" (%sPR%u is unused)\n", off, pr, prefix, i); return NO_PROT; } - msg_pdbg("0x%02X: 0x%08x ", off, pr); - msg_pwarn("%sPR%u: Warning: 0x%08x-0x%08x is %s.\n", prefix, i, ICH_FREG_BASE(pr), + msg_pdbg("0x%02"PRIX8": 0x%08"PRIx32" ", off, pr); + msg_pwarn("%sPR%u: Warning: 0x%08"PRIx32"-0x%08"PRIx32" is %s.\n", prefix, i, ICH_FREG_BASE(pr), ICH_FREG_LIMIT(pr), access_names[rwperms]); - return access_perms_to_protection[rwperms]; + + return rwperms; } /* Set/Clear the read and write protection enable bits of PR register @i @@ -1668,7 +1925,7 @@ static void ich9_set_pr(const size_t reg_pr0, int i, int read_prot, int write_pr uint32_t old = mmio_readl(addr); uint32_t new; - msg_gspew("PR%u is 0x%08x", i, old); + msg_gspew("PR%u is 0x%08"PRIx32"", i, old); new = old & ~((1 << PR_RP_OFF) | (1 << PR_WP_OFF)); if (read_prot) new |= (1 << PR_RP_OFF); @@ -1678,385 +1935,455 @@ static void ich9_set_pr(const size_t reg_pr0, int i, int read_prot, int write_pr msg_gspew(" already.\n"); return; } - msg_gspew(", trying to set it to 0x%08x ", new); + msg_gspew(", trying to set it to 0x%08"PRIx32" ", new); rmmio_writel(new, addr); - msg_gspew("resulted in 0x%08x.\n", mmio_readl(addr)); + msg_gspew("resulted in 0x%08"PRIx32".\n", mmio_readl(addr)); } static const struct spi_master spi_master_ich7 = { - .max_data_read = 64, - .max_data_write = 64, - .command = ich_spi_send_command, - .multicommand = ich_spi_send_multicommand, - .read = default_spi_read, - .write_256 = default_spi_write_256, - .write_aai = default_spi_write_aai, + .max_data_read = 64, + .max_data_write = 64, + .command = ich_spi_send_command, + .multicommand = ich_spi_send_multicommand, + .map_flash_region = physmap, + .unmap_flash_region = physunmap, + .read = default_spi_read, + .write_256 = default_spi_write_256, }; static const struct spi_master spi_master_ich9 = { - .max_data_read = 64, - .max_data_write = 64, - .command = ich_spi_send_command, - .multicommand = ich_spi_send_multicommand, - .read = default_spi_read, - .write_256 = default_spi_write_256, - .write_aai = default_spi_write_aai, + .max_data_read = 64, + .max_data_write = 64, + .command = ich_spi_send_command, + .multicommand = ich_spi_send_multicommand, + .map_flash_region = physmap, + .unmap_flash_region = physunmap, + .read = default_spi_read, + .write_256 = default_spi_write_256, + .probe_opcode = ich_spi_probe_opcode, }; static const struct opaque_master opaque_master_ich_hwseq = { - .max_data_read = 64, - .max_data_write = 64, - .probe = ich_hwseq_probe, - .read = ich_hwseq_read, - .write = ich_hwseq_write, - .erase = ich_hwseq_block_erase, + .max_data_read = 64, + .max_data_write = 64, + .probe = ich_hwseq_probe, + .read = ich_hwseq_read, + .write = ich_hwseq_write, + .erase = ich_hwseq_block_erase, + .read_register = ich_hwseq_read_status, + .write_register = ich_hwseq_write_status, + .get_region = ich_get_region, + .shutdown = ich_hwseq_shutdown, }; -int ich_init_spi(void *spibar, enum ich_chipset ich_gen) +static int init_ich7_spi(void *spibar, enum ich_chipset ich_gen) { unsigned int i; - uint16_t tmp2; - uint32_t tmp; - char *arg; - int ich_spi_rw_restricted = 0; - int desc_valid = 0; - struct ich_descriptors desc; - enum ich_spi_mode { - ich_auto, - ich_hwseq, - ich_swseq - } ich_spi_mode = ich_auto; - size_t num_freg, num_pr, reg_pr0; - ich_generation = ich_gen; - ich_spibar = spibar; + msg_pdbg("0x00: 0x%04"PRIx16" (SPIS)\n", mmio_readw(spibar + 0)); + msg_pdbg("0x02: 0x%04"PRIx16" (SPIC)\n", mmio_readw(spibar + 2)); + msg_pdbg("0x04: 0x%08"PRIx32" (SPIA)\n", mmio_readl(spibar + 4)); + + ichspi_bbar = mmio_readl(spibar + 0x50); + + msg_pdbg("0x50: 0x%08"PRIx32" (BBAR)\n", ichspi_bbar); + msg_pdbg("0x54: 0x%04"PRIx16" (PREOP)\n", mmio_readw(spibar + 0x54)); + msg_pdbg("0x56: 0x%04"PRIx16" (OPTYPE)\n", mmio_readw(spibar + 0x56)); + msg_pdbg("0x58: 0x%08"PRIx32" (OPMENU)\n", mmio_readl(spibar + 0x58)); + msg_pdbg("0x5c: 0x%08"PRIx32" (OPMENU+4)\n", mmio_readl(spibar + 0x5c)); + + for (i = 0; i < 3; i++) { + int offs; + offs = 0x60 + (i * 4); + msg_pdbg("0x%02x: 0x%08"PRIx32" (PBR%u)\n", offs, mmio_readl(spibar + offs), i); + } + if (mmio_readw(spibar) & (1 << 15)) { + msg_pwarn("WARNING: SPI Configuration Lockdown activated.\n"); + ichspi_lock = true; + } + ich_init_opcodes(ich_gen); + ich_set_bbar(0, ich_gen); + register_spi_master(&spi_master_ich7, NULL); + + return 0; +} + +enum ich_spi_mode { + ich_auto, + ich_hwseq, + ich_swseq +}; + +static int get_ich_spi_mode_param(const struct programmer_cfg *cfg, enum ich_spi_mode *ich_spi_mode) +{ + char *const arg = extract_programmer_param_str(cfg, "ich_spi_mode"); + if (!arg) { + return 0; + } else if (!strcmp(arg, "hwseq")) { + *ich_spi_mode = ich_hwseq; + msg_pspew("user selected hwseq\n"); + } else if (!strcmp(arg, "swseq")) { + *ich_spi_mode = ich_swseq; + msg_pspew("user selected swseq\n"); + } else if (!strcmp(arg, "auto")) { + msg_pspew("user selected auto\n"); + *ich_spi_mode = ich_auto; + } else if (!strlen(arg)) { + msg_perr("Missing argument for ich_spi_mode.\n"); + free(arg); + return ERROR_FLASHROM_FATAL; + } else { + msg_perr("Unknown argument for ich_spi_mode: %s\n", arg); + free(arg); + return ERROR_FLASHROM_FATAL; + } + free(arg); - memset(&desc, 0x00, sizeof(struct ich_descriptors)); + return 0; +} +static void init_chipset_properties(struct swseq_data *swseq, struct hwseq_data *hwseq, + size_t *num_freg, size_t *num_pr, size_t *reg_pr0, + enum ich_chipset ich_gen) +{ /* Moving registers / bits */ switch (ich_gen) { case CHIPSET_100_SERIES_SUNRISE_POINT: case CHIPSET_C620_SERIES_LEWISBURG: case CHIPSET_300_SERIES_CANNON_POINT: case CHIPSET_400_SERIES_COMET_POINT: + case CHIPSET_500_SERIES_TIGER_POINT: + case CHIPSET_600_SERIES_ALDER_POINT: + case CHIPSET_METEOR_LAKE: case CHIPSET_APOLLO_LAKE: - num_pr = 6; /* Includes GPR0 */ - reg_pr0 = PCH100_REG_FPR0; - swseq_data.reg_ssfsc = PCH100_REG_SSFSC; - swseq_data.reg_preop = PCH100_REG_PREOP; - swseq_data.reg_optype = PCH100_REG_OPTYPE; - swseq_data.reg_opmenu = PCH100_REG_OPMENU; - hwseq_data.addr_mask = PCH100_FADDR_FLA; - hwseq_data.only_4k = true; - hwseq_data.hsfc_fcycle = PCH100_HSFC_FCYCLE; + case CHIPSET_GEMINI_LAKE: + case CHIPSET_JASPER_LAKE: + case CHIPSET_ELKHART_LAKE: + *num_pr = 6; /* Includes GPR0 */ + *reg_pr0 = PCH100_REG_FPR0; + swseq->reg_ssfsc = PCH100_REG_SSFSC; + swseq->reg_preop = PCH100_REG_PREOP; + swseq->reg_optype = PCH100_REG_OPTYPE; + swseq->reg_opmenu = PCH100_REG_OPMENU; + hwseq->addr_mask = PCH100_FADDR_FLA; + hwseq->only_4k = true; + hwseq->hsfc_fcycle = PCH100_HSFC_FCYCLE; break; default: - num_pr = 5; - reg_pr0 = ICH9_REG_PR0; - swseq_data.reg_ssfsc = ICH9_REG_SSFS; - swseq_data.reg_preop = ICH9_REG_PREOP; - swseq_data.reg_optype = ICH9_REG_OPTYPE; - swseq_data.reg_opmenu = ICH9_REG_OPMENU; - hwseq_data.addr_mask = ICH9_FADDR_FLA; - hwseq_data.only_4k = false; - hwseq_data.hsfc_fcycle = HSFC_FCYCLE; + *num_pr = 5; + *reg_pr0 = ICH9_REG_PR0; + swseq->reg_ssfsc = ICH9_REG_SSFS; + swseq->reg_preop = ICH9_REG_PREOP; + swseq->reg_optype = ICH9_REG_OPTYPE; + swseq->reg_opmenu = ICH9_REG_OPMENU; + hwseq->addr_mask = ICH9_FADDR_FLA; + hwseq->only_4k = false; + hwseq->hsfc_fcycle = ICH9_HSFC_FCYCLE; break; } + switch (ich_gen) { case CHIPSET_100_SERIES_SUNRISE_POINT: - num_freg = 10; + *num_freg = 10; break; case CHIPSET_C620_SERIES_LEWISBURG: - num_freg = 12; /* 12 MMIO regs, but 16 regions in FD spec */ + *num_freg = 12; /* 12 MMIO regs, but 16 regions in FD spec */ break; case CHIPSET_300_SERIES_CANNON_POINT: case CHIPSET_400_SERIES_COMET_POINT: + case CHIPSET_500_SERIES_TIGER_POINT: + case CHIPSET_600_SERIES_ALDER_POINT: + case CHIPSET_METEOR_LAKE: case CHIPSET_APOLLO_LAKE: - num_freg = 16; + case CHIPSET_GEMINI_LAKE: + case CHIPSET_JASPER_LAKE: + case CHIPSET_ELKHART_LAKE: + *num_freg = 16; break; default: - num_freg = 5; + *num_freg = 5; break; } +} + +static int init_ich_default(const struct programmer_cfg *cfg, void *spibar, enum ich_chipset ich_gen) +{ + unsigned int i; + uint16_t tmp2; + uint32_t tmp; + int ich_spi_rw_restricted = 0; + bool desc_valid = false; + struct ich_descriptors desc = { 0 }; + enum ich_spi_mode ich_spi_mode = ich_auto; + size_t num_freg, num_pr, reg_pr0; + struct hwseq_data hwseq_data = { 0 }; + init_chipset_properties(&swseq_data, &hwseq_data, &num_freg, &num_pr, ®_pr0, ich_gen); + + int ret = get_ich_spi_mode_param(cfg, &ich_spi_mode); + if (ret) + return ret; + + tmp2 = mmio_readw(spibar + ICH9_REG_HSFS); + msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2); + prettyprint_ich9_reg_hsfs(tmp2, ich_gen); + if (tmp2 & HSFS_FLOCKDN) { + msg_pinfo("SPI Configuration is locked down.\n"); + ichspi_lock = true; + } + if (tmp2 & HSFS_FDV) + desc_valid = true; + if (!(tmp2 & HSFS_FDOPSS) && desc_valid) + msg_pinfo("The Flash Descriptor Override Strap-Pin is set. Restrictions implied by\n" + "the Master Section of the flash descriptor are NOT in effect. Please note\n" + "that Protected Range (PR) restrictions still apply.\n"); + ich_init_opcodes(ich_gen); + + if (desc_valid) { + tmp2 = mmio_readw(spibar + ICH9_REG_HSFC); + msg_pdbg("0x06: 0x%04"PRIx16" (HSFC)\n", tmp2); + prettyprint_ich9_reg_hsfc(tmp2, ich_gen); + } + + tmp = mmio_readl(spibar + ICH9_REG_FADDR); + msg_pdbg2("0x08: 0x%08"PRIx32" (FADDR)\n", tmp); switch (ich_gen) { - case CHIPSET_ICH7: - case CHIPSET_TUNNEL_CREEK: - case CHIPSET_CENTERTON: - msg_pdbg("0x00: 0x%04x (SPIS)\n", - mmio_readw(spibar + 0)); - msg_pdbg("0x02: 0x%04x (SPIC)\n", - mmio_readw(spibar + 2)); - msg_pdbg("0x04: 0x%08x (SPIA)\n", - mmio_readl(spibar + 4)); - ichspi_bbar = mmio_readl(spibar + 0x50); - msg_pdbg("0x50: 0x%08x (BBAR)\n", - ichspi_bbar); - msg_pdbg("0x54: 0x%04x (PREOP)\n", - mmio_readw(spibar + 0x54)); - msg_pdbg("0x56: 0x%04x (OPTYPE)\n", - mmio_readw(spibar + 0x56)); - msg_pdbg("0x58: 0x%08x (OPMENU)\n", - mmio_readl(spibar + 0x58)); - msg_pdbg("0x5c: 0x%08x (OPMENU+4)\n", - mmio_readl(spibar + 0x5c)); - for (i = 0; i < 3; i++) { - int offs; - offs = 0x60 + (i * 4); - msg_pdbg("0x%02x: 0x%08x (PBR%u)\n", offs, - mmio_readl(spibar + offs), i); - } - if (mmio_readw(spibar) & (1 << 15)) { - msg_pwarn("WARNING: SPI Configuration Lockdown activated.\n"); - ichspi_lock = 1; - } - ich_init_opcodes(ich_gen); - ich_set_bbar(0, ich_gen); - register_spi_master(&spi_master_ich7); + case CHIPSET_100_SERIES_SUNRISE_POINT: + case CHIPSET_C620_SERIES_LEWISBURG: + case CHIPSET_300_SERIES_CANNON_POINT: + case CHIPSET_400_SERIES_COMET_POINT: + case CHIPSET_500_SERIES_TIGER_POINT: + case CHIPSET_600_SERIES_ALDER_POINT: + case CHIPSET_METEOR_LAKE: + case CHIPSET_APOLLO_LAKE: + case CHIPSET_GEMINI_LAKE: + case CHIPSET_JASPER_LAKE: + case CHIPSET_ELKHART_LAKE: + tmp = mmio_readl(spibar + PCH100_REG_DLOCK); + msg_pdbg("0x0c: 0x%08"PRIx32" (DLOCK)\n", tmp); + prettyprint_pch100_reg_dlock(tmp); break; - case CHIPSET_ICH8: - default: /* Future version might behave the same */ - arg = extract_programmer_param("ich_spi_mode"); - if (arg && !strcmp(arg, "hwseq")) { - ich_spi_mode = ich_hwseq; - msg_pspew("user selected hwseq\n"); - } else if (arg && !strcmp(arg, "swseq")) { - ich_spi_mode = ich_swseq; - msg_pspew("user selected swseq\n"); - } else if (arg && !strcmp(arg, "auto")) { - msg_pspew("user selected auto\n"); - ich_spi_mode = ich_auto; - } else if (arg && !strlen(arg)) { - msg_perr("Missing argument for ich_spi_mode.\n"); - free(arg); - return ERROR_FATAL; - } else if (arg) { - msg_perr("Unknown argument for ich_spi_mode: %s\n", - arg); - free(arg); - return ERROR_FATAL; - } - free(arg); + default: + break; + } - tmp2 = mmio_readw(spibar + ICH9_REG_HSFS); - msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2); - prettyprint_ich9_reg_hsfs(tmp2, ich_gen); - if (tmp2 & HSFS_FLOCKDN) { - msg_pinfo("SPI Configuration is locked down.\n"); - ichspi_lock = 1; - } - if (tmp2 & HSFS_FDV) - desc_valid = 1; - if (!(tmp2 & HSFS_FDOPSS) && desc_valid) - msg_pinfo("The Flash Descriptor Override Strap-Pin is set. Restrictions implied by\n" - "the Master Section of the flash descriptor are NOT in effect. Please note\n" - "that Protected Range (PR) restrictions still apply.\n"); - ich_init_opcodes(ich_gen); - - if (desc_valid) { - tmp2 = mmio_readw(spibar + ICH9_REG_HSFC); - msg_pdbg("0x06: 0x%04x (HSFC)\n", tmp2); - prettyprint_ich9_reg_hsfc(tmp2, ich_gen); - } + if (desc_valid) { + tmp = mmio_readl(spibar + ICH9_REG_FRAP); + msg_pdbg("0x50: 0x%08"PRIx32" (FRAP)\n", tmp); + msg_pdbg("BMWAG 0x%02"PRIx32", ", ICH_BMWAG(tmp)); + msg_pdbg("BMRAG 0x%02"PRIx32", ", ICH_BMRAG(tmp)); + msg_pdbg("BRWA 0x%02"PRIx32", ", ICH_BRWA(tmp)); + msg_pdbg("BRRA 0x%02"PRIx32"\n", ICH_BRRA(tmp)); - tmp = mmio_readl(spibar + ICH9_REG_FADDR); - msg_pdbg2("0x08: 0x%08x (FADDR)\n", tmp); + /* Handle FREGx and FRAP registers */ + for (i = 0; i < num_freg; i++) + ich_spi_rw_restricted |= ich9_handle_frap(hwseq_data.fd_regions, tmp, i); + if (ich_spi_rw_restricted) + msg_pinfo("Not all flash regions are freely accessible by flashrom. This is " + "most likely\ndue to an active ME. Please see " + "https://flashrom.org/ME for details.\n"); + } + + /* Handle PR registers */ + for (i = 0; i < num_pr; i++) { + /* if not locked down try to disable PR locks first */ + if (!ichspi_lock) + ich9_set_pr(reg_pr0, i, 0, 0); + ich_spi_rw_restricted |= ich9_handle_pr(reg_pr0, i); + } + switch (ich_spi_rw_restricted) { + case WRITE_PROT: + msg_pwarn("At least some flash regions are write protected. For write operations,\n" + "you should use a flash layout and include only writable regions. See\n" + "manpage for more details.\n"); + break; + case READ_PROT: + case LOCKED: + msg_pwarn("At least some flash regions are read protected. You have to use a flash\n" + "layout and include only accessible regions. For write operations, you'll\n" + "additionally need the --noverify-all switch. See manpage for more details.\n"); + break; + } + + tmp = mmio_readl(spibar + swseq_data.reg_ssfsc); + msg_pdbg("0x%zx: 0x%02"PRIx32" (SSFS)\n", swseq_data.reg_ssfsc, tmp & 0xff); + prettyprint_ich9_reg_ssfs(tmp); + if (tmp & SSFS_FCERR) { + msg_pdbg("Clearing SSFS.FCERR\n"); + mmio_writeb(SSFS_FCERR, spibar + swseq_data.reg_ssfsc); + } + msg_pdbg("0x%zx: 0x%06"PRIx32" (SSFC)\n", swseq_data.reg_ssfsc + 1, tmp >> 8); + prettyprint_ich9_reg_ssfc(tmp); + + msg_pdbg("0x%zx: 0x%04"PRIx16" (PREOP)\n", + swseq_data.reg_preop, mmio_readw(spibar + swseq_data.reg_preop)); + msg_pdbg("0x%zx: 0x%04"PRIx16" (OPTYPE)\n", + swseq_data.reg_optype, mmio_readw(spibar + swseq_data.reg_optype)); + msg_pdbg("0x%zx: 0x%08"PRIx32" (OPMENU)\n", + swseq_data.reg_opmenu, mmio_readl(spibar + swseq_data.reg_opmenu)); + msg_pdbg("0x%zx: 0x%08"PRIx32" (OPMENU+4)\n", + swseq_data.reg_opmenu + 4, mmio_readl(spibar + swseq_data.reg_opmenu + 4)); + + if (desc_valid) { switch (ich_gen) { + case CHIPSET_ICH8: case CHIPSET_100_SERIES_SUNRISE_POINT: case CHIPSET_C620_SERIES_LEWISBURG: case CHIPSET_300_SERIES_CANNON_POINT: case CHIPSET_400_SERIES_COMET_POINT: + case CHIPSET_500_SERIES_TIGER_POINT: + case CHIPSET_600_SERIES_ALDER_POINT: + case CHIPSET_METEOR_LAKE: case CHIPSET_APOLLO_LAKE: - tmp = mmio_readl(spibar + PCH100_REG_DLOCK); - msg_pdbg("0x0c: 0x%08x (DLOCK)\n", tmp); - prettyprint_pch100_reg_dlock(tmp); + case CHIPSET_GEMINI_LAKE: + case CHIPSET_JASPER_LAKE: + case CHIPSET_BAYTRAIL: + case CHIPSET_ELKHART_LAKE: break; default: + ichspi_bbar = mmio_readl(spibar + ICH9_REG_BBAR); + msg_pdbg("0x%x: 0x%08"PRIx32" (BBAR)\n", ICH9_REG_BBAR, ichspi_bbar); + ich_set_bbar(0, ich_gen); break; } - if (desc_valid) { - tmp = mmio_readl(spibar + ICH9_REG_FRAP); - msg_pdbg("0x50: 0x%08x (FRAP)\n", tmp); - msg_pdbg("BMWAG 0x%02x, ", ICH_BMWAG(tmp)); - msg_pdbg("BMRAG 0x%02x, ", ICH_BMRAG(tmp)); - msg_pdbg("BRWA 0x%02x, ", ICH_BRWA(tmp)); - msg_pdbg("BRRA 0x%02x\n", ICH_BRRA(tmp)); - - /* Handle FREGx and FRAP registers */ - for (i = 0; i < num_freg; i++) - ich_spi_rw_restricted |= ich9_handle_frap(tmp, i); - if (ich_spi_rw_restricted) - msg_pinfo("Not all flash regions are freely accessible by flashrom. This is " - "most likely\ndue to an active ME. Please see " - "https://flashrom.org/ME for details.\n"); - } - - /* Handle PR registers */ - for (i = 0; i < num_pr; i++) { - /* if not locked down try to disable PR locks first */ - if (!ichspi_lock) - ich9_set_pr(reg_pr0, i, 0, 0); - ich_spi_rw_restricted |= ich9_handle_pr(reg_pr0, i); + if (ich_gen == CHIPSET_ICH8) { + tmp = mmio_readl(spibar + ICH8_REG_VSCC); + msg_pdbg("0x%x: 0x%08"PRIx32" (VSCC)\n", ICH8_REG_VSCC, tmp); + msg_pdbg("VSCC: "); + prettyprint_ich_reg_vscc(tmp, FLASHROM_MSG_DEBUG, true); + } else { + tmp = mmio_readl(spibar + ICH9_REG_LVSCC); + msg_pdbg("0x%x: 0x%08"PRIx32" (LVSCC)\n", ICH9_REG_LVSCC, tmp); + msg_pdbg("LVSCC: "); + prettyprint_ich_reg_vscc(tmp, FLASHROM_MSG_DEBUG, true); + + tmp = mmio_readl(spibar + ICH9_REG_UVSCC); + msg_pdbg("0x%x: 0x%08"PRIx32" (UVSCC)\n", ICH9_REG_UVSCC, tmp); + msg_pdbg("UVSCC: "); + prettyprint_ich_reg_vscc(tmp, FLASHROM_MSG_DEBUG, false); } - switch (ich_spi_rw_restricted) { - case WRITE_PROT: - msg_pwarn("At least some flash regions are write protected. For write operations,\n" - "you should use a flash layout and include only writable regions. See\n" - "manpage for more details.\n"); + switch (ich_gen) { + case CHIPSET_ICH8: + case CHIPSET_100_SERIES_SUNRISE_POINT: + case CHIPSET_C620_SERIES_LEWISBURG: + case CHIPSET_300_SERIES_CANNON_POINT: + case CHIPSET_400_SERIES_COMET_POINT: + case CHIPSET_500_SERIES_TIGER_POINT: + case CHIPSET_600_SERIES_ALDER_POINT: + case CHIPSET_METEOR_LAKE: + case CHIPSET_APOLLO_LAKE: + case CHIPSET_GEMINI_LAKE: + case CHIPSET_JASPER_LAKE: + case CHIPSET_ELKHART_LAKE: break; - case READ_PROT: - case LOCKED: - msg_pwarn("At least some flash regions are read protected. You have to use a flash\n" - "layout and include only accessible regions. For write operations, you'll\n" - "additionally need the --noverify-all switch. See manpage for more details.\n" - ); + default: + tmp = mmio_readl(spibar + ICH9_REG_FPB); + msg_pdbg("0x%x: 0x%08"PRIx32" (FPB)\n", ICH9_REG_FPB, tmp); break; } - tmp = mmio_readl(spibar + swseq_data.reg_ssfsc); - msg_pdbg("0x%zx: 0x%02x (SSFS)\n", swseq_data.reg_ssfsc, tmp & 0xff); - prettyprint_ich9_reg_ssfs(tmp); - if (tmp & SSFS_FCERR) { - msg_pdbg("Clearing SSFS.FCERR\n"); - mmio_writeb(SSFS_FCERR, spibar + swseq_data.reg_ssfsc); - } - msg_pdbg("0x%zx: 0x%06x (SSFC)\n", swseq_data.reg_ssfsc + 1, tmp >> 8); - prettyprint_ich9_reg_ssfc(tmp); - - msg_pdbg("0x%zx: 0x%04x (PREOP)\n", - swseq_data.reg_preop, mmio_readw(spibar + swseq_data.reg_preop)); - msg_pdbg("0x%zx: 0x%04x (OPTYPE)\n", - swseq_data.reg_optype, mmio_readw(spibar + swseq_data.reg_optype)); - msg_pdbg("0x%zx: 0x%08x (OPMENU)\n", - swseq_data.reg_opmenu, mmio_readl(spibar + swseq_data.reg_opmenu)); - msg_pdbg("0x%zx: 0x%08x (OPMENU+4)\n", - swseq_data.reg_opmenu + 4, mmio_readl(spibar + swseq_data.reg_opmenu + 4)); - - if (desc_valid) { - switch (ich_gen) { - case CHIPSET_ICH8: - case CHIPSET_100_SERIES_SUNRISE_POINT: - case CHIPSET_C620_SERIES_LEWISBURG: - case CHIPSET_300_SERIES_CANNON_POINT: - case CHIPSET_400_SERIES_COMET_POINT: - case CHIPSET_APOLLO_LAKE: - case CHIPSET_BAYTRAIL: - break; - default: - ichspi_bbar = mmio_readl(spibar + ICH9_REG_BBAR); - msg_pdbg("0x%x: 0x%08x (BBAR)\n", ICH9_REG_BBAR, ichspi_bbar); - ich_set_bbar(0, ich_gen); - break; - } + if (read_ich_descriptors_via_fdo(ich_gen, spibar, &desc) == ICH_RET_OK) + prettyprint_ich_descriptors(ich_gen, &desc); - if (ich_gen == CHIPSET_ICH8) { - tmp = mmio_readl(spibar + ICH8_REG_VSCC); - msg_pdbg("0x%x: 0x%08x (VSCC)\n", ICH8_REG_VSCC, tmp); - msg_pdbg("VSCC: "); - prettyprint_ich_reg_vscc(tmp, FLASHROM_MSG_DEBUG, true); - } else { - tmp = mmio_readl(spibar + ICH9_REG_LVSCC); - msg_pdbg("0x%x: 0x%08x (LVSCC)\n", ICH9_REG_LVSCC, tmp); - msg_pdbg("LVSCC: "); - prettyprint_ich_reg_vscc(tmp, FLASHROM_MSG_DEBUG, true); - - tmp = mmio_readl(spibar + ICH9_REG_UVSCC); - msg_pdbg("0x%x: 0x%08x (UVSCC)\n", ICH9_REG_UVSCC, tmp); - msg_pdbg("UVSCC: "); - prettyprint_ich_reg_vscc(tmp, FLASHROM_MSG_DEBUG, false); - } + /* If the descriptor is valid and indicates multiple + * flash devices we need to use hwseq to be able to + * access the second flash device. + */ + if (ich_spi_mode == ich_auto && desc.content.NC != 0) { + msg_pinfo("Enabling hardware sequencing due to multiple flash chips detected.\n"); + ich_spi_mode = ich_hwseq; + } + } - switch (ich_gen) { - case CHIPSET_ICH8: - case CHIPSET_100_SERIES_SUNRISE_POINT: - case CHIPSET_C620_SERIES_LEWISBURG: - case CHIPSET_300_SERIES_CANNON_POINT: - case CHIPSET_400_SERIES_COMET_POINT: - case CHIPSET_APOLLO_LAKE: - break; - default: - tmp = mmio_readl(spibar + ICH9_REG_FPB); - msg_pdbg("0x%x: 0x%08x (FPB)\n", ICH9_REG_FPB, tmp); - break; - } + if (ich_spi_mode == ich_auto && ichspi_lock && + ich_missing_opcodes()) { + msg_pinfo("Enabling hardware sequencing because " + "some important opcode is locked.\n"); + ich_spi_mode = ich_hwseq; + } - if (read_ich_descriptors_via_fdo(ich_gen, spibar, &desc) == ICH_RET_OK) - prettyprint_ich_descriptors(ich_gen, &desc); + if (ich_spi_mode == ich_auto && + (ich_gen == CHIPSET_100_SERIES_SUNRISE_POINT || + ich_gen == CHIPSET_300_SERIES_CANNON_POINT || + ich_gen == CHIPSET_400_SERIES_COMET_POINT || + ich_gen == CHIPSET_500_SERIES_TIGER_POINT || + ich_gen == CHIPSET_600_SERIES_ALDER_POINT)) { + msg_pdbg("Enabling hardware sequencing by default for 100+ series PCH.\n"); + ich_spi_mode = ich_hwseq; + } - /* If the descriptor is valid and indicates multiple - * flash devices we need to use hwseq to be able to - * access the second flash device. - */ - if (ich_spi_mode == ich_auto && desc.content.NC != 0) { - msg_pinfo("Enabling hardware sequencing due to " - "multiple flash chips detected.\n"); - ich_spi_mode = ich_hwseq; - } - } + if (ich_spi_mode == ich_auto && + (ich_gen == CHIPSET_APOLLO_LAKE || + ich_gen == CHIPSET_GEMINI_LAKE || + ich_gen == CHIPSET_JASPER_LAKE || + ich_gen == CHIPSET_ELKHART_LAKE || + ich_gen == CHIPSET_METEOR_LAKE)) { + msg_pdbg("Enabling hardware sequencing by default for Apollo/Gemini/Jasper/Elkhart/Meteor Lake.\n"); + ich_spi_mode = ich_hwseq; + } - if (ich_spi_mode == ich_auto && ichspi_lock && - ich_missing_opcodes()) { - msg_pinfo("Enabling hardware sequencing because " - "some important opcode is locked.\n"); - ich_spi_mode = ich_hwseq; + if (ich_spi_mode == ich_hwseq) { + if (!desc_valid) { + msg_perr("Hardware sequencing was requested " + "but the flash descriptor is not valid. Aborting.\n"); + return ERROR_FLASHROM_FATAL; } - if (ich_spi_mode == ich_auto && - (ich_gen == CHIPSET_100_SERIES_SUNRISE_POINT || - ich_gen == CHIPSET_300_SERIES_CANNON_POINT || - ich_gen == CHIPSET_400_SERIES_COMET_POINT)) { - msg_pdbg("Enabling hardware sequencing by default for 100+ series PCH.\n"); - ich_spi_mode = ich_hwseq; + int tmpi = getFCBA_component_density(ich_gen, &desc, 0); + if (tmpi < 0) { + msg_perr("Could not determine density of flash component %d.\n", 0); + return ERROR_FLASHROM_FATAL; } + hwseq_data.size_comp0 = tmpi; - if (ich_spi_mode == ich_auto && ich_gen == CHIPSET_APOLLO_LAKE) { - msg_pdbg("Enabling hardware sequencing by default for Apollo Lake.\n"); - ich_spi_mode = ich_hwseq; + tmpi = getFCBA_component_density(ich_gen, &desc, 1); + if (tmpi < 0) { + msg_perr("Could not determine density of flash component %d.\n", 1); + return ERROR_FLASHROM_FATAL; } + hwseq_data.size_comp1 = tmpi; - if (ich_spi_mode == ich_hwseq) { - if (!desc_valid) { - msg_perr("Hardware sequencing was requested " - "but the flash descriptor is not " - "valid. Aborting.\n"); - return ERROR_FATAL; - } + struct hwseq_data *opaque_hwseq_data = calloc(1, sizeof(struct hwseq_data)); + if (!opaque_hwseq_data) + return ERROR_FLASHROM_FATAL; + memcpy(opaque_hwseq_data, &hwseq_data, sizeof(*opaque_hwseq_data)); + register_opaque_master(&opaque_master_ich_hwseq, opaque_hwseq_data); + } else { + register_spi_master(&spi_master_ich9, NULL); + } - int tmpi = getFCBA_component_density(ich_gen, &desc, 0); - if (tmpi < 0) { - msg_perr("Could not determine density of flash component %d.\n", 0); - return ERROR_FATAL; - } - hwseq_data.size_comp0 = tmpi; + return 0; +} - tmpi = getFCBA_component_density(ich_gen, &desc, 1); - if (tmpi < 0) { - msg_perr("Could not determine density of flash component %d.\n", 1); - return ERROR_FATAL; - } - hwseq_data.size_comp1 = tmpi; +int ich_init_spi(const struct programmer_cfg *cfg, void *spibar, enum ich_chipset ich_gen) +{ + ich_generation = ich_gen; + ich_spibar = spibar; - register_opaque_master(&opaque_master_ich_hwseq); - } else { - register_spi_master(&spi_master_ich9); - } - break; + switch (ich_gen) { + case CHIPSET_ICH7: + case CHIPSET_TUNNEL_CREEK: + case CHIPSET_CENTERTON: + return init_ich7_spi(spibar, ich_gen); + case CHIPSET_ICH8: + default: /* Future version might behave the same */ + return init_ich_default(cfg, spibar, ich_gen); } - - return 0; } static const struct spi_master spi_master_via = { - .max_data_read = 16, - .max_data_write = 16, - .command = ich_spi_send_command, - .multicommand = ich_spi_send_multicommand, - .read = default_spi_read, - .write_256 = default_spi_write_256, - .write_aai = default_spi_write_aai, + .max_data_read = 16, + .max_data_write = 16, + .command = ich_spi_send_command, + .multicommand = ich_spi_send_multicommand, + .map_flash_region = physmap, + .unmap_flash_region = physunmap, + .read = default_spi_read, + .write_256 = default_spi_write_256, + .probe_opcode = ich_spi_probe_opcode, }; int via_init_spi(uint32_t mmio_base) @@ -2065,42 +2392,40 @@ int via_init_spi(uint32_t mmio_base) ich_spibar = rphysmap("VIA SPI MMIO registers", mmio_base, 0x70); if (ich_spibar == ERROR_PTR) - return ERROR_FATAL; + return ERROR_FLASHROM_FATAL; /* Do we really need no write enable? Like the LPC one at D17F0 0x40 */ /* Not sure if it speaks all these bus protocols. */ internal_buses_supported &= BUS_LPC | BUS_FWH; ich_generation = CHIPSET_ICH7; - register_spi_master(&spi_master_via); + register_spi_master(&spi_master_via, NULL); - msg_pdbg("0x00: 0x%04x (SPIS)\n", mmio_readw(ich_spibar + 0)); - msg_pdbg("0x02: 0x%04x (SPIC)\n", mmio_readw(ich_spibar + 2)); - msg_pdbg("0x04: 0x%08x (SPIA)\n", mmio_readl(ich_spibar + 4)); + msg_pdbg("0x00: 0x%04"PRIx16" (SPIS)\n", mmio_readw(ich_spibar + 0)); + msg_pdbg("0x02: 0x%04"PRIx16" (SPIC)\n", mmio_readw(ich_spibar + 2)); + msg_pdbg("0x04: 0x%08"PRIx32" (SPIA)\n", mmio_readl(ich_spibar + 4)); for (i = 0; i < 2; i++) { int offs; offs = 8 + (i * 8); - msg_pdbg("0x%02x: 0x%08x (SPID%d)\n", offs, - mmio_readl(ich_spibar + offs), i); - msg_pdbg("0x%02x: 0x%08x (SPID%d+4)\n", offs + 4, + msg_pdbg("0x%02x: 0x%08"PRIx32" (SPID%d)\n", offs, mmio_readl(ich_spibar + offs), i); + msg_pdbg("0x%02x: 0x%08"PRIx32" (SPID%d+4)\n", offs + 4, mmio_readl(ich_spibar + offs + 4), i); } ichspi_bbar = mmio_readl(ich_spibar + 0x50); - msg_pdbg("0x50: 0x%08x (BBAR)\n", ichspi_bbar); - msg_pdbg("0x54: 0x%04x (PREOP)\n", mmio_readw(ich_spibar + 0x54)); - msg_pdbg("0x56: 0x%04x (OPTYPE)\n", mmio_readw(ich_spibar + 0x56)); - msg_pdbg("0x58: 0x%08x (OPMENU)\n", mmio_readl(ich_spibar + 0x58)); - msg_pdbg("0x5c: 0x%08x (OPMENU+4)\n", mmio_readl(ich_spibar + 0x5c)); + + msg_pdbg("0x50: 0x%08"PRIx32" (BBAR)\n", ichspi_bbar); + msg_pdbg("0x54: 0x%04"PRIx16" (PREOP)\n", mmio_readw(ich_spibar + 0x54)); + msg_pdbg("0x56: 0x%04"PRIx16" (OPTYPE)\n", mmio_readw(ich_spibar + 0x56)); + msg_pdbg("0x58: 0x%08"PRIx32" (OPMENU)\n", mmio_readl(ich_spibar + 0x58)); + msg_pdbg("0x5c: 0x%08"PRIx32" (OPMENU+4)\n", mmio_readl(ich_spibar + 0x5c)); for (i = 0; i < 3; i++) { int offs; offs = 0x60 + (i * 4); - msg_pdbg("0x%02x: 0x%08x (PBR%d)\n", offs, - mmio_readl(ich_spibar + offs), i); + msg_pdbg("0x%02x: 0x%08"PRIx32" (PBR%d)\n", offs, mmio_readl(ich_spibar + offs), i); } - msg_pdbg("0x6c: 0x%04x (CLOCK/DEBUG)\n", - mmio_readw(ich_spibar + 0x6c)); + msg_pdbg("0x6c: 0x%04x (CLOCK/DEBUG)\n", mmio_readw(ich_spibar + 0x6c)); if (mmio_readw(ich_spibar) & (1 << 15)) { msg_pwarn("Warning: SPI Configuration Lockdown activated.\n"); - ichspi_lock = 1; + ichspi_lock = true; } ich_set_bbar(0, ich_generation); @@ -2108,5 +2433,3 @@ int via_init_spi(uint32_t mmio_base) return 0; } - -#endif |