Am 20.08.2011 12:39 schrieb Stefan Tauner:
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)?)
We have no limits for the commit message size, but maybe we should start to package additional documentation with flashrom?
- revise (levels of) prints
- revise timeout values (for optimal performance)
- build upon the upcoming opaque interface framework instead
I see all those TODOs, and most of them should be addressed before enabling the code. That's why I'd like to get most of this patch merged really soon except for the few places where the new code is hooked up. That way we get a much smaller remaining patch and the discussion about user interfaces and infrastructure are not attached to a megapatch, but rather to a small "hook up hwseq" patch.
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 | 3 +- ichspi.c | 357 +++++++++++++++++++++++++++++++++++++++++++++++++++-- programmer.h | 1 + 7 files changed, 414 insertions(+), 13 deletions(-)
diff --git a/chipdrivers.h b/chipdrivers.h index 3f5b503..f3781ae 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -86,6 +86,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);
Postpone this hunk for the hookup patch.
/* jedec.c */ uint8_t oddparity(uint8_t val); void toggle_ready_jedec(chipaddr dst); diff --git a/flashchips.c b/flashchips.c index 4d741a0..20f7197 100644 --- a/flashchips.c +++ b/flashchips.c @@ -8782,7 +8782,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__))
Postpone this hunk for the hookup patch.
{ .vendor = "AMIC", .name = "unknown AMIC SPI chip", @@ -8915,6 +8935,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 */
Postpone...
#define ISSI_ID 0xD5 /* ISSI Integrated Silicon Solutions */
/* diff --git a/ich_descriptors.c b/ich_descriptors.c index ce40d9f..8076b09 100644 --- a/ich_descriptors.c +++ b/ich_descriptors.c @@ -214,6 +214,41 @@ void prettyprint_ich_descriptor_master(const struct ich_desc_master *mstr) msg_pdbg2("\n"); }
+/** 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 ich_descriptors *desc)
I'd prefer to have the order (desc, idx) for the parameters here.
+{
- uint8_t size_enc;
- static const int const dec_mem[6] = {
512 * 1024,
1 * 1024 * 1024,
2 * 1024 * 1024,
4 * 1024 * 1024,
8 * 1024 * 1024,
16 * 1024 * 1024,
- };
Kill dec_mem and replace dec_mem[size_enc] with (1<<(19+size_enc)).
- switch(idx) {
- case 0:
size_enc = desc->component.comp1_density;
break;
- case 1:
if (desc->content.NC == 0)
If NC is shorthand for "not connected", this looks like inverted logic.
return 0;
size_enc = desc->component.comp2_density;
break;
- default:
msg_perr("Only component index 0 or 1 are supported yet.\n");
More specific error message: "Only ICH SPI component..."
return 0;
- }
- if (size_enc > 5) {
msg_perr("Density of component with index %d is invalid. "
Same error message change here.
"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 b1da914..8ab798f 100644 --- a/ich_descriptors.h +++ b/ich_descriptors.h @@ -255,7 +255,8 @@ void prettyprint_ich_descriptor_region(const struct ich_descriptors *desc); void prettyprint_ich_descriptor_master(const struct ich_desc_master *master);
int read_ich_descriptors_via_fdo(void *spibar, struct ich_descriptors *desc);
+int getFCBA_component_density(uint8_t idx, const struct ich_descriptors *desc);
#endif /* BITFIELDS == 1 */ #endif /* __ICH_DESCRIPTORS_H__ */ #endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/ichspi.c b/ichspi.c index 2e25d34..61c0825 100644 --- a/ichspi.c +++ b/ichspi.c @@ -26,7 +26,10 @@ #if defined(__i386__) || defined(__x86_64__)
#include <string.h> +#include <stdlib.h> #include "flash.h" +#include "flashchips.h" +#include "chipdrivers.h"
Including flashchips.h or chipdrivers.h from a programmer driver is a sign that something went wrong during the design phase.
#include "programmer.h" #include "spi.h" #include "ich_descriptors.h" @@ -1084,6 +1087,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);
Can this write fail somehow?
+}
+/* Sets FADDR.FLA to 'addr' and returns the erase block size in bytes
- of the block containing this address. */
Is it really possible to encode per-region block sizes in a descriptor?
+static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr) +{
- uint8_t enc_berase;
- static const uint32_t const dec_berase[4] = {
decode_ instead of dec_. dec_ sounds too much like decrement. Or you pick a completely different name, e.g. eraseblock_sizes.
256,
4 * 1024,
8 * 1024,
64 * 1024
- };
- ich_hwseq_set_addr(addr);
- enc_berase = (REGREAD16(ICH9_REG_HSFS) & HSFS_BERASE) >>
HSFS_BERASE only has two adjacent bits set, correct?
HSFS_BERASE_OFF;
- return dec_berase[enc_berase];
+}
+/* Polls for Cycle Done Status, Flash Cycle Error or timeout in 10 us intervals.
Not something to be addressed in this patch, but we should really think about exponential backoff with maximum wait for such wait-for-hardware calls.
- 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,
Can we specify the timeout in usecs instead and use programmer_delay(8) to avoid division overhead (use shift instead)?
unsigned int len)
+{
- uint16_t hsfs;
- uint32_t addr;
- while ((((hsfs = REGREAD16(ICH9_REG_HSFS)) &
(HSFS_FDONE | HSFS_FCERR)) == 0) &&
Can we really ignore AEL here?
--timeout) {
programmer_delay(10);
- }
- REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS));
This clears all errors. OK.
- if (!timeout) {
addr = REGREAD32(ICH9_REG_FADDR) & 0x01FFFFFF;
msg_perr("Timeout error between offset 0x%08x and "
"0x%08x + %d (=0x%08x)!\n",
Please don't mix hex and decimal encoding.
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",
hex/decimal mixture.
addr, addr + len - 1, addr, len - 1);
prettyprint_ich9_reg_hsfs(hsfs);
prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC));
return 1;
- }
- return 0;
+}
ich_hwseq_probe, ich_hwseq_send_command, ich_hwseq_block_erase, ich_hwseq_read and ich_hwseq_write256 use an interface which does not fit well with flashrom. Could you cut out those functions and post them separately, preferably as part of the hookup patch I mentioned above?
+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 +1502,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,
+};
Move the hunk above to the hookup patch as well.
int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, int ich_generation) { @@ -1214,21 +1519,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:
spibar_offset = 0x3020; break; case 8:register_spi_programmer(&spi_programmer_ich7);
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; }register_spi_programmer(&spi_programmer_ich9);
@@ -1239,8 +1543,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:
Please make sure the VIA driver doesn't explode with this.
msg_pdbg("0x00: 0x%04x (SPIS)\n", mmio_readw(ich_spibar + 0)); msg_pdbg("0x02: 0x%04x (SPIC)\n",
@@ -1277,9 +1581,21 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, ichspi_lock = 1; } ich_set_bbar(ich_generation, 0);
ich_init_opcodes(); break;register_spi_programmer(&spi_programmer_ich7);
- case SPI_CONTROLLER_ICH9:
- case 8:
- case 9:
- case 10:
- default: /* Future version might behave the same */
OK until here.
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);
Please move the programmer parameter evaluation to the hookup patch.
tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFS); msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2); prettyprint_ich9_reg_hsfs(tmp2);
@@ -1363,18 +1679,37 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
msg_pdbg("\n");
#if BITFIELDS == 1
#if BITFIELDS needs to die.
{ /* to be able to declare desc here and avoid another
* "#if BITFIELDS == 1" on top
*/
if (ichspi_desc) {struct ich_descriptors desc = {{ 0 }};
Maybe rename ichspi_desc to ichspi_desc_active or ichspi_desc_enabled.
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 */
This case needs at least a msg_pinfo, maybe even msg_perr. We act contrary to the wishes of the user.
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);
Please move register_spi_programmer to the hookup patch. The rest of the code of this hunk should stay in this patch.
} else {
register_spi_programmer(&spi_programmer_ich9);
}ich_init_opcodes();
-#endif /* BITFIELDS == 1 */
}
+#else /* BITFIELDS == 1 */
Once you remove the #ifdef, the hunk below will probably die completely.
ich_init_opcodes();register_spi_programmer(&spi_programmer_ich9);
break;
- default:
/* Nothing */
+#endif /* BITFIELDS == 1 */ break; }
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,
Please move this to the hookup patch.
SPI_CONTROLLER_IT85XX, SPI_CONTROLLER_IT87XX, SPI_CONTROLLER_SB600,
Regards, Carl-Daniel
On Sat, 17 Sep 2011 20:58:41 +0200 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net wrote:
Am 20.08.2011 12:39 schrieb Stefan Tauner:
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)?)
We have no limits for the commit message size, but maybe we should start to package additional documentation with flashrom?
hard decision to make. my view about smaller bits of information (about 1-2 paragraphs): 1. just make commit logs verbose ++ context + in the repository ~ ok-ish accessibility via trac et al. - very low visibility -- not editable
2. blog posts/mails/wiki entries ++ high visibility + accessibility ~ editable by authors (except for mails) -- not in the repository/easily deprecated -- usually no code context
3. Documentation folder ++ full VCS support + keeps the code from getting cluttered ~ accessibility -- no context/not navigable because it is unstructured
4. documentation directly in the source ++ full VCS support + context: important code is in direct vicinity, but unrelated stuff too - accessibility because the big picture is spread - less code in view
for bigger stuff like the complete description of descriptors, hardware sequencing or ME unlocking the values change a lot. context becomes less important, versioning and changeability to, because the big picture is not hurt by minor improvements of understanding. also, long paragraphs inside the code might by frown upon by readers/devs.
in my other/private projects i usually have long(er) descriptions at the top of each file that explains the bigger picture and this is also what i miss most when i delve into a foreign code base. this can also be done in a Documentations folder, although i do not seem an apparent need to split it that way. the only advantage is smaller source files. having those big chunks in the wiki or in blog posts is a viable alternative imho, because it has a better visibility (also attracts search engines). the disadvantage is that those entries are easily forgotten to be kept in sync. but this is less of an issue for "the big picture" information. i am undecided on where to put bigger chunks. but it should probably be coherently in one medium and not spread over mails, wiki and blog entries etc. (like it is now).
- revise (levels of) prints
- revise timeout values (for optimal performance)
- build upon the upcoming opaque interface framework instead
I see all those TODOs, and most of them should be addressed before enabling the code. That's why I'd like to get most of this patch merged really soon except for the few places where the new code is hooked up. That way we get a much smaller remaining patch and the discussion about user interfaces and infrastructure are not attached to a megapatch, but rather to a small "hook up hwseq" patch.
i see your point, but i am a bit skeptical still. let's see how this works out.
Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at
diff --git a/ich_descriptors.c b/ich_descriptors.c index ce40d9f..8076b09 100644 --- a/ich_descriptors.c +++ b/ich_descriptors.c @@ -214,6 +214,41 @@ void prettyprint_ich_descriptor_master(const struct ich_desc_master *mstr) msg_pdbg2("\n"); }
+/** 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 ich_descriptors *desc)
I'd prefer to have the order (desc, idx) for the parameters here.
done
+{
- uint8_t size_enc;
- static const int const dec_mem[6] = {
512 * 1024,
1 * 1024 * 1024,
2 * 1024 * 1024,
4 * 1024 * 1024,
8 * 1024 * 1024,
16 * 1024 * 1024,
- };
Kill dec_mem and replace dec_mem[size_enc] with (1<<(19+size_enc)).
oh :) good point
- switch(idx) {
- case 0:
size_enc = desc->component.comp1_density;
break;
- case 1:
if (desc->content.NC == 0)
If NC is shorthand for "not connected", this looks like inverted logic.
one click away in any sane editor is: NC :2, /* Number Of Components */ ;) which is 0-based btw, hence NC == 0 -> only one device -> density of component with index 1: 0
return 0;
size_enc = desc->component.comp2_density;
break;
- default:
msg_perr("Only component index 0 or 1 are supported yet.\n");
More specific error message: "Only ICH SPI component..."
return 0;
- }
- if (size_enc > 5) {
msg_perr("Density of component with index %d is invalid. "
Same error message change here.
since we are no longer limited by the deleted array and the future development of those bits (if any) is pretty obvious, should we drop the return 0 and change the message to a warning? the densities have 3 bits, so value 6 and 7 (32 and 64 MB) are reserved atm.
"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 b1da914..8ab798f 100644 --- a/ich_descriptors.h +++ b/ich_descriptors.h @@ -255,7 +255,8 @@ void prettyprint_ich_descriptor_region(const struct ich_descriptors *desc); void prettyprint_ich_descriptor_master(const struct ich_desc_master *master);
int read_ich_descriptors_via_fdo(void *spibar, struct ich_descriptors *desc);
+int getFCBA_component_density(uint8_t idx, const struct ich_descriptors *desc);
#endif /* BITFIELDS == 1 */ #endif /* __ICH_DESCRIPTORS_H__ */ #endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/ichspi.c b/ichspi.c index 2e25d34..61c0825 100644 --- a/ichspi.c +++ b/ichspi.c @@ -26,7 +26,10 @@ #if defined(__i386__) || defined(__x86_64__)
#include <string.h> +#include <stdlib.h> #include "flash.h" +#include "flashchips.h" +#include "chipdrivers.h"
Including flashchips.h or chipdrivers.h from a programmer driver is a sign that something went wrong during the design phase.
that's a bit oversimplified imo. there is no way to implement this without some major changes to flashrom and this is exactly what i tried to accomplish. and i don't have a problem with changing this if the opaque framework is ready/available.
#include "programmer.h" #include "spi.h" #include "ich_descriptors.h" @@ -1084,6 +1087,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;
i have postponed the struct for now. it is used to communicate the component sizes from the init function to the probing function. both parts are not in the first patch.
+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);
Can this write fail somehow?
not really, but the address could be truncated, if it is > 0x01FFFFFF. and of course setting it to some address > total size of the chips might have funny effects. the question is where to deal with this. imho not in this function but its callers. in the first patch this is only ich_hwseq_get_erase_block_size which has no persistence effects. i have expanded the comment of ich_hwseq_get_erase_block to indicate that the address must be valid and added a description of ich_hwseq_set_addr.
+}
+/* Sets FADDR.FLA to 'addr' and returns the erase block size in bytes
- of the block containing this address. */
Is it really possible to encode per-region block sizes in a descriptor?
not per FREG-defined "region" but per FPB-defined flash partition. i have added an explanation of this.
+static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr) +{
- uint8_t enc_berase;
- static const uint32_t const dec_berase[4] = {
decode_ instead of dec_. dec_ sounds too much like decrement. Or you pick a completely different name, e.g. eraseblock_sizes.
really? together with another variable that has the prefix enc_? hm. what about "encoded" and "decoded". it is pretty clear that we are doing some stuff with erase block(s/sizes) so that is redundant anyway.
256,
4 * 1024,
8 * 1024,
64 * 1024
- };
- ich_hwseq_set_addr(addr);
- enc_berase = (REGREAD16(ICH9_REG_HSFS) & HSFS_BERASE) >>
HSFS_BERASE only has two adjacent bits set, correct?
jup: #define HSFS_BERASE (0x3 << HSFS_BERASE_OFF)
HSFS_BERASE_OFF;
- return dec_berase[enc_berase];
+}
+/* Polls for Cycle Done Status, Flash Cycle Error or timeout in 10 us intervals.
Not something to be addressed in this patch, but we should really think about exponential backoff with maximum wait for such wait-for-hardware calls.
have we talked about this already? i can remember thinking about this already too. at least specifying one (big) initial delay and then some finer grained querying intervals would be a good idea. hm actually both is needed. a initial delay and a exponential backing off delay.
- 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,
Can we specify the timeout in usecs instead and use programmer_delay(8) to avoid division overhead (use shift instead)?
please explain, which division? hm probably the non-existent one that should be there to really do what the comment says :)
i have added this (and changed the 10 to 8 everywhere): timeout >>= 3; /* equals /= 8; scale timeout duration to counter */ should i drop the shifting because the compiler will do it anyway and it is better readable?
unsigned int len)
+{
- uint16_t hsfs;
- uint32_t addr;
- while ((((hsfs = REGREAD16(ICH9_REG_HSFS)) &
(HSFS_FDONE | HSFS_FCERR)) == 0) &&
Can we really ignore AEL here?
we never care for AEL specifically, because it is just an additional flag that indicates the reason for a set FCERR afaik.
--timeout) {
programmer_delay(10);
- }
- REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS));
This clears all errors. OK.
jup
- if (!timeout) {
addr = REGREAD32(ICH9_REG_FADDR) & 0x01FFFFFF;
msg_perr("Timeout error between offset 0x%08x and "
"0x%08x + %d (=0x%08x)!\n",
Please don't mix hex and decimal encoding.
please think about this twice. imho this form gives the most readable output. addresses are easier to read as hex, but lengths are easier to grasp in decimal (at least for me). the base is clearly visible for all values too.
if you insist on this i would suggest removing the length value completely and only use "Timeout error between offset 0x%08x and 0x%08x!\n". that's less information though and will not increase readability imo.
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",
hex/decimal mixture.
ditto
addr, addr + len - 1, addr, len - 1);
prettyprint_ich9_reg_hsfs(hsfs);
prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC));
return 1;
- }
- return 0;
+}
ich_hwseq_probe, ich_hwseq_send_command, ich_hwseq_block_erase, ich_hwseq_read and ich_hwseq_write256 use an interface which does not fit well with flashrom. Could you cut out those functions and post them separately, preferably as part of the hookup patch I mentioned above?
done
+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,
+};
Move the hunk above to the hookup patch as well.
likewise
int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, int ich_generation) { @@ -1214,21 +1519,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:
spibar_offset = 0x3020; break; case 8:register_spi_programmer(&spi_programmer_ich7);
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; }register_spi_programmer(&spi_programmer_ich9);
@@ -1239,8 +1543,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:
Please make sure the VIA driver doesn't explode with this.
afaics VIA driver directly calls via_init_spi and never ich_spi_init. it registers its own programmer there. i can't see how it would be affected.
from a quick look i think we should extract a ich_init_spi_7 (or so) from ich_init_spi and combine it with via_init_spi. this should be done together with the ich7 cleanup/macro definition patch.
msg_pdbg("0x00: 0x%04x (SPIS)\n", mmio_readw(ich_spibar + 0)); msg_pdbg("0x02: 0x%04x (SPIC)\n",
@@ -1277,9 +1581,21 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb, ichspi_lock = 1; } ich_set_bbar(ich_generation, 0);
ich_init_opcodes(); break;register_spi_programmer(&spi_programmer_ich7);
- case SPI_CONTROLLER_ICH9:
- case 8:
- case 9:
- case 10:
- default: /* Future version might behave the same */
OK until here.
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);
Please move the programmer parameter evaluation to the hookup patch.
gone
tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFS); msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2); prettyprint_ich9_reg_hsfs(tmp2);
@@ -1363,18 +1679,37 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
msg_pdbg("\n");
#if BITFIELDS == 1
#if BITFIELDS needs to die.
it is already dead, but you wanted me to post the first patch of the series only...
{ /* to be able to declare desc here and avoid another
* "#if BITFIELDS == 1" on top
*/
if (ichspi_desc) {struct ich_descriptors desc = {{ 0 }};
Maybe rename ichspi_desc to ichspi_desc_active or ichspi_desc_enabled.
if at all it should be _valid because this is used all over the datasheet. and the ichspi_ prefix is pretty useless since it is a local variable. have changed this to desc_valid. is that ok with you?
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 */
This case needs at least a msg_pinfo, maybe even msg_perr. We act contrary to the wishes of the user.
true.
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);
Please move register_spi_programmer to the hookup patch. The rest of the code of this hunk should stay in this patch.
hm.. i have removed hwseq_en and the struct already... and i think it would be better off in the hookup patch anyway. this actually IS the hookup code :)
ich_init_opcodes();register_spi_programmer(&spi_programmer_ich9);
break;
- default:
/* Nothing */
+#endif /* BITFIELDS == 1 */ break; }
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,
Please move this to the hookup patch.
gone.
i had to add #if 0 around the unreachable code to not break compilation. i would rather keep the patch out of the repository until we can merge the hookup code too. if i wont change it, there is no need to review it again then and since it only touches ich-specific stuff and files it will be easy to keep it rebased to HEAD even outside subversion. do you want me to mail in the new patch set (which also includes an unchanged ich_descriptor_tool patch in addition to the two hwseq patches) and how do we continue from here?