From 8463764f7e4383e1e2339eee174d4151337bc467 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 15 Oct 2014 13:44:23 +1100 Subject: libflash: Sync with pflash 0.8.3 Fixes 64MB chip support, improve Macronix settings, add Micron chip support, etc... Signed-off-by: Benjamin Herrenschmidt --- hw/ast-bmc/ast-sf-ctrl.c | 552 ++++++++++++++++++++++++++++++++++++++++------- hw/sfc-ctrl.c | 4 +- include/ast.h | 6 + libflash/libflash-priv.h | 56 +++-- libflash/libflash.c | 75 ++++--- 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 /* 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); } -- cgit v1.1