summaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-10-30 16:13:17 +1100
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-10-30 16:13:17 +1100
commitba41a2a1d0cd456c68918a79e88a1989d1de1649 (patch)
treec563bbde43d41b5eabae3b61d69ec00915ff6199 /hw
parent80c5d677204b36df437a89ecbe5bc171869ab669 (diff)
downloadtalos-skiboot-ba41a2a1d0cd456c68918a79e88a1989d1de1649.tar.gz
talos-skiboot-ba41a2a1d0cd456c68918a79e88a1989d1de1649.zip
libflash: Update from pflash-0.8.6
Improve calibration and use safer timings for commands. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/ast-bmc/ast-sf-ctrl.c106
1 files changed, 77 insertions, 29 deletions
diff --git a/hw/ast-bmc/ast-sf-ctrl.c b/hw/ast-bmc/ast-sf-ctrl.c
index 356e01b9..06287a86 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:
OpenPOWER on IntegriCloud