diff options
-rw-r--r-- | hw/ast-bmc/ast-sf-ctrl.c | 106 | ||||
-rw-r--r-- | libflash/libflash-priv.h | 1 | ||||
-rw-r--r-- | libflash/libflash.c | 97 |
3 files changed, 146 insertions, 58 deletions
diff --git a/hw/ast-bmc/ast-sf-ctrl.c b/hw/ast-bmc/ast-sf-ctrl.c index 356e01b..06287a8 100644 --- a/hw/ast-bmc/ast-sf-ctrl.c +++ b/hw/ast-bmc/ast-sf-ctrl.c @@ -29,7 +29,7 @@ #define __unused __attribute__((unused)) #endif -#define CALIBRATE_BUF_SIZE 1024 +#define CALIBRATE_BUF_SIZE 16384 struct ast_sf_ctrl { /* We have 2 controllers, one for the BMC flash, one for the PNOR */ @@ -223,22 +223,23 @@ static void ast_get_ahb_freq(void) FL_DBG("AST: AHB frequency: %d Mhz\n", ast_ahb_freq / 1000000); } -static int ast_sf_check_reads(struct ast_sf_ctrl *ct, const uint8_t *test_buf) +static int ast_sf_check_reads(struct ast_sf_ctrl *ct, + const uint8_t *golden_buf, uint8_t *test_buf) { - uint8_t buf[CALIBRATE_BUF_SIZE]; int i, rc; for (i = 0; i < 10; i++) { - rc = ast_copy_from_ahb(buf, ct->flash, CALIBRATE_BUF_SIZE); + rc = ast_copy_from_ahb(test_buf, ct->flash, CALIBRATE_BUF_SIZE); if (rc) return rc; - if (memcmp(buf, test_buf, CALIBRATE_BUF_SIZE) != 0) + if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0) return FLASH_ERR_VERIFY_FAILURE; } return 0; } -static int ast_sf_calibrate_reads(struct ast_sf_ctrl *ct, uint32_t hdiv, const uint8_t *test_buf) +static int ast_sf_calibrate_reads(struct ast_sf_ctrl *ct, uint32_t hdiv, + const uint8_t *golden_buf, uint8_t *test_buf) { int i, rc; int good_pass = -1, pass_count = 0; @@ -256,7 +257,7 @@ static int ast_sf_calibrate_reads(struct ast_sf_ctrl *ct, uint32_t hdiv, const u ct->fread_timing_val &= mask; ct->fread_timing_val |= FREAD_TPASS(i) << shift; ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg); - rc = ast_sf_check_reads(ct, test_buf); + rc = ast_sf_check_reads(ct, golden_buf, test_buf); if (rc && rc != FLASH_ERR_VERIFY_FAILURE) return rc; pass = (rc == 0); @@ -280,16 +281,40 @@ static int ast_sf_calibrate_reads(struct ast_sf_ctrl *ct, uint32_t hdiv, const u ct->fread_timing_val &= mask; ct->fread_timing_val |= FREAD_TPASS(good_pass) << shift; ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg); - FL_DBG("AST: * -> good is pass %d [0x%08x]\n", good_pass, ct->fread_timing_val); + FL_DBG("AST: * -> good is pass %d [0x%08x]\n", + good_pass, ct->fread_timing_val); return 0; } -static int ast_sf_optimize_reads(struct ast_sf_ctrl *ct, uint32_t max_freq) +static bool ast_calib_data_usable(const uint8_t *test_buf, uint32_t size) { - uint8_t test_buf[CALIBRATE_BUF_SIZE]; + const uint32_t *tb32 = (const uint32_t *)test_buf; + uint32_t i, cnt = 0; + + /* We check if we have enough words that are neither all 0 + * nor all 1's so the calibration can be considered valid. + * + * I use an arbitrary threshold for now of 64 + */ + size >>= 2; + for (i = 0; i < size; i++) { + if (tb32[i] != 0 && tb32[i] != 0xffffffff) + cnt++; + } + return cnt >= 64; +} + +static int ast_sf_optimize_reads(struct ast_sf_ctrl *ct, + struct flash_info *info __unused, + uint32_t max_freq) +{ + uint8_t *golden_buf, *test_buf; int i, rc, best_div = -1; uint32_t save_read_val = ct->ctl_read_val; + test_buf = malloc(CALIBRATE_BUF_SIZE * 2); + golden_buf = test_buf + CALIBRATE_BUF_SIZE; + /* We start with the dumbest setting and read some data */ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) | (0x00 << 28) | /* Single bit */ @@ -300,13 +325,24 @@ static int ast_sf_optimize_reads(struct ast_sf_ctrl *ct, uint32_t max_freq) (0x00); /* normal read */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); - rc = ast_copy_from_ahb(test_buf, ct->flash, CALIBRATE_BUF_SIZE); - if (rc) + rc = ast_copy_from_ahb(golden_buf, ct->flash, CALIBRATE_BUF_SIZE); + if (rc) { + free(test_buf); return rc; + } /* Establish our read mode with freq field set to 0 */ ct->ctl_read_val = save_read_val & 0xfffff0ff; + /* Check if calibration data is suitable */ + if (!ast_calib_data_usable(golden_buf, CALIBRATE_BUF_SIZE)) { + FL_INF("AST: Calibration area too uniform, " + "using low speed\n"); + ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); + free(test_buf); + return 0; + } + /* Now we iterate the HCLK dividers until we find our breaking point */ for (i = 5; i > 0; i--) { uint32_t tv, freq; @@ -320,14 +356,17 @@ static int ast_sf_optimize_reads(struct ast_sf_ctrl *ct, uint32_t max_freq) tv = ct->ctl_read_val | (ast_ct_hclk_divs[i - 1] << 8); ast_ahb_writel(tv, ct->ctl_reg); FL_DBG("AST: Trying HCLK/%d...\n", i); - rc = ast_sf_calibrate_reads(ct, i, test_buf); + rc = ast_sf_calibrate_reads(ct, i, golden_buf, test_buf); /* Some other error occurred, bail out */ - if (rc && rc != FLASH_ERR_VERIFY_FAILURE) + if (rc && rc != FLASH_ERR_VERIFY_FAILURE) { + free(test_buf); return rc; + } if (rc == 0) best_div = i; } + free(test_buf); /* Nothing found ? */ if (best_div < 0) @@ -345,7 +384,16 @@ static int ast_sf_get_hclk(uint32_t *ctl_val, uint32_t max_freq) { int i; - for (i = 1; i <= 5; i++) { + /* It appears that running commands at HCLK/2 on some micron + * chips results in occasionally reads of bogus status (that + * or unrelated chip hangs). + * + * Since we cannot calibrate properly the reads for commands, + * instead, let's limit our SPI frequency to HCLK/4 to stay + * on the safe side of things + */ +#define MIN_CMD_FREQ 4 + for (i = MIN_CMD_FREQ; i <= 5; i++) { uint32_t freq = ast_ahb_freq / i; if (freq >= max_freq) continue; @@ -357,11 +405,9 @@ static int ast_sf_get_hclk(uint32_t *ctl_val, uint32_t max_freq) static int ast_sf_setup_macronix(struct ast_sf_ctrl *ct, struct flash_info *info) { - int rc, div; + int rc, div __unused; uint8_t srcr[2]; - (void)info; - /* * Those Macronix chips support dual reads at 104Mhz * and dual IO at 84Mhz with 4 dummies. @@ -431,7 +477,7 @@ static int ast_sf_setup_macronix(struct ast_sf_ctrl *ct, struct flash_info *info (0x01); /* fast read */ /* Configure SPI flash read timing */ - rc = ast_sf_optimize_reads(ct, 104000000); + rc = ast_sf_optimize_reads(ct, info, 104000000); if (rc) { FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc); return rc; @@ -452,7 +498,6 @@ static int ast_sf_setup_macronix(struct ast_sf_ctrl *ct, struct flash_info *info (0x00); /* normal read */ div = ast_sf_get_hclk(&ct->ctl_val, 106000000); - (void)div; FL_DBG("AST: Command timing set to HCLK/%d\n", div); /* Update chip with current read config */ @@ -462,9 +507,7 @@ static int ast_sf_setup_macronix(struct ast_sf_ctrl *ct, struct flash_info *info static int ast_sf_setup_winbond(struct ast_sf_ctrl *ct, struct flash_info *info) { - int rc, div; - - (void)info; + int rc, div __unused; FL_DBG("AST: Setting up Windbond...\n"); @@ -484,7 +527,7 @@ static int ast_sf_setup_winbond(struct ast_sf_ctrl *ct, struct flash_info *info) (0x01); /* fast read */ /* Configure SPI flash read timing */ - rc = ast_sf_optimize_reads(ct, 104000000); + rc = ast_sf_optimize_reads(ct, info, 104000000); if (rc) { FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc); return rc; @@ -504,7 +547,6 @@ static int ast_sf_setup_winbond(struct ast_sf_ctrl *ct, struct flash_info *info) (0x01); /* fast read */ div = ast_sf_get_hclk(&ct->ctl_val, 106000000); - (void)div; FL_DBG("AST: Command timing set to HCLK/%d\n", div); /* Update chip with current read config */ @@ -515,7 +557,7 @@ static int ast_sf_setup_winbond(struct ast_sf_ctrl *ct, struct flash_info *info) static int ast_sf_setup_micron(struct ast_sf_ctrl *ct, struct flash_info *info) { uint8_t vconf, ext_id[6]; - int rc, div; + int rc, div __unused; FL_DBG("AST: Setting up Micron...\n"); @@ -560,9 +602,15 @@ static int ast_sf_setup_micron(struct ast_sf_ctrl *ct, struct flash_info *info) rc = ast_sf_cmd_wr(&ct->ops, CMD_MIC_WRVCONF, false, 0, &vconf, 1); if (rc != 0) { - FL_ERR("AST: Failed to write Micron vconf, sticking to dumb speed\n"); + FL_ERR("AST: Failed to write Micron vconf, " + " sticking to dumb speed\n"); goto dumb; } + rc = fl_sync_wait_idle(&ct->ops);; + if (rc != 0) { + FL_ERR("AST: Failed waiting for config write\n"); + return rc; + } FL_DBG("AST: Updated to : 0x%02x\n", vconf); /* @@ -580,7 +628,7 @@ static int ast_sf_setup_micron(struct ast_sf_ctrl *ct, struct flash_info *info) (0x01); /* fast read */ /* Configure SPI flash read timing */ - rc = ast_sf_optimize_reads(ct, 133000000); + rc = ast_sf_optimize_reads(ct, info, 133000000); if (rc) { FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc); return rc; @@ -600,11 +648,11 @@ static int ast_sf_setup_micron(struct ast_sf_ctrl *ct, struct flash_info *info) (0x00); /* norm read */ div = ast_sf_get_hclk(&ct->ctl_val, 133000000); - (void)div; FL_DBG("AST: Command timing set to HCLK/%d\n", div); /* Update chip with current read config */ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); + return 0; dumb: diff --git a/libflash/libflash-priv.h b/libflash/libflash-priv.h index c394d4c..ba6da66 100644 --- a/libflash/libflash-priv.h +++ b/libflash/libflash-priv.h @@ -226,6 +226,7 @@ struct spi_flash_ctrl { }; extern int fl_wren(struct spi_flash_ctrl *ct); +extern int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat); extern int fl_sync_wait_idle(struct spi_flash_ctrl *ct); #endif /* LIBFLASH_PRIV_H */ diff --git a/libflash/libflash.c b/libflash/libflash.c index f438efe..5badbff 100644 --- a/libflash/libflash.c +++ b/libflash/libflash.c @@ -40,27 +40,11 @@ struct flash_chip { void *smart_buf; /* Buffer for smart writes */ }; -static int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat) -{ - return ct->cmd_rd(ct, CMD_RDSR, false, 0, stat, 1); -} +bool libflash_debug; -/* Exported for internal use */ -int fl_wren(struct spi_flash_ctrl *ct) +int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat) { - uint8_t stat; - int i, rc; - - /* Some flashes need it to be hammered */ - for (i = 0; i < 1000; i++) { - rc = ct->cmd_wr(ct, CMD_WREN, false, 0, NULL, 0); - if (rc) return rc; - rc = fl_read_stat(ct, &stat); - if (rc) return rc; - if (stat & STAT_WEN) - return 0; - } - return FLASH_ERR_WREN_TIMEOUT; + return ct->cmd_rd(ct, CMD_RDSR, false, 0, stat, 1); } static void fl_micron_status(struct spi_flash_ctrl *ct) @@ -94,6 +78,31 @@ int fl_sync_wait_idle(struct spi_flash_ctrl *ct) /* return FLASH_ERR_WIP_TIMEOUT; */ } +/* Exported for internal use */ +int fl_wren(struct spi_flash_ctrl *ct) +{ + int i, rc; + uint8_t stat; + + /* Some flashes need it to be hammered */ + for (i = 0; i < 1000; i++) { + rc = ct->cmd_wr(ct, CMD_WREN, false, 0, NULL, 0); + if (rc) return rc; + rc = fl_read_stat(ct, &stat); + if (rc) return rc; + if (stat & STAT_WIP) { + FL_ERR("LIBFLASH: WREN has WIP status set !\n"); + rc = fl_sync_wait_idle(ct); + if (rc) + return rc; + continue; + } + if (stat & STAT_WEN) + return 0; + } + return FLASH_ERR_WREN_TIMEOUT; +} + int flash_read(struct flash_chip *c, uint32_t pos, void *buf, uint32_t len) { struct spi_flash_ctrl *ct = c->ctrl; @@ -425,6 +434,35 @@ int flash_smart_write(struct flash_chip *c, uint32_t dst, const void *src, return 0; } +static int fl_chip_id(struct spi_flash_ctrl *ct, uint8_t *id_buf, + uint32_t *id_size) +{ + int rc; + uint8_t stat; + + /* Check initial status */ + rc = fl_read_stat(ct, &stat); + if (rc) + return rc; + + /* If stuck writing, wait for idle */ + if (stat & STAT_WIP) { + FL_ERR("LIBFLASH: Flash in writing state ! Waiting...\n"); + rc = fl_sync_wait_idle(ct); + if (rc) + return rc; + } else + FL_DBG("LIBFLASH: Init status: %02x\n", stat); + + /* Fallback to get ID manually */ + rc = ct->cmd_rd(ct, CMD_RDID, false, 0, id_buf, 3); + if (rc) + return rc; + *id_size = 3; + + return 0; +} + static int flash_identify(struct flash_chip *c) { struct spi_flash_ctrl *ct = c->ctrl; @@ -438,15 +476,10 @@ static int flash_identify(struct flash_chip *c) /* High level controller interface */ id_size = MAX_ID_SIZE; rc = ct->chip_id(ct, id, &id_size); - if (rc) - return rc; - } else { - /* Fallback to get ID manually */ - rc = ct->cmd_rd(ct, CMD_RDID, false, 0, id, 3); - if (rc) - return rc; - id_size = 3; - } + } else + rc = fl_chip_id(ct, id, &id_size); + if (rc) + return rc; if (id_size < 3) return FLASH_ERR_CHIP_UNKNOWN; @@ -503,9 +536,15 @@ static int flash_identify(struct flash_chip *c) static int flash_set_4b(struct flash_chip *c, bool enable) { struct spi_flash_ctrl *ct = c->ctrl; + int rc; /* Some flash chips want this */ - fl_wren(ct); + rc = fl_wren(ct); + if (rc) { + FL_ERR("LIBFLASH: Error %d enabling write for set_4b\n", rc); + /* Ignore the error & move on (could be wrprotect chip) */ + } + /* Ignore error in case chip is write protected */ return ct->cmd_wr(ct, enable ? CMD_EN4B : CMD_EX4B, false, 0, NULL, 0); |