todo:
 - revise hwseq activation: when should hwseq be used, when swseq?
   in the current version i have added a programmer parameter to tell flashrom 
when one
   wants to use hwseq _if it is available_ (i.e. descriptor valid), example:
flashrom -p internal:laptop=force_I_want_a_brick,hwseq=yes -VV
   this needs to be documented in the man page if it gets merged like that.
 - 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

Signed-off-by: Stefan Tauner <stefan.tau...@student.tuwien.ac.at>
---
 chipdrivers.h |    6 +
 flashchips.c  |   23 ++++-
 flashchips.h  |    2 +
 ichspi.c      |  352 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 programmer.h  |    1 +
 5 files changed, 373 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 1a5622f..041b9a5 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -8752,7 +8752,27 @@ const struct flashchip flashchips[] = {
                .read           = read_memmapped,
                .voltage        = {3000, 3600}, /* Also has 12V fast program */
        },
-
+#if defined(CONFIG_INTERNAL) && (defined(__i386__) || defined(__x86_64__))
+       {
+               .vendor         = "Intel",
+               .name           = "Hardware Sequencing",
+               .bustype        = BUS_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",
@@ -8885,6 +8905,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 ff49d31..1374b2f 100644
--- a/flashchips.h
+++ b/flashchips.h
@@ -346,6 +346,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/ichspi.c b/ichspi.c
index 737613b..eaaf98e 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -26,7 +26,9 @@
 #if defined(__i386__) || defined(__x86_64__)
 
 #include <string.h>
+#include <stdlib.h>
 #include "flash.h"
+#include "flashchips.h"
 #include "chipdrivers.h"
 #include "programmer.h"
 #include "spi.h"
@@ -1084,6 +1086,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;
+       static const uint32_t const 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 (=0x%08x + %d)!\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;
@@ -1207,6 +1501,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)
 {
@@ -1214,21 +1518,20 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, 
void *rcrb,
        uint8_t old, new;
        uint16_t spibar_offset, tmp2;
        uint32_t tmp;
+       char *arg;
        int ichspi_desc = 0;
+       int hwseq_en = 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;
        }
@@ -1239,8 +1542,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",
@@ -1277,9 +1580,21 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, 
void *rcrb,
                        ichspi_lock = 1;
                }
                ich_set_bbar(0);
+               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 */
+               arg = extract_programmer_param("hwseq");
+               if (arg && !strcmp(arg, "yes"))
+                       hwseq_en = 1;
+               else if (arg && !strlen(arg))
+                       msg_pinfo("Missing argument for hwseq.\n");
+               else if (arg)
+                       msg_pinfo("Unknown argument for hwseq: %s\n", arg);
+               free(arg);
                tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFS);
                msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2);
                prettyprint_ich9_reg_hsfs(tmp2);
@@ -1363,19 +1678,36 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, 
void *rcrb,
 
                msg_pdbg("\n");
 #if BITFIELDS == 1
+               { /* to be able to declare desc here and avoid another
+                  * "#if BITFIELDS == 1" on top
+                  */
+               struct ich_descriptors desc = {{ 0 }};
                if (ichspi_desc) {
-                       struct ich_descriptors desc = {{ 0 }};
                        if (read_ich_descriptors_via_fdo(ich_spibar, &desc) ==
                            RET_OK)
                                prettyprint_ich_descriptors(CHIPSET_ICH_UNKNOWN,
                                                            &desc);
+                       /* 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 (desc.content.NC != 0)
+                               hwseq_en = 1;
+               } else
+                       hwseq_en = 0; /* disable it even if the user said yes */
+
+               if (hwseq_en > 0) {
+                       hwseq.size_comp0 = getFCBA_component_density(0, &desc);
+                       hwseq.size_comp1 = getFCBA_component_density(1, &desc);
+                       register_spi_programmer(&spi_programmer_ich_hwseq);
+               } else {
+                       register_spi_programmer(&spi_programmer_ich9);
+                       ich_init_opcodes();
+               }
                }
 #endif /* BITFIELDS == 1 */
                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,
-- 
1.7.1


_______________________________________________
flashrom mailing list
flashrom@flashrom.org
http://www.flashrom.org/mailman/listinfo/flashrom

Reply via email to