aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/ast-bmc/ast-sf-ctrl.c552
-rw-r--r--hw/sfc-ctrl.c4
-rw-r--r--include/ast.h6
-rw-r--r--libflash/libflash-priv.h56
-rw-r--r--libflash/libflash.c75
5 files changed, 563 insertions, 130 deletions
diff --git a/hw/ast-bmc/ast-sf-ctrl.c b/hw/ast-bmc/ast-sf-ctrl.c
index e0d5fcc..356e01b 100644
--- a/hw/ast-bmc/ast-sf-ctrl.c
+++ b/hw/ast-bmc/ast-sf-ctrl.c
@@ -29,6 +29,8 @@
#define __unused __attribute__((unused))
#endif
+#define CALIBRATE_BUF_SIZE 1024
+
struct ast_sf_ctrl {
/* We have 2 controllers, one for the BMC flash, one for the PNOR */
uint8_t type;
@@ -42,6 +44,10 @@ struct ast_sf_ctrl {
/* Control register value for (fast) reads */
uint32_t ctl_read_val;
+ /* Flash read timing register */
+ uint32_t fread_timing_reg;
+ uint32_t fread_timing_val;
+
/* Address of the flash mapping */
uint32_t flash;
@@ -52,6 +58,16 @@ struct ast_sf_ctrl {
struct spi_flash_ctrl ops;
};
+static uint32_t ast_ahb_freq;
+
+static const uint32_t ast_ct_hclk_divs[] = {
+ 0xf, /* HCLK */
+ 0x7, /* HCLK/2 */
+ 0xe, /* HCLK/3 */
+ 0x6, /* HCLK/4 */
+ 0xd, /* HCLK/5 */
+};
+
static int ast_sf_start_cmd(struct ast_sf_ctrl *ct, uint8_t cmd)
{
/* Switch to user mode, CE# dropped */
@@ -172,10 +188,444 @@ static int ast_sf_read(struct spi_flash_ctrl *ctrl, uint32_t pos,
return ast_copy_from_ahb(buf, ct->flash + pos, len);
}
-static int ast_sf_setup(struct spi_flash_ctrl *ctrl, struct flash_info *info,
- uint32_t *tsize)
+static void ast_get_ahb_freq(void)
+{
+ static const uint32_t cpu_freqs_24_48[] = {
+ 384000000,
+ 360000000,
+ 336000000,
+ 408000000
+ };
+ static const uint32_t cpu_freqs_25[] = {
+ 400000000,
+ 375000000,
+ 350000000,
+ 425000000
+ };
+ static const uint32_t ahb_div[] = { 1, 2, 4, 3 };
+ uint32_t strap, cpu_clk, div;
+
+ if (ast_ahb_freq)
+ return;
+
+ /* HW strapping gives us the CPU freq and AHB divisor */
+ strap = ast_ahb_readl(SCU_HW_STRAPPING);
+ if (strap & 0x00800000) {
+ FL_DBG("AST: CLKIN 25Mhz\n");
+ cpu_clk = cpu_freqs_25[(strap >> 8) & 3];
+ } else {
+ FL_DBG("AST: CLKIN 24/48Mhz\n");
+ cpu_clk = cpu_freqs_24_48[(strap >> 8) & 3];
+ }
+ FL_DBG("AST: CPU frequency: %d Mhz\n", cpu_clk / 1000000);
+ div = ahb_div[(strap >> 10) & 3];
+ ast_ahb_freq = cpu_clk / div;
+ 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)
+{
+ 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);
+ if (rc)
+ return rc;
+ if (memcmp(buf, test_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)
+{
+ int i, rc;
+ int good_pass = -1, pass_count = 0;
+ uint32_t shift = (hdiv - 1) << 2;
+ uint32_t mask = ~(0xfu << shift);
+
+#define FREAD_TPASS(i) (((i) / 2) | (((i) & 1) ? 0 : 8))
+
+ /* Try HCLK delay 0..5, each one with/without delay and look for a
+ * good pair.
+ */
+ for (i = 0; i < 12; i++) {
+ bool pass;
+
+ 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);
+ if (rc && rc != FLASH_ERR_VERIFY_FAILURE)
+ return rc;
+ pass = (rc == 0);
+ FL_DBG(" * [%08x] %d HCLK delay, %dns DI delay : %s\n",
+ ct->fread_timing_val, i/2, (i & 1) ? 0 : 4, pass ? "PASS" : "FAIL");
+ if (pass) {
+ pass_count++;
+ if (pass_count == 3) {
+ good_pass = i - 1;
+ break;
+ }
+ } else
+ pass_count = 0;
+ }
+
+ /* No good setting for this frequency */
+ if (good_pass < 0)
+ return FLASH_ERR_VERIFY_FAILURE;
+
+ /* We have at least one pass of margin, let's use first pass */
+ 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);
+ return 0;
+}
+
+static int ast_sf_optimize_reads(struct ast_sf_ctrl *ct, uint32_t max_freq)
+{
+ uint8_t test_buf[CALIBRATE_BUF_SIZE];
+ int i, rc, best_div = -1;
+ uint32_t save_read_val = ct->ctl_read_val;
+
+ /* We start with the dumbest setting and read some data */
+ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# max */
+ (0x03 << 16) | /* use normal reads */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x00 << 6) | /* no dummy cycle */
+ (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)
+ return rc;
+
+ /* Establish our read mode with freq field set to 0 */
+ ct->ctl_read_val = save_read_val & 0xfffff0ff;
+
+ /* Now we iterate the HCLK dividers until we find our breaking point */
+ for (i = 5; i > 0; i--) {
+ uint32_t tv, freq;
+
+ /* Compare timing to max */
+ freq = ast_ahb_freq / i;
+ if (freq >= max_freq)
+ continue;
+
+ /* Set the timing */
+ 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);
+
+ /* Some other error occurred, bail out */
+ if (rc && rc != FLASH_ERR_VERIFY_FAILURE)
+ return rc;
+ if (rc == 0)
+ best_div = i;
+ }
+
+ /* Nothing found ? */
+ if (best_div < 0)
+ FL_ERR("AST: No good frequency, using dumb slow\n");
+ else {
+ FL_DBG("AST: Found good read timings at HCLK/%d\n", best_div);
+ ct->ctl_read_val |= (ast_ct_hclk_divs[best_div - 1] << 8);
+ }
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ return 0;
+}
+
+static int ast_sf_get_hclk(uint32_t *ctl_val, uint32_t max_freq)
+{
+ int i;
+
+ for (i = 1; i <= 5; i++) {
+ uint32_t freq = ast_ahb_freq / i;
+ if (freq >= max_freq)
+ continue;
+ *ctl_val |= (ast_ct_hclk_divs[i - 1] << 8);
+ return i;
+ }
+ return 0;
+}
+
+static int ast_sf_setup_macronix(struct ast_sf_ctrl *ct, struct flash_info *info)
+{
+ int rc, div;
+ uint8_t srcr[2];
+
+ (void)info;
+
+ /*
+ * Those Macronix chips support dual reads at 104Mhz
+ * and dual IO at 84Mhz with 4 dummies.
+ *
+ * Our calibration algo should give us something along
+ * the lines of HCLK/3 (HCLK/2 seems to work sometimes
+ * but appears to be fairly unreliable) which is 64Mhz
+ *
+ * So we chose dual IO mode.
+ *
+ * The CE# inactive width for reads must be 7ns, we set it
+ * to 3T which is about 15ns at the fastest speed we support
+ * HCLK/2) as I've had issue with smaller values.
+ *
+ * For write and program it's 30ns so let's set the value
+ * for normal ops to 6T.
+ *
+ * Preserve the current 4b mode.
+ */
+ FL_DBG("AST: Setting up Macronix...\n");
+
+ /*
+ * Read the status and config registers
+ */
+ rc = ast_sf_cmd_rd(&ct->ops, CMD_RDSR, false, 0, &srcr[0], 1);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to read status\n");
+ return rc;
+ }
+ rc = ast_sf_cmd_rd(&ct->ops, CMD_RDCR, false, 0, &srcr[1], 1);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to read configuration\n");
+ return rc;
+ }
+
+ FL_DBG("AST: Macronix SR:CR: 0x%02x:%02x\n", srcr[0], srcr[1]);
+
+ /* Switch to 8 dummy cycles to enable 104Mhz operations */
+ srcr[1] = (srcr[1] & 0x3f) | 0x80;
+
+ rc = fl_wren(&ct->ops);
+ if (rc) {
+ FL_ERR("AST: Failed to WREN for Macronix config\n");
+ return rc;
+ }
+
+ rc = ast_sf_cmd_wr(&ct->ops, CMD_WRSR, false, 0, srcr, 2);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to write Macronix config\n");
+ return rc;
+ }
+ rc = fl_sync_wait_idle(&ct->ops);;
+ if (rc != 0) {
+ FL_ERR("AST: Failed waiting for config write\n");
+ return rc;
+ }
+
+ FL_DBG("AST: Macronix SR:CR: 0x%02x:%02x\n", srcr[0], srcr[1]);
+
+ /* Use 2READ */
+ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x03 << 28) | /* Dual IO */
+ (0x0d << 24) | /* CE# width 3T */
+ (0xbb << 16) | /* 2READ command */
+ (0x00 << 8) | /* HCLK/16 (optimize later) */
+ (0x02 << 6) | /* 2 bytes dummy cycle (8 clocks) */
+ (0x01); /* fast read */
+
+ /* Configure SPI flash read timing */
+ rc = ast_sf_optimize_reads(ct, 104000000);
+ if (rc) {
+ FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * For other commands and writes also increase the SPI clock
+ * to HCLK/2 since the chip supports up to 133Mhz and set
+ * CE# inactive to 6T. We request a timing that is 20% below
+ * the limit of the chip, so about 106Mhz which should fit.
+ */
+ ct->ctl_val = (ct->ctl_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x0a << 24) | /* CE# width 6T (b1010) */
+ (0x00 << 16) | /* no command */
+ (0x00 << 8) | /* HCLK/16 (done later) */
+ (0x00 << 6) | /* no dummy cycle */
+ (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 */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ return 0;
+}
+
+static int ast_sf_setup_winbond(struct ast_sf_ctrl *ct, struct flash_info *info)
+{
+ int rc, div;
+
+ (void)info;
+
+ FL_DBG("AST: Setting up Windbond...\n");
+
+ /*
+ * This Windbond chip support dual reads at 104Mhz
+ * with 8 dummy cycles.
+ *
+ * The CE# inactive width for reads must be 10ns, we set it
+ * to 3T which is about 15.6ns.
+ */
+ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x02 << 28) | /* Dual bit data only */
+ (0x0e << 24) | /* CE# width 2T (b1110) */
+ (0x3b << 16) | /* DREAD command */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x01 << 6) | /* 1-byte dummy cycle */
+ (0x01); /* fast read */
+
+ /* Configure SPI flash read timing */
+ rc = ast_sf_optimize_reads(ct, 104000000);
+ if (rc) {
+ FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * For other commands and writes also increase the SPI clock
+ * to HCLK/2 since the chip supports up to 133Mhz. CE# inactive
+ * for write and erase is 50ns so let's set it to 10T.
+ */
+ ct->ctl_val = (ct->ctl_read_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x06 << 24) | /* CE# width 10T (b0110) */
+ (0x00 << 16) | /* no command */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x00 << 6) | /* no dummy cycle */
+ (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 */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ return 0;
+}
+
+static int ast_sf_setup_micron(struct ast_sf_ctrl *ct, struct flash_info *info)
+{
+ uint8_t vconf, ext_id[6];
+ int rc, div;
+
+ FL_DBG("AST: Setting up Micron...\n");
+
+ /*
+ * Read the extended chip ID to try to detect old vs. new
+ * flashes since old Micron flashes have a lot of issues
+ */
+ rc = ast_sf_cmd_rd(&ct->ops, CMD_RDID, false, 0, ext_id, 6);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to read Micron ext ID, sticking to dumb speed\n");
+ return 0;
+ }
+ /* Check ID matches expectations */
+ if (ext_id[0] != ((info->id >> 16) & 0xff) ||
+ ext_id[1] != ((info->id >> 8) & 0xff) ||
+ ext_id[2] != ((info->id ) & 0xff)) {
+ FL_ERR("AST: Micron ext ID mismatch, sticking to dumb speed\n");
+ return 0;
+ }
+ FL_DBG("AST: Micron ext ID byte: 0x%02x\n", ext_id[4]);
+
+ /* Check for old (<45nm) chips, don't try to be fancy on those */
+ if (!(ext_id[4] & 0x40)) {
+ FL_DBG("AST: Old chip, using dumb timings\n");
+ goto dumb;
+ }
+
+ /*
+ * Read the micron specific volatile configuration reg
+ */
+ rc = ast_sf_cmd_rd(&ct->ops, CMD_MIC_RDVCONF, false, 0, &vconf, 1);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to read Micron vconf, sticking to dumb speed\n");
+ goto dumb;
+ }
+ FL_DBG("AST: Micron VCONF: 0x%02x\n", vconf);
+
+ /* Switch to 8 dummy cycles (we might be able to operate with 4
+ * but let's keep some margin
+ */
+ vconf = (vconf & 0x0f) | 0x80;
+
+ 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");
+ goto dumb;
+ }
+ FL_DBG("AST: Updated to : 0x%02x\n", vconf);
+
+ /*
+ * Try to do full dual IO, with 8 dummy cycles it supports 133Mhz
+ *
+ * The CE# inactive width for reads must be 20ns, we set it
+ * to 4T which is about 20.8ns.
+ */
+ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x03 << 28) | /* Single bit */
+ (0x0c << 24) | /* CE# 4T */
+ (0xbb << 16) | /* 2READ command */
+ (0x00 << 8) | /* HCLK/16 (optimize later) */
+ (0x02 << 6) | /* 8 dummy cycles (2 bytes) */
+ (0x01); /* fast read */
+
+ /* Configure SPI flash read timing */
+ rc = ast_sf_optimize_reads(ct, 133000000);
+ if (rc) {
+ FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * For other commands and writes also increase the SPI clock
+ * to HCLK/2 since the chip supports up to 133Mhz. CE# inactive
+ * for write and erase is 50ns so let's set it to 10T.
+ */
+ ct->ctl_val = (ct->ctl_read_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x06 << 24) | /* CE# width 10T (b0110) */
+ (0x00 << 16) | /* no command */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x00 << 6) | /* no dummy cycle */
+ (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:
+ ct->ctl_val = ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# max */
+ (0x03 << 16) | /* use normal reads */
+ (0x06 << 8) | /* HCLK/4 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* normal read */
+
+ /* Update chip with current read config */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ return 0;
+}
+
+static int ast_sf_setup(struct spi_flash_ctrl *ctrl, uint32_t *tsize)
{
struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ struct flash_info *info = ctrl->finfo;
(void)tsize;
@@ -186,89 +636,13 @@ static int ast_sf_setup(struct spi_flash_ctrl *ctrl, struct flash_info *info,
switch(info->id) {
case 0xc22019: /* MX25L25635F */
case 0xc2201a: /* MX66L51235F */
- /*
- * Those Macronix chips support dual IO reads at 104Mhz
- * with 8 dummy cycles so let's use HCLK/2 which is 96Mhz.
- *
- * We use DREAD (dual read) for now as it defaults to 8
- * dummy cycles. Eventually we'd like to use 2READ (which
- * also has the address using 2 IOs) but that defaults
- * to 6 dummy cycles and we can only do a multiple of bytes
- * (Note: I think that accounts for the dual IO so a byte is
- * probably 4 clocks in that mode, but I need to dlb check).
- *
- * We can change the configuration of the flash so we can
- * do that later, it's a bit more complex.
- *
- * The CE# inactive width for reads must be 7ns, we set it
- * to 2T which is about 10.4ns.
- *
- * For write and program it's 30ns so let's set the value
- * for normal ops to 6T.
- *
- * Preserve the current 4b mode.
- */
- ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
- (0x02 << 28) | /* Dual bit data only */
- (0x0e << 24) | /* CE# width 2T (b1110) */
- (0x3b << 16) | /* DREAD command */
- (0x07 << 8) | /* HCLK/2 */
- (0x01 << 6) | /* 1-byte dummy cycle */
- (0x01); /* fast read */
-
- /* Configure SPI flash read timing ? */
-
- /*
- * For other commands and writes also increase the SPI clock
- * to HCLK/2 since the chip supports up to 133Mhz and set
- * CE# inactive to 6T
- */
- ct->ctl_val = (ct->ctl_val & 0x2000) |
- (0x00 << 28) | /* Single bit */
- (0x0a << 24) | /* CE# width 6T (b1010) */
- (0x00 << 16) | /* no command */
- (0x07 << 8) | /* HCLK/2 */
- (0x00 << 6) | /* no dummy cycle */
- (0x00); /* normal read */
-
- /* Update chip with current read config */
- ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
- break;
+ return ast_sf_setup_macronix(ct, info);
case 0xef4018: /* W25Q128BV */
- /*
- * This Windbond chip support dual IO reads at 104Mhz
- * with 8 dummy cycles so let's use HCLK/2.
- *
- * The CE# inactive width for reads must be 10ns, we set it
- * to 3T which is about 15.6ns.
- */
- ct->ctl_read_val =
- (0x02 << 28) | /* Dual bit data only */
- (0x0e << 24) | /* CE# width 2T (b1110) */
- (0x3b << 16) | /* DREAD command */
- (0x07 << 8) | /* HCLK/2 */
- (0x01 << 6) | /* 1-byte dummy cycle */
- (0x01); /* fast read */
-
- /* Configure SPI flash read timing ? */
-
- /*
- * For other commands and writes also increase the SPI clock
- * to HCLK/2 since the chip supports up to 133Mhz. CE# inactive
- * for write and erase is 50ns so let's set it to 10T.
- */
- ct->ctl_val =
- (0x00 << 28) | /* Single bit */
- (0x06 << 24) | /* CE# width 10T (b0110) */
- (0x00 << 16) | /* no command */
- (0x07 << 8) | /* HCLK/2 */
- (0x00 << 6) | /* no dummy cycle */
- (0x01); /* fast read */
-
- /* Update chip with current read config */
- ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
- break;
+ return ast_sf_setup_winbond(ct, info);
+ case 0x20ba20: /* MT25Qx512xx */
+ return ast_sf_setup_micron(ct, info);
}
+ /* No special tuning */
return 0;
}
@@ -277,6 +651,7 @@ static bool ast_sf_init_pnor(struct ast_sf_ctrl *ct)
uint32_t reg;
ct->ctl_reg = PNOR_SPI_FCTL_CTRL;
+ ct->fread_timing_reg = PNOR_SPI_FREAD_TIMING;
ct->flash = PNOR_FLASH_BASE;
/* Enable writing to the controller */
@@ -312,8 +687,12 @@ static bool ast_sf_init_pnor(struct ast_sf_ctrl *ct)
/* Initial read mode is default */
ct->ctl_read_val = ct->ctl_val;
+ /* Initial read timings all 0 */
+ ct->fread_timing_val = 0;
+
/* Configure for read */
ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg);
if (ct->ctl_val & 0x2000)
ct->mode_4b = true;
@@ -326,6 +705,7 @@ static bool ast_sf_init_pnor(struct ast_sf_ctrl *ct)
static bool ast_sf_init_bmc(struct ast_sf_ctrl *ct)
{
ct->ctl_reg = BMC_SPI_FCTL_CTRL;
+ ct->fread_timing_reg = BMC_SPI_FREAD_TIMING;
ct->flash = BMC_FLASH_BASE;
/*
@@ -347,8 +727,12 @@ static bool ast_sf_init_bmc(struct ast_sf_ctrl *ct)
/* Initial read mode is default */
ct->ctl_read_val = ct->ctl_val;
+ /* Initial read timings all 0 */
+ ct->fread_timing_val = 0;
+
/* Configure for read */
ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg);
ct->mode_4b = false;
@@ -376,6 +760,8 @@ int ast_sf_open(uint8_t type, struct spi_flash_ctrl **ctrl)
ct->ops.read = ast_sf_read;
ct->ops.setup = ast_sf_setup;
+ ast_get_ahb_freq();
+
if (type == AST_SF_TYPE_PNOR) {
if (!ast_sf_init_pnor(ct))
goto fail;
diff --git a/hw/sfc-ctrl.c b/hw/sfc-ctrl.c
index de163c5..16922d3 100644
--- a/hw/sfc-ctrl.c
+++ b/hw/sfc-ctrl.c
@@ -383,10 +383,10 @@ static int sfc_erase(struct spi_flash_ctrl *ctrl, uint32_t addr,
return 0;
}
-static int sfc_setup(struct spi_flash_ctrl *ctrl, struct flash_info *info,
- uint32_t *tsize)
+static int sfc_setup(struct spi_flash_ctrl *ctrl, uint32_t *tsize)
{
struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
+ struct flash_info *info = ctrl->finfo;
uint32_t er_flags;
(void)tsize;
diff --git a/include/ast.h b/include/ast.h
index f97aa44..efc898d 100644
--- a/include/ast.h
+++ b/include/ast.h
@@ -23,12 +23,14 @@
/* SPI Flash controller #1 (BMC) */
#define BMC_SPI_FCTL_BASE 0x1E620000
#define BMC_SPI_FCTL_CTRL (BMC_SPI_FCTL_BASE + 0x10)
+#define BMC_SPI_FREAD_TIMING (BMC_SPI_FCTL_BASE + 0x94)
#define BMC_FLASH_BASE 0x20000000
/* SPI Flash controller #2 (PNOR) */
#define PNOR_SPI_FCTL_BASE 0x1E630000
#define PNOR_SPI_FCTL_CONF (PNOR_SPI_FCTL_BASE + 0x00)
#define PNOR_SPI_FCTL_CTRL (PNOR_SPI_FCTL_BASE + 0x04)
+#define PNOR_SPI_FREAD_TIMING (PNOR_SPI_FCTL_BASE + 0x14)
#define PNOR_FLASH_BASE 0x30000000
/* LPC registers */
@@ -45,6 +47,10 @@
#define VUART1_ADDRL (VUART1_BASE + 0x28)
#define VUART1_ADDRH (VUART1_BASE + 0x2c)
+/* SCU registers */
+#define SCU_BASE 0x1e6e2000
+#define SCU_HW_STRAPPING (SCU_BASE + 0x70)
+
/*
* AHB Accessors
*/
diff --git a/libflash/libflash-priv.h b/libflash/libflash-priv.h
index 44fa513..c394d4c 100644
--- a/libflash/libflash-priv.h
+++ b/libflash/libflash-priv.h
@@ -21,21 +21,30 @@
#include <ccan/container_of/container_of.h>
/* Flash commands */
-#define CMD_PP 0x02
-#define CMD_READ 0x03
-#define CMD_WRDI 0x04
-#define CMD_RDSR 0x05
-#define CMD_WREN 0x06
-#define CMD_SE 0x20
-#define CMD_RDSCUR 0x2b
-#define CMD_BE32K 0x52
-#define CMD_CE 0x60
-#define CMD_RDID 0x9f
-#define CMD_EN4B 0xb7
-#define CMD_BE 0xd8
-#define CMD_RDDPB 0xe0
-#define CMD_RDSPB 0xe2
-#define CMD_EX4B 0xe9
+#define CMD_WRSR 0x01 /* Write Status Register (also config. on Macronix) */
+#define CMD_PP 0x02 /* Page Program */
+#define CMD_READ 0x03 /* READ */
+#define CMD_WRDI 0x04 /* Write Disable */
+#define CMD_RDSR 0x05 /* Read Status Register */
+#define CMD_WREN 0x06 /* Write Enable */
+#define CMD_RDCR 0x15 /* Read configuration register (Macronix) */
+#define CMD_SE 0x20 /* Sector (4K) Erase */
+#define CMD_RDSCUR 0x2b /* Read Security Register (Macronix) */
+#define CMD_BE32K 0x52 /* Block (32K) Erase */
+#define CMD_RDSFDP 0x5a /* Read SFDP JEDEC info */
+#define CMD_CE 0x60 /* Chip Erase (Macronix/Winbond) */
+#define CMD_MIC_WREVCONF 0x61 /* Micron Write Enhanced Volatile Config */
+#define CMD_MIC_RDEVCONF 0x65 /* Micron Read Enhanced Volatile Config */
+#define CMD_MIC_RDFLST 0x70 /* Micron Read Flag Status */
+#define CMD_MIC_WRVCONF 0x81 /* Micron Write Volatile Config */
+#define CMD_MIC_RDVCONF 0x85 /* Micron Read Volatile Config */
+#define CMD_RDID 0x9f /* Read JEDEC ID */
+#define CMD_EN4B 0xb7 /* Enable 4B addresses */
+#define CMD_MIC_BULK_ERASE 0xc7 /* Micron Bulk Erase */
+#define CMD_BE 0xd8 /* Block (64K) Erase */
+#define CMD_RDDPB 0xe0 /* Read dynamic protection (Macronix) */
+#define CMD_RDSPB 0xe2 /* Read static protection (Macronix) */
+#define CMD_EX4B 0xe9 /* Exit 4B addresses */
/* Flash status bits */
#define STAT_WIP 0x01
@@ -49,7 +58,9 @@ struct flash_info {
#define FL_ERASE_4K 0x00000001 /* Supports 4k erase */
#define FL_ERASE_32K 0x00000002 /* Supports 32k erase */
#define FL_ERASE_64K 0x00000004 /* Supports 64k erase */
-#define FL_ERASE_CHIP 0x00000008 /* Supports 64k erase */
+#define FL_ERASE_CHIP 0x00000008 /* Supports 0x60 cmd chip erase */
+#define FL_ERASE_BULK 0x00000010 /* Supports 0xc7 cmd bulk erase */
+#define FL_MICRON_BUGS 0x00000020 /* Various micron bug workarounds */
#define FL_ERASE_ALL (FL_ERASE_4K | FL_ERASE_32K | FL_ERASE_64K | \
FL_ERASE_CHIP)
#define FL_CAN_4B 0x00000010 /* Supports 4b mode */
@@ -76,7 +87,7 @@ struct spi_flash_ctrl {
* **************************************************/
/*
- * - setup(ctrl, info, tsize)
+ * - setup(ctrl, tsize)
*
* Provides the controller with an option to configure itself
* based on the specific flash type. It can also override some
@@ -84,8 +95,7 @@ struct spi_flash_ctrl {
* which can be needed for high level controllers. It can also
* override the total flash size.
*/
- int (*setup)(struct spi_flash_ctrl *ctrl, struct flash_info *info,
- uint32_t *tsize);
+ int (*setup)(struct spi_flash_ctrl *ctrl, uint32_t *tsize);
/*
* - set_4b(ctrl, enable)
@@ -208,6 +218,14 @@ struct spi_flash_ctrl {
int (*cmd_wr)(struct spi_flash_ctrl *ctrl, uint8_t cmd,
bool has_addr, uint32_t addr, const void *buffer,
uint32_t size);
+
+ /* The core will establish this at init, after chip ID has
+ * been probed
+ */
+ struct flash_info *finfo;
};
+extern int fl_wren(struct spi_flash_ctrl *ct);
+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 a3e6ff2..f438efe 100644
--- a/libflash/libflash.c
+++ b/libflash/libflash.c
@@ -21,10 +21,13 @@
#include "libflash-priv.h"
static const struct flash_info flash_info[] = {
- { 0xc22019, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "MXxxL25635F"},
- { 0xc2201a, 0x04000000, FL_ERASE_ALL | FL_CAN_4B, "MXxxL51235F"},
- { 0xef4018, 0x01000000, FL_ERASE_ALL, "W25Q128BV" },
- { 0x55aa55, 0x00100000, FL_ERASE_ALL | FL_CAN_4B, "TEST_FLASH"},
+ { 0xc22019, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL25635F"},
+ { 0xc2201a, 0x04000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL51235F"},
+ { 0xef4018, 0x01000000, FL_ERASE_ALL, "Winbond W25Q128BV" },
+ { 0x20ba20, 0x04000000, FL_ERASE_4K | FL_ERASE_64K | FL_CAN_4B |
+ FL_ERASE_BULK | FL_MICRON_BUGS,
+ "Micron N25Qx512Ax" },
+ { 0x55aa55, 0x00100000, FL_ERASE_ALL | FL_CAN_4B, "TEST_FLASH" },
};
struct flash_chip {
@@ -37,24 +40,22 @@ struct flash_chip {
void *smart_buf; /* Buffer for smart writes */
};
-static int fl_read_stat(struct flash_chip *c, uint8_t *stat)
+static int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat)
{
- struct spi_flash_ctrl *ct = c->ctrl;
-
return ct->cmd_rd(ct, CMD_RDSR, false, 0, stat, 1);
}
-static int fl_wren(struct flash_chip *c)
+/* Exported for internal use */
+int fl_wren(struct spi_flash_ctrl *ct)
{
- struct spi_flash_ctrl *ct = c->ctrl;
uint8_t stat;
int i, rc;
/* Some flashes need it to be hammered */
- for (i = 0; i < 10; i++) {
+ 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(c, &stat);
+ rc = fl_read_stat(ct, &stat);
if (rc) return rc;
if (stat & STAT_WEN)
return 0;
@@ -62,18 +63,33 @@ static int fl_wren(struct flash_chip *c)
return FLASH_ERR_WREN_TIMEOUT;
}
+static void fl_micron_status(struct spi_flash_ctrl *ct)
+{
+ uint8_t flst;
+
+ /*
+ * After a success status on a write or erase, we
+ * need to do that command or some chip variants will
+ * lock
+ */
+ ct->cmd_rd(ct, CMD_MIC_RDFLST, false, 0, &flst, 1);
+}
+
/* Synchronous write completion, probably need a yield hook */
-static int fl_sync_wait_idle(struct flash_chip *c)
+int fl_sync_wait_idle(struct spi_flash_ctrl *ct)
{
- int rc;
uint8_t stat;
+ int rc;
/* XXX Add timeout */
for (;;) {
- rc = fl_read_stat(c, &stat);
+ rc = fl_read_stat(ct, &stat);
if (rc) return rc;
- if (!(stat & STAT_WIP))
+ if (!(stat & STAT_WIP)) {
+ if (ct->finfo->flags & FL_MICRON_BUGS)
+ fl_micron_status(ct);
return 0;
+ }
}
/* return FLASH_ERR_WIP_TIMEOUT; */
}
@@ -157,7 +173,7 @@ int flash_erase(struct flash_chip *c, uint32_t dst, uint32_t size)
fl_get_best_erase(c, dst, size, &chunk, &cmd);
/* Poke write enable */
- rc = fl_wren(c);
+ rc = fl_wren(ct);
if (rc)
return rc;
@@ -167,7 +183,7 @@ int flash_erase(struct flash_chip *c, uint32_t dst, uint32_t size)
return rc;
/* Wait for write complete */
- rc = fl_sync_wait_idle(c);
+ rc = fl_sync_wait_idle(ct);
if (rc)
return rc;
@@ -183,7 +199,7 @@ int flash_erase_chip(struct flash_chip *c)
int rc;
/* XXX TODO: Fallback to using normal erases */
- if (!(c->info.flags & FL_ERASE_CHIP))
+ if (!(c->info.flags & (FL_ERASE_CHIP|FL_ERASE_BULK)))
return FLASH_ERR_CHIP_ER_NOT_SUPPORTED;
FL_DBG("LIBFLASH: Erasing chip...\n");
@@ -192,15 +208,18 @@ int flash_erase_chip(struct flash_chip *c)
if (ct->erase)
return ct->erase(ct, 0, 0xffffffff);
- rc = fl_wren(c);
+ rc = fl_wren(ct);
if (rc) return rc;
- rc = ct->cmd_wr(ct, CMD_CE, false, 0, NULL, 0);
+ if (c->info.flags & FL_ERASE_CHIP)
+ rc = ct->cmd_wr(ct, CMD_CE, false, 0, NULL, 0);
+ else
+ rc = ct->cmd_wr(ct, CMD_MIC_BULK_ERASE, false, 0, NULL, 0);
if (rc)
return rc;
/* Wait for write complete */
- return fl_sync_wait_idle(c);
+ return fl_sync_wait_idle(ct);
}
static int fl_wpage(struct flash_chip *c, uint32_t dst, const void *src,
@@ -212,7 +231,7 @@ static int fl_wpage(struct flash_chip *c, uint32_t dst, const void *src,
if (size < 1 || size > 0x100)
return FLASH_ERR_BAD_PAGE_SIZE;
- rc = fl_wren(c);
+ rc = fl_wren(ct);
if (rc) return rc;
rc = ct->cmd_wr(ct, CMD_PP, true, dst, src, size);
@@ -220,7 +239,7 @@ static int fl_wpage(struct flash_chip *c, uint32_t dst, const void *src,
return rc;
/* Wait for write complete */
- return fl_sync_wait_idle(c);
+ return fl_sync_wait_idle(ct);
}
int flash_write(struct flash_chip *c, uint32_t dst, const void *src,
@@ -406,7 +425,6 @@ int flash_smart_write(struct flash_chip *c, uint32_t dst, const void *src,
return 0;
}
-
static int flash_identify(struct flash_chip *c)
{
struct spi_flash_ctrl *ct = c->ctrl;
@@ -446,18 +464,19 @@ static int flash_identify(struct flash_chip *c)
if (info->id == iid)
break;
}
- if (info->id != iid)
+ if (!info || info->id != iid)
return FLASH_ERR_CHIP_UNKNOWN;
c->info = *info;
c->tsize = info->size;
+ ct->finfo = &c->info;
/*
* Let controller know about our settings and possibly
* override them
*/
if (ct->setup) {
- rc = ct->setup(ct, &c->info, &c->tsize);
+ rc = ct->setup(ct, &c->tsize);
if (rc)
return rc;
}
@@ -485,6 +504,10 @@ static int flash_set_4b(struct flash_chip *c, bool enable)
{
struct spi_flash_ctrl *ct = c->ctrl;
+ /* Some flash chips want this */
+ fl_wren(ct);
+ /* Ignore error in case chip is write protected */
+
return ct->cmd_wr(ct, enable ? CMD_EN4B : CMD_EX4B, false, 0, NULL, 0);
}