Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at --- chipdrivers.h | 6 ++ flashchips.c | 40 ++++++++- ichspi.c | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- programmer.h | 1 + 4 files changed, 316 insertions(+), 11 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 a1e3441..42391cd 100644 --- a/flashchips.c +++ b/flashchips.c @@ -8273,7 +8273,44 @@ const struct flashchip flashchips[] = { .write = write_jedec_1, .read = read_memmapped, }, - +#if defined(__i386__) || defined(__x86_64__) + { + .vendor = "Intel", + .name = "Hardware Sequencing", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = INTEL_ID, + .model_id = GENERIC_DEVICE_ID, + .total_size = 4, + .page_size = 256, + .tested = TEST_OK_PR, + .probe = ich_hwseq_probe, + .block_erasers = + { + { + .eraseblocks = { {256, 4 * 4} }, + .block_erase = ich_hwseq_block_erase, + //}, { + //{ + //.eraseblocks = { {256, CHIPSIZE * 4} }, + //.block_erase = ich_hwseq_block_erase, + //}, { + //{ + //.eraseblocks = { {4 * 1024, CHIPSIZE / 4} }, + //.block_erase = ich_hwseq_block_erase, + //}, { + //{ + //.eraseblocks = { {8 * 1024, CHIPSIZE / 8} }, + //.block_erase = ich_hwseq_block_erase, + //}, { + //{ + //.eraseblocks = { {64 * 1024, CHIPSIZE / 64} }, + //.block_erase = ich_hwseq_block_erase, + } + }, + .write = ich_hwseq_write_256, + .read = ich_hwseq_read, + }, +#endif // defined(__i386__) || defined(__x86_64__) { .vendor = "AMIC", .name = "unknown AMIC SPI chip", @@ -8406,6 +8443,7 @@ const struct flashchip flashchips[] = { .probe = probe_spi_rdid, .write = NULL, }, + { .vendor = "Generic", .name = "unknown SPI chip (REMS)", diff --git a/ichspi.c b/ichspi.c index 5bba0ab..8cdcb5c 100644 --- a/ichspi.c +++ b/ichspi.c @@ -1099,6 +1099,240 @@ static int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt, return result; }
+uint32_t ich_hwseq_get_erase_block_size(void) +{ + uint8_t enc_berase = (REGREAD16(ICH9_REG_HSFS) & HSFS_BERASE) >> + HSFS_BERASE_OFF; + const uint32_t dec_berase[4] = { + 256, + 4 * 1024, + 8 * 1024, + 64 * 1024 + }; + return dec_berase[enc_berase]; +} + +int ich_hwseq_probe(struct flashchip *flash) +{ + extern struct flash_descriptor fdbar; + msg_cdbg("Prerequisites for Intel Hardware Sequencing are "); + if (spi_programmer->type == SPI_CONTROLLER_ICH_HWSEQ) { + msg_cdbg("met.\n"); + msg_cdbg("Found %d attached SPI flash chips with a " + "combined density of %d kB.\n", fdbar.NC + 1, + (getFCBA_component_density(0) + + getFCBA_component_density(1)) / 1024); + return 1; + } else { + msg_cdbg("not met.\n"); + return 0; + } +} + +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; + uint32_t hsfc; + uint32_t hsfs; + uint32_t timeout = 5000 * 1000; /* 5 s for max 64 kB */ + + REGWRITE32(ICH9_REG_FADDR, (addr & 0x00FFFFFF)); + erase_block = ich_hwseq_get_erase_block_size(); + if (strcmp(flash->name, "Hardware Sequencing") != 0) { + msg_perr("This chip (%s) is not supported in hardware" + "sequencing mode\n", flash->name); + return -1; + } + + 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; + } + + 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; + } + + msg_pdbg("erasing %d bytes starting at 0x%06x\n", len, addr); + + hsfc = REGREAD16(ICH9_REG_HSFC); + /* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */ + REGWRITE16(ICH9_REG_HSFC, hsfc); + hsfc &= ~HSFC_FCYCLE; /* clear operation */ + hsfc = (0x11 << HSFC_FCYCLE_OFF); /* set erase operation */ + hsfc |= HSFC_FGO; /* start */ + REGWRITE16(ICH9_REG_HSFC, hsfc); + + /* Wait for Cycle Done Status or Flash Cycle Error. */ + while (((REGREAD16(ICH9_REG_HSFS) & + (HSFS_FDONE | HSFS_FCERR)) == 0) && + --timeout) { + programmer_delay(10); + } + if (!timeout) { + msg_perr("timeout, ICH9_REG_HSFS=0x%04x\n", + REGREAD16(ICH9_REG_HSFS)); + return 1; + } + + hsfs = REGREAD16(ICH9_REG_HSFS); + REGWRITE16(ICH9_REG_HSFS, hsfs); /* clear all error bits */ + if (hsfs & HSFS_FCERR) { + msg_perr("Transaction error between offset 0x%06x and 0x%06x + " + "%d (= 0x%06x)!\n", + addr, addr, len, addr+len); + prettyprint_ich9_reg_hsfs(hsfs); + prettyprint_ich9_reg_hsfc(hsfs); + return 1; + } + return 0; +} + +int ich_hwseq_read(struct flashchip *flash, uint8_t *buf, int addr, int len) +{ + uint32_t *buf32 = (uint32_t *)buf; + uint16_t hsfc = REGREAD16(ICH9_REG_HSFC); + uint16_t hsfs; + uint16_t timeout = 100 * 60; + int i; + + if (strcmp(flash->name, "Hardware Sequencing") != 0) { + msg_perr("This chip (%s) is not supported in hardware" + "sequencing mode\n", flash->name); + return -1; + } + + if (len % 4 != 0) { + msg_perr("Read size has to be a multiple of 4 in hardware" + "sequencing mode\n"); + 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_HSFC, hsfc); + + for (i = 0; i < len; i += 4) { + REGWRITE32(ICH9_REG_FADDR, (addr & 0x00FFFFFF)); + hsfc = REGREAD16(ICH9_REG_HSFC); + hsfc &= ~HSFC_FCYCLE; /* set read operation */ + hsfc &= ~HSFC_FDBC; /* clear byte count */ + hsfc |= (3 << HSFC_FDBC_OFF); /* set byte count to 3+1 */ + hsfc |= HSFC_FGO; /* start */ + REGWRITE16(ICH9_REG_HSFC, hsfc); + + /* Wait for Cycle Done Status or Flash Cycle Error. */ + while (((REGREAD16(ICH9_REG_HSFS) & + (HSFS_FDONE | HSFS_FCERR)) == 0) && + --timeout) { + programmer_delay(10); + } + if (!timeout) { + msg_perr("timeout, ICH9_REG_HSFS=0x%04x\n", + REGREAD16(ICH9_REG_HSFS)); + return 1; + } + + hsfs = REGREAD16(ICH9_REG_HSFS); + REGWRITE16(ICH9_REG_HSFS, hsfs); /* clear all error bits */ + if (hsfs & HSFS_FCERR) { + msg_perr("Transaction error between offset 0x%06x and " + "0x%06x + %d (= 0x%06x)!\n", + addr, addr, 4, addr + 4); + prettyprint_ich9_reg_hsfs(hsfs); + prettyprint_ich9_reg_hsfc(hsfs); + return 1; + } + + *buf32++ = REGREAD32(ICH9_REG_FDATA0); + addr += 4; + } + return 0; +} + +int ich_hwseq_write_256(struct flashchip *flash, uint8_t *buf, int addr, int len) +{ + uint32_t *buf32 = (uint32_t *)buf; + uint16_t hsfc = REGREAD16(ICH9_REG_HSFC); + uint16_t hsfs; + uint16_t timeout = 100 * 60; + int i; + if (strcmp(flash->name, "Hardware Sequencing") != 0) { + msg_perr("This chip (%s) is not supported in hardware" + "sequencing mode\n", flash->name); + return -1; + } + + if ((len % 4 != 0)) { + msg_perr("Write size has to be a multiple of 4 in hardware " + "sequencing mode\n"); + 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_HSFC, hsfc); + + for (i = 0; i < len; i += 4) { + REGWRITE32(ICH9_REG_FADDR, (addr & 0x00FFFFFF)); + REGWRITE32(ICH9_REG_FDATA0, *buf32++); + hsfc = REGREAD16(ICH9_REG_HSFC); + hsfc &= ~HSFC_FCYCLE; /* clear operation */ + hsfc = (0x10 << HSFC_FCYCLE_OFF); /* set write operation */ + hsfc &= ~HSFC_FDBC; /* clear byte count */ + hsfc |= (3 << HSFC_FDBC_OFF); /* set byte count to 3+1 */ + hsfc |= HSFC_FGO; /* start */ + REGWRITE16(ICH9_REG_HSFC, hsfc); + + /* Wait for Cycle Done Status or Flash Cycle Error. */ + while (((REGREAD16(ICH9_REG_HSFS) & + (HSFS_FDONE | HSFS_FCERR)) == 0) && + --timeout) { + programmer_delay(10); + } + if (!timeout) { + msg_perr("timeout, ICH9_REG_HSFS=0x%04x\n", + REGREAD16(ICH9_REG_HSFS)); + return 1; + } + + hsfs = REGREAD16(ICH9_REG_HSFS); + REGWRITE16(ICH9_REG_HSFS, hsfs); /* clear all error bits */ + if (hsfs & HSFS_FCERR) { + msg_perr("Transaction error between offset 0x%06x and " + "0x%06x + %d (= 0x%06x)!\n", + addr, addr, 4, addr + 4); + prettyprint_ich9_reg_hsfs(hsfs); + prettyprint_ich9_reg_hsfc(hsfs); + return 1; + } + addr += 4; + } + return 0; +} + static int ich_spi_send_multicommand(struct spi_command *cmds) { int ret = 0; @@ -1217,6 +1451,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) { @@ -1225,20 +1469,21 @@ 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; + /* used for hw sequencing detection */ + extern struct flash_descriptor fdbar; + uint32_t size_low; + uint32_t size_high;
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; } @@ -1249,8 +1494,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", @@ -1286,9 +1531,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); @@ -1365,10 +1614,21 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, read_ich_descriptors_from_fdo(ich_spibar); pretty_print_ich_descriptors(); } - ich_init_opcodes(); - break; - default: - /* Nothing */ + + /* Fetch erase block sizes for addresses below and above FPBA + * and check if erase blocks are equal for all addresses. */ + REGWRITE32(ICH9_REG_FADDR, (0 & 0x00FFFFFF)); + size_low = ich_hwseq_get_erase_block_size(); + REGWRITE32(ICH9_REG_FADDR, + (REGREAD32(ICH9_REG_FPB) & FPB_FPBA) & 0x00FFFFFF); + size_high = ich_hwseq_get_erase_block_size(); + + if ((ichspi_lock || fdbar.NC != 0) && (size_low == size_high)) + register_spi_programmer(&spi_programmer_ich_hwseq); + else { + register_spi_programmer(&spi_programmer_ich9); + ich_init_opcodes(); + } break; }
diff --git a/programmer.h b/programmer.h index e1147f1..8ccaf0b 100644 --- a/programmer.h +++ b/programmer.h @@ -538,6 +538,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,