todo: - revise hwseq activation: when should hwseq be used, when swseq? when should swseq be forbidden? etc. - commit message (is a link to my blog post a good idea (additionally)?) - revise (levels of) prints - revise timeout values (for optimal performance) - build upon the upcoming opaque interface framework instead
Tested-by: David Hendricks dhendrix@google.com Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at --- chipdrivers.h | 6 + flashchips.c | 23 ++++- flashchips.h | 2 + ich_descriptors.c | 35 ++++++ ich_descriptors.h | 1 + ichspi.c | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++-- programmer.h | 1 + 7 files changed, 387 insertions(+), 12 deletions(-)
diff --git a/chipdrivers.h b/chipdrivers.h index 92ddbea..f97e2ad 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -84,6 +84,12 @@ void print_status_82802ab(uint8_t status); int unlock_82802ab(struct flashchip *flash); int unlock_28f004s5(struct flashchip *flash);
+/* ichspi.c */ +int ich_hwseq_probe(struct flashchip *flash); +int ich_hwseq_read(struct flashchip *flash, uint8_t *buf, int start, int len); +int ich_hwseq_block_erase(struct flashchip *flash, unsigned int addr, unsigned int blocklen); +int ich_hwseq_write_256(struct flashchip *flash, uint8_t *buf, int start, int len); + /* jedec.c */ uint8_t oddparity(uint8_t val); void toggle_ready_jedec(chipaddr dst); diff --git a/flashchips.c b/flashchips.c index 1b5c88a..1fbdcfe 100644 --- a/flashchips.c +++ b/flashchips.c @@ -8507,7 +8507,27 @@ const struct flashchip flashchips[] = { .write = write_jedec_1, .read = read_memmapped, }, - +#if defined(CONFIG_INTERNAL) && (defined(__i386__) || defined(__x86_64__)) + { + .vendor = "Intel", + .name = "Hardware Sequencing", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = INTEL_ID, + .model_id = INTEL_HWSEQ, + .total_size = 0, + .page_size = 256, + .tested = TEST_OK_PREW, + .probe = ich_hwseq_probe, + .block_erasers = + { + { /* erase blocks will be set by the probing function */ + .block_erase = ich_hwseq_block_erase, + } + }, + .write = ich_hwseq_write_256, + .read = ich_hwseq_read, + }, +#endif // defined(CONFIG_INTERNAL) && (defined(__i386__) || defined(__x86_64__)) { .vendor = "AMIC", .name = "unknown AMIC SPI chip", @@ -8640,6 +8660,7 @@ const struct flashchip flashchips[] = { .probe = probe_spi_rdid, .write = NULL, }, + { .vendor = "Generic", .name = "unknown SPI chip (REMS)", diff --git a/flashchips.h b/flashchips.h index 3b2b94f..6b8d574 100644 --- a/flashchips.h +++ b/flashchips.h @@ -340,6 +340,8 @@ #define SHARP_LH28F008SA 0xA2 /* Sharp chip, Intel Vendor ID */ #define SHARP_LH28F008SC 0xA6 /* Sharp chip, Intel Vendor ID */
+#define INTEL_HWSEQ 0xFFFE /* dummy ID for hardware sequencing */ + #define ISSI_ID 0xD5 /* ISSI Integrated Silicon Solutions */
/* diff --git a/ich_descriptors.c b/ich_descriptors.c index 0281488..c54e710 100644 --- a/ich_descriptors.c +++ b/ich_descriptors.c @@ -393,6 +393,41 @@ int read_ich_descriptors_from_dump(const uint32_t *dump, enum chipset cs, struct } #else // ICH_DESCRIPTORS_FROM_MMAP_DUMP
+/** Returns the integer representation of the component density with index +idx in bytes or 0 if a correct size can not be determined. */ +int getFCBA_component_density(uint8_t idx, const struct flash_descriptors *desc) +{ + uint8_t size_enc; + const int dec_mem[6] = { + 512 * 1024, + 1 * 1024 * 1024, + 2 * 1024 * 1024, + 4 * 1024 * 1024, + 8 * 1024 * 1024, + 16 * 1024 * 1024, + }; + + switch(idx) { + case 0: + size_enc = desc->component.comp1_density; + break; + case 1: + if (desc->content.NC == 0) + return 0; + size_enc = desc->component.comp2_density; + break; + default: + msg_perr("Only component index 0 or 1 are supported yet.\n"); + return 0; + } + if (size_enc > 5) { + msg_perr("Density of component with index %d is invalid. " + "Encoded density is 0x%x.\n", idx, size_enc); + return 0; + } + return dec_mem[size_enc]; +} + static uint32_t read_descriptor_reg(uint8_t section, uint16_t offset, void *spibar) { uint32_t control = 0; diff --git a/ich_descriptors.h b/ich_descriptors.h index 1129805..fd56b26 100644 --- a/ich_descriptors.h +++ b/ich_descriptors.h @@ -452,6 +452,7 @@ int read_ich_descriptors_from_dump(const uint32_t *dump, enum chipset cs, struct #else // ICH_DESCRIPTORS_FROM_MMAP_DUMP
void read_ich_descriptors_via_fdo(void *spibar, struct flash_descriptors *desc); +int getFCBA_component_density(uint8_t idx, const struct flash_descriptors *desc);
#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP #endif // __ICH_DESCRIPTORS_H__ diff --git a/ichspi.c b/ichspi.c index edca654..9597c05 100644 --- a/ichspi.c +++ b/ichspi.c @@ -23,10 +23,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#if defined(__i386__) || defined(__x86_64__) +#if defined(CONFIG_INTERNAL) && (defined(__i386__) || defined(__x86_64__))
#include <string.h> #include "flash.h" +#include "flashchips.h" #include "chipdrivers.h" #include "programmer.h" #include "spi.h" @@ -1073,6 +1074,298 @@ static int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt, return result; }
+static struct hwseq { + uint32_t size_comp0; + uint32_t size_comp1; +} hwseq; + +static void ich_hwseq_set_addr(uint32_t addr) +{ + uint32_t addr_old = REGREAD32(ICH9_REG_FADDR) & ~0x01FFFFFF; + REGWRITE32(ICH9_REG_FADDR, (addr & 0x01FFFFFF) | addr_old); +} + +/* Sets FADDR.FLA to 'addr' and returns the erase block size in bytes + * of the block containing this address. */ +static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr) +{ + uint8_t enc_berase; + const uint32_t dec_berase[4] = { + 256, + 4 * 1024, + 8 * 1024, + 64 * 1024 + }; + + ich_hwseq_set_addr(addr); + enc_berase = (REGREAD16(ICH9_REG_HSFS) & HSFS_BERASE) >> + HSFS_BERASE_OFF; + return dec_berase[enc_berase]; +} + +/* Polls for Cycle Done Status, Flash Cycle Error or timeout in 10 us intervals. + 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) +{ + uint16_t hsfs; + uint32_t addr; + + while ((((hsfs = REGREAD16(ICH9_REG_HSFS)) & + (HSFS_FDONE | HSFS_FCERR)) == 0) && + --timeout) { + programmer_delay(10); + } + REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS)); + if (!timeout) { + addr = REGREAD32(ICH9_REG_FADDR) & 0x01FFFFFF; + msg_perr("Timeout error between offset 0x%08x and " + "0x%08x + %d (=0x%08x)!\n", + addr, addr, len - 1, addr + len - 1); + prettyprint_ich9_reg_hsfs(hsfs); + prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC)); + return 1; + } + + if (hsfs & HSFS_FCERR) { + addr = REGREAD32(ICH9_REG_FADDR) & 0x01FFFFFF; + msg_perr("Transaction error between offset 0x%08x and " + "0x%08x + %d (=0x%08x)!\n", + addr, addr, len - 1, addr + len - 1); + prettyprint_ich9_reg_hsfs(hsfs); + prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC)); + return 1; + } + return 0; +} + +int ich_hwseq_probe(struct flashchip *flash) +{ + uint32_t total_size, boundary; + uint32_t erase_size_low, size_low, erase_size_high, size_high; + struct block_eraser *eraser; + + if (flash->manufacture_id != INTEL_ID || + flash->model_id != INTEL_HWSEQ) { + msg_cerr("This chip (%s) is not supported in hardware" + "sequencing mode and should never have been probed.\n", + flash->name); + msg_cerr("%s: Please report a bug at flashrom@flashrom.org\n", + __func__); + return 0; + } + + msg_cdbg("Prerequisites for Intel Hardware Sequencing are "); + if (spi_programmer->type != SPI_CONTROLLER_ICH_HWSEQ) { + msg_cdbg("not met.\n"); + return 0; + } + msg_cdbg("met.\n"); + + total_size = hwseq.size_comp0 + hwseq.size_comp1; + msg_cdbg("Found %d attached SPI flash chip", + (hwseq.size_comp1 != 0) ? 2 : 1); + if (hwseq.size_comp1 != 0) + msg_cdbg("s with a combined"); + else + msg_cdbg(" with a"); + msg_cdbg(" density of %d kB.\n", total_size / 1024); + flash->total_size = total_size / 1024; + + eraser = &(flash->block_erasers[0]); + boundary = (REGREAD32(ICH9_REG_FPB) & FPB_FPBA) << 12; + size_high = total_size - boundary; + erase_size_high = ich_hwseq_get_erase_block_size(boundary); + + if (boundary == 0) { + msg_cdbg("There is only one partition containing the whole " + "address space (0x%06x - 0x%06x).\n", 0, size_high-1); + eraser->eraseblocks[0].size = erase_size_high; + eraser->eraseblocks[0].count = size_high / erase_size_high; + msg_cdbg("There are %d erase blocks with %d B each.\n", + size_high / erase_size_high, erase_size_high); + } else { + msg_cdbg("The flash address space (0x%06x - 0x%06x) is divided " + "at address 0x%06x in two partitions.\n", + 0, size_high-1, boundary); + size_low = total_size - size_high; + erase_size_low = ich_hwseq_get_erase_block_size(0); + + 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", + 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", + boundary, size_high-1); + msg_cdbg("In that range are %d erase blocks with %d B each.\n", + size_high / erase_size_high, erase_size_high); + } + return 1; +} + +static int ich_hwseq_send_command(unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + msg_pdbg("skipped. Intel Hardware Sequencing does not support sending " + "arbitrary commands."); + return -1; +} + +int ich_hwseq_block_erase(struct flashchip *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 */ + + if (flash->manufacture_id != INTEL_ID || + flash->model_id != INTEL_HWSEQ) { + msg_perr("This chip (%s) is not supported in hardware" + "sequencing mode\n", flash->name); + return -1; + } + + erase_block = ich_hwseq_get_erase_block_size(addr); + if (len != erase_block) { + msg_cerr("Erase block size for address 0x%06x is %d B, " + "but requested erase block size is %d B. " + "Not erasing anything.\n", addr, erase_block, len); + return -1; + } + + /* Although the hardware supports this (it would erase the whole block + * 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). " + "Not erasing anything.\n", addr, erase_block); + return -1; + } + + if (addr + len > flash->total_size * 1024) { + msg_perr("Request to erase some inaccessible memory address(es)" + " (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); + + /* 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 &= ~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); + REGWRITE16(ICH9_REG_HSFC, hsfc); + + if (ich_hwseq_wait_for_cycle_complete(timeout, len)) + return -1; + return 0; +} + +int ich_hwseq_read(struct flashchip *flash, uint8_t *buf, int addr, int len) +{ + uint16_t hsfc; + uint16_t timeout = 100 * 60; + uint8_t block_len; + + if (flash->manufacture_id != INTEL_ID || + flash->model_id != INTEL_HWSEQ) { + msg_perr("This chip (%s) is not supported in hardware" + "sequencing mode.\n", flash->name); + return -1; + } + + if (addr < 0 || addr + len > flash->total_size * 1024) { + msg_perr("Request to read from an inaccessible memory address " + "(addr=0x%x, len=%d).\n", addr, len); + return -1; + } + + msg_pdbg("Reading %d bytes starting at 0x%06x.\n", len, addr); + /* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */ + REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS)); + + while (len > 0) { + block_len = min(len, spi_programmer->max_data_read); + ich_hwseq_set_addr(addr); + hsfc = REGREAD16(ICH9_REG_HSFC); + hsfc &= ~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)) + return 1; + ich_read_data(buf, block_len, ICH9_REG_FDATA0); + addr += block_len; + buf += block_len; + len -= block_len; + } + return 0; +} + +int ich_hwseq_write_256(struct flashchip *flash, uint8_t *buf, int addr, int len) +{ + uint16_t hsfc; + uint16_t timeout = 100 * 60; + uint8_t block_len; + + if (flash->manufacture_id != INTEL_ID || + flash->model_id != INTEL_HWSEQ) { + msg_perr("This chip (%s) is not supported in hardware" + "sequencing mode\n", flash->name); + return -1; + } + + if (addr < 0 || addr + len > flash->total_size * 1024) { + msg_perr("Request to write to an inaccessible memory address " + "(addr=0x%x, len=%d).\n", addr, len); + return -1; + } + + msg_pdbg("Writing %d bytes starting at 0x%06x.\n", len, addr); + /* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */ + REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS)); + + while (len > 0) { + ich_hwseq_set_addr(addr); + block_len = min(len, spi_programmer->max_data_write); + ich_fill_data(buf, block_len, ICH9_REG_FDATA0); + hsfc = REGREAD16(ICH9_REG_HSFC); + hsfc &= ~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)) + return -1; + addr += block_len; + buf += block_len; + len -= block_len; + } + return 0; +} + static int ich_spi_send_multicommand(struct spi_command *cmds) { int ret = 0; @@ -1191,6 +1484,16 @@ static const struct spi_programmer spi_programmer_ich9 = { .write_256 = default_spi_write_256, };
+static const struct spi_programmer spi_programmer_ich_hwseq = { + .type = SPI_CONTROLLER_ICH_HWSEQ, + .max_data_read = 64, + .max_data_write = 64, + .command = ich_hwseq_send_command, + .multicommand = default_spi_send_multicommand, + .read = ich_hwseq_read, + .write_256 = ich_hwseq_write_256, +}; + int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, int ich_generation) { @@ -1199,20 +1502,18 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, uint16_t spibar_offset, tmp2; uint32_t tmp; int ichspi_desc = 0; + struct flash_descriptors desc = {{ 0 }};
switch (ich_generation) { case 7: - register_spi_programmer(&spi_programmer_ich7); spibar_offset = 0x3020; break; case 8: - register_spi_programmer(&spi_programmer_ich9); spibar_offset = 0x3020; break; case 9: case 10: default: /* Future version might behave the same */ - register_spi_programmer(&spi_programmer_ich9); spibar_offset = 0x3800; break; } @@ -1223,8 +1524,8 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, /* Assign Virtual Address */ ich_spibar = rcrb + spibar_offset;
- switch (spi_programmer->type) { - case SPI_CONTROLLER_ICH7: + switch (ich_generation) { + case 7: msg_pdbg("0x00: 0x%04x (SPIS)\n", mmio_readw(ich_spibar + 0)); msg_pdbg("0x02: 0x%04x (SPIC)\n", @@ -1260,9 +1561,13 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, msg_pinfo("WARNING: SPI Configuration Lockdown activated.\n"); ichspi_lock = 1; } + register_spi_programmer(&spi_programmer_ich7); ich_init_opcodes(); break; - case SPI_CONTROLLER_ICH9: + case 8: + case 9: + case 10: + default: /* Future version might behave the same */ tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFS); msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2); prettyprint_ich9_reg_hsfs(tmp2); @@ -1338,15 +1643,19 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
msg_pdbg("\n"); if (ichspi_desc) { - struct flash_descriptors desc = {{ 0 }}; read_ich_descriptors_via_fdo(ich_spibar, &desc); prettyprint_ich_descriptors(CHIPSET_UNKNOWN, &desc); + if (ichspi_lock || desc.content.NC != 0) { + hwseq.size_comp0 = getFCBA_component_density(0, &desc); + hwseq.size_comp1 = getFCBA_component_density(1, &desc); + register_spi_programmer(&spi_programmer_ich_hwseq); + break; + } } + + register_spi_programmer(&spi_programmer_ich9); ich_init_opcodes(); break; - default: - /* Nothing */ - break; }
old = pci_read_byte(dev, 0xdc); diff --git a/programmer.h b/programmer.h index 6a28dbe..6e316d1 100644 --- a/programmer.h +++ b/programmer.h @@ -517,6 +517,7 @@ enum spi_controller { #if defined(__i386__) || defined(__x86_64__) SPI_CONTROLLER_ICH7, SPI_CONTROLLER_ICH9, + SPI_CONTROLLER_ICH_HWSEQ, SPI_CONTROLLER_IT85XX, SPI_CONTROLLER_IT87XX, SPI_CONTROLLER_SB600,