aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/ast-bmc/ast-sf-ctrl.c106
-rw-r--r--libflash/libflash-priv.h1
-rw-r--r--libflash/libflash.c97
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);