add bmc flash capability
diff --git a/objects/pflash/ast-sf-ctrl.c b/objects/pflash/ast-sf-ctrl.c
index b154682..bf5d372 100644
--- a/objects/pflash/ast-sf-ctrl.c
+++ b/objects/pflash/ast-sf-ctrl.c
@@ -1,830 +1,923 @@
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <libflash/libflash.h>
-#include <libflash/libflash-priv.h>
-
-#include "ast.h"
-
-#ifndef __unused
-#define __unused __attribute__((unused))
-#endif
-
-#define CALIBRATE_BUF_SIZE 16384
-
-struct ast_sf_ctrl {
- /* We have 2 controllers, one for the BMC flash, one for the PNOR */
- uint8_t type;
-
- /* Address and previous value of the ctrl register */
- uint32_t ctl_reg;
-
- /* Control register value for normal commands */
- uint32_t ctl_val;
-
- /* 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;
-
- /* Current 4b mode */
- bool mode_4b;
-
- /* Callbacks */
- 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 */
- ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
-
- /* user mode, CE# active */
- ast_ahb_writel(ct->ctl_val | 3, ct->ctl_reg);
-
- /* write cmd */
- return ast_copy_to_ahb(ct->flash, &cmd, 1);
-}
-
-static void ast_sf_end_cmd(struct ast_sf_ctrl *ct)
-{
- /* clear CE# */
- ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
-
- /* Switch back to read mode */
- ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
-}
-
-static int ast_sf_send_addr(struct ast_sf_ctrl *ct, uint32_t addr)
-{
- const void *ap;
-
- /* Layout address MSB first in memory */
- addr = cpu_to_be32(addr);
-
- /* Send the right amount of bytes */
- ap = (char *)&addr;
-
- if (ct->mode_4b)
- return ast_copy_to_ahb(ct->flash, ap, 4);
- else
- return ast_copy_to_ahb(ct->flash, ap + 1, 3);
-}
-
-static int ast_sf_cmd_rd(struct spi_flash_ctrl *ctrl, uint8_t cmd,
- bool has_addr, uint32_t addr, void *buffer,
- uint32_t size)
-{
- struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
- int rc;
-
- rc = ast_sf_start_cmd(ct, cmd);
- if (rc)
- goto bail;
- if (has_addr) {
- rc = ast_sf_send_addr(ct, addr);
- if (rc)
- goto bail;
- }
- if (buffer && size)
- rc = ast_copy_from_ahb(buffer, ct->flash, size);
- bail:
- ast_sf_end_cmd(ct);
- return rc;
-}
-
-static int ast_sf_cmd_wr(struct spi_flash_ctrl *ctrl, uint8_t cmd,
- bool has_addr, uint32_t addr, const void *buffer,
- uint32_t size)
-{
- struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
- int rc;
-
- rc = ast_sf_start_cmd(ct, cmd);
- if (rc)
- goto bail;
- if (has_addr) {
- rc = ast_sf_send_addr(ct, addr);
- if (rc)
- goto bail;
- }
- if (buffer && size)
- rc = ast_copy_to_ahb(ct->flash, buffer, size);
- bail:
- ast_sf_end_cmd(ct);
- return rc;
-}
-
-static int ast_sf_set_4b(struct spi_flash_ctrl *ctrl, bool enable)
-{
- struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
-
- if (ct->type != AST_SF_TYPE_PNOR)
- return enable ? FLASH_ERR_4B_NOT_SUPPORTED : 0;
-
- /*
- * We update the "old" value as well since when quitting
- * we don't restore the mode of the flash itself so we need
- * to leave the controller in a compatible setup
- */
- if (enable) {
- ct->ctl_val |= 0x2000;
- ct->ctl_read_val |= 0x2000;
- } else {
- ct->ctl_val &= ~0x2000;
- ct->ctl_read_val &= ~0x2000;
- }
- ct->mode_4b = enable;
-
- /* Update read mode */
- ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
-
- return 0;
-}
-
-static int ast_sf_read(struct spi_flash_ctrl *ctrl, uint32_t pos,
- void *buf, uint32_t len)
-{
- struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
-
- /*
- * We are in read mode by default. We don't yet support fancy
- * things like fast read or X2 mode
- */
- return ast_copy_from_ahb(buf, ct->flash + pos, len);
-}
-
-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 *golden_buf, uint8_t *test_buf)
-{
- int i, rc;
-
- for (i = 0; i < 10; i++) {
- rc = ast_copy_from_ahb(test_buf, ct->flash, CALIBRATE_BUF_SIZE);
- if (rc)
- return rc;
- 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 *golden_buf, 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, golden_buf, 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 bool ast_calib_data_usable(const uint8_t *test_buf, uint32_t 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,
- 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 */
- (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(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;
-
- /* 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, golden_buf, test_buf);
-
- /* Some other error occurred, bail out */
- 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)
- 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;
-
- /* 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;
- *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];
-
- /*
- * 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, info, 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);
- 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;
-
- 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, info, 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);
- 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;
- }
- 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);
-
- /*
- * 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, info, 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);
- 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;
-
- /*
- * Configure better timings and read mode for known
- * flash chips
- */
- switch(info->id) {
- case 0xc22019: /* MX25L25635F */
- case 0xc2201a: /* MX66L51235F */
- return ast_sf_setup_macronix(ct, info);
- case 0xef4018: /* W25Q128BV */
- return ast_sf_setup_winbond(ct, info);
- case 0x20ba20: /* MT25Qx512xx */
- return ast_sf_setup_micron(ct, info);
- }
- /* No special tuning */
- return 0;
-}
-
-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 */
- reg = ast_ahb_readl(PNOR_SPI_FCTL_CONF);
- if (reg == 0xffffffff) {
- FL_ERR("AST_SF: Failed read from controller config\n");
- return false;
- }
- ast_ahb_writel(reg | 1, PNOR_SPI_FCTL_CONF);
-
- /*
- * Snapshot control reg and sanitize it for our
- * use, switching to 1-bit mode, clearing user
- * mode if set, etc...
- *
- * Also configure SPI clock to something safe
- * like HCLK/8 (24Mhz)
- */
- ct->ctl_val = ast_ahb_readl(ct->ctl_reg);
- if (ct->ctl_val == 0xffffffff) {
- FL_ERR("AST_SF: Failed read from controller control\n");
- return false;
- }
-
- ct->ctl_val = (ct->ctl_val & 0x2000) |
- (0x00 << 28) | /* Single bit */
- (0x00 << 24) | /* CE# width 16T */
- (0x00 << 16) | /* no command */
- (0x04 << 8) | /* HCLK/8 */
- (0x00 << 6) | /* no dummy cycle */
- (0x00); /* normal read */
-
- /* 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;
- else
- ct->mode_4b = false;
-
- return true;
-}
-
-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;
-
- /*
- * Snapshot control reg and sanitize it for our
- * use, switching to 1-bit mode, clearing user
- * mode if set, etc...
- *
- * Also configure SPI clock to something safe
- * like HCLK/8 (24Mhz)
- */
- ct->ctl_val =
- (0x00 << 28) | /* Single bit */
- (0x00 << 24) | /* CE# width 16T */
- (0x00 << 16) | /* no command */
- (0x04 << 8) | /* HCLK/8 */
- (0x00 << 6) | /* no dummy cycle */
- (0x00); /* normal read */
-
- /* 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;
-
- return true;
-}
-
-int ast_sf_open(uint8_t type, struct spi_flash_ctrl **ctrl)
-{
- struct ast_sf_ctrl *ct;
-
- if (type != AST_SF_TYPE_PNOR && type != AST_SF_TYPE_BMC)
- return -EINVAL;
-
- *ctrl = NULL;
- ct = malloc(sizeof(*ct));
- if (!ct) {
- FL_ERR("AST_SF: Failed to allocate\n");
- return -ENOMEM;
- }
- memset(ct, 0, sizeof(*ct));
- ct->type = type;
- ct->ops.cmd_wr = ast_sf_cmd_wr;
- ct->ops.cmd_rd = ast_sf_cmd_rd;
- ct->ops.set_4b = ast_sf_set_4b;
- 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;
- } else {
- if (!ast_sf_init_bmc(ct))
- goto fail;
- }
-
- *ctrl = &ct->ops;
-
- return 0;
- fail:
- free(ct);
- return -EIO;
-}
-
-void ast_sf_close(struct spi_flash_ctrl *ctrl)
-{
- struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
-
- /* Restore control reg to read */
- ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
-
- /* Additional cleanup */
- if (ct->type == AST_SF_TYPE_PNOR) {
- uint32_t reg = ast_ahb_readl(PNOR_SPI_FCTL_CONF);
- if (reg != 0xffffffff)
- ast_ahb_writel(reg & ~1, PNOR_SPI_FCTL_CONF);
- }
-
- /* Free the whole lot */
- free(ct);
-}
-
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libflash-priv.h>
+
+#include "ast.h"
+
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+
+#define CALIBRATE_BUF_SIZE 16384
+
+struct ast_sf_ctrl {
+ /* We have 2 controllers, one for the BMC flash, one for the PNOR */
+ uint8_t type;
+
+ /* Address and previous value of the ctrl register */
+ uint32_t ctl_reg;
+
+ /* Control register value for normal commands */
+ uint32_t ctl_val;
+
+ /* 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;
+
+ /* Current 4b mode */
+ bool mode_4b;
+
+ /* Callbacks */
+ 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 */
+ ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
+
+ /* user mode, CE# active */
+ ast_ahb_writel(ct->ctl_val | 3, ct->ctl_reg);
+
+ /* write cmd */
+ return ast_copy_to_ahb(ct->flash, &cmd, 1);
+}
+
+static void ast_sf_end_cmd(struct ast_sf_ctrl *ct)
+{
+ /* clear CE# */
+ ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
+
+ /* Switch back to read mode */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+}
+
+static int ast_sf_send_addr(struct ast_sf_ctrl *ct, uint32_t addr)
+{
+ const void *ap;
+
+ /* Layout address MSB first in memory */
+ addr = cpu_to_be32(addr);
+
+ /* Send the right amount of bytes */
+ ap = (char *)&addr;
+
+ if (ct->mode_4b)
+ return ast_copy_to_ahb(ct->flash, ap, 4);
+ else
+ return ast_copy_to_ahb(ct->flash, ap + 1, 3);
+}
+
+static int ast_sf_cmd_rd(struct spi_flash_ctrl *ctrl, uint8_t cmd,
+ bool has_addr, uint32_t addr, void *buffer,
+ uint32_t size)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ int rc;
+
+ rc = ast_sf_start_cmd(ct, cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = ast_sf_send_addr(ct, addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = ast_copy_from_ahb(buffer, ct->flash, size);
+ bail:
+ ast_sf_end_cmd(ct);
+ return rc;
+}
+
+static int ast_sf_cmd_wr(struct spi_flash_ctrl *ctrl, uint8_t cmd,
+ bool has_addr, uint32_t addr, const void *buffer,
+ uint32_t size)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ int rc;
+
+ rc = ast_sf_start_cmd(ct, cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = ast_sf_send_addr(ct, addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = ast_copy_to_ahb(ct->flash, buffer, size);
+ bail:
+ ast_sf_end_cmd(ct);
+ return rc;
+}
+
+static int ast_sf_set_4b(struct spi_flash_ctrl *ctrl, bool enable)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ uint32_t ce_ctrl = 0;
+
+ if (ct->type == AST_SF_TYPE_BMC && ct->ops.finfo->size > 0x1000000)
+ ce_ctrl = ast_ahb_readl(BMC_SPI_FCTL_CE_CTRL);
+
+ /*
+ * We update the "old" value as well since when quitting
+ * we don't restore the mode of the flash itself so we need
+ * to leave the controller in a compatible setup
+ */
+ if (enable) {
+ ct->ctl_val |= 0x2000;
+ ct->ctl_read_val |= 0x2000;
+ ce_ctrl |= 0x1;
+ } else {
+ ct->ctl_val &= ~0x2000;
+ ct->ctl_read_val &= ~0x2000;
+ ce_ctrl &= ~0x1;
+ }
+ ct->mode_4b = enable;
+
+ /* Update read mode */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ if (ce_ctrl)
+ ast_ahb_writel(ce_ctrl, BMC_SPI_FCTL_CE_CTRL);
+
+ return 0;
+}
+
+static int ast_sf_read(struct spi_flash_ctrl *ctrl, uint32_t pos,
+ void *buf, uint32_t len)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+
+ /*
+ * We are in read mode by default. We don't yet support fancy
+ * things like fast read or X2 mode
+ */
+ return ast_copy_from_ahb(buf, ct->flash + pos, len);
+}
+
+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 *golden_buf, uint8_t *test_buf)
+{
+ int i, rc;
+
+ for (i = 0; i < 10; i++) {
+ rc = ast_copy_from_ahb(test_buf, ct->flash, CALIBRATE_BUF_SIZE);
+ if (rc)
+ return rc;
+ 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 *golden_buf, 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, golden_buf, 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 bool ast_calib_data_usable(const uint8_t *test_buf, uint32_t 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 */
+ (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(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;
+
+ /* 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, golden_buf, test_buf);
+
+ /* Some other error occurred, bail out */
+ 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)
+ 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;
+
+ /* 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;
+ *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 __unused;
+ uint8_t srcr[2];
+
+ /*
+ * 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, info, 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);
+ 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 __unused;
+
+ 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, info, 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);
+ 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 __unused;
+
+ 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;
+ }
+ 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);
+
+ /*
+ * 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, info, 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);
+ 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;
+
+ /*
+ * Configure better timings and read mode for known
+ * flash chips
+ */
+ switch(info->id) {
+ case 0xc22018: /* MX25L12835F */
+ case 0xc22019: /* MX25L25635F */
+ case 0xc2201a: /* MX66L51235F */
+ return ast_sf_setup_macronix(ct, info);
+ case 0xef4018: /* W25Q128BV */
+ return ast_sf_setup_winbond(ct, info);
+ case 0x20ba20: /* MT25Qx512xx */
+ return ast_sf_setup_micron(ct, info);
+ }
+ /* No special tuning */
+ return 0;
+}
+
+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 */
+ reg = ast_ahb_readl(PNOR_SPI_FCTL_CONF);
+ if (reg == 0xffffffff) {
+ FL_ERR("AST_SF: Failed read from controller config\n");
+ return false;
+ }
+ ast_ahb_writel(reg | 1, PNOR_SPI_FCTL_CONF);
+
+ /*
+ * Snapshot control reg and sanitize it for our
+ * use, switching to 1-bit mode, clearing user
+ * mode if set, etc...
+ *
+ * Also configure SPI clock to something safe
+ * like HCLK/8 (24Mhz)
+ */
+ ct->ctl_val = ast_ahb_readl(ct->ctl_reg);
+ if (ct->ctl_val == 0xffffffff) {
+ FL_ERR("AST_SF: Failed read from controller control\n");
+ return false;
+ }
+
+ ct->ctl_val = (ct->ctl_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# width 16T */
+ (0x00 << 16) | /* no command */
+ (0x04 << 8) | /* HCLK/8 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* normal read */
+
+ /* 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;
+ else
+ ct->mode_4b = false;
+
+ return true;
+}
+
+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;
+
+ /*
+ * Snapshot control reg and sanitize it for our
+ * use, switching to 1-bit mode, clearing user
+ * mode if set, etc...
+ *
+ * Also configure SPI clock to something safe
+ * like HCLK/8 (24Mhz)
+ */
+ ct->ctl_val =
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# width 16T */
+ (0x00 << 16) | /* no command */
+ (0x04 << 8) | /* HCLK/8 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* normal read */
+
+ /* 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;
+
+ return true;
+}
+
+static int ast_mem_set4b(struct spi_flash_ctrl *ctrl __unused,
+ bool enable __unused)
+{
+ return 0;
+}
+
+static int ast_mem_setup(struct spi_flash_ctrl *ctrl __unused,
+ uint32_t *tsize __unused)
+{
+ return 0;
+}
+
+static int ast_mem_chipid(struct spi_flash_ctrl *ctrl __unused, uint8_t *id_buf,
+ uint32_t *id_size)
+{
+ if (*id_size < 3)
+ return -1;
+
+ id_buf[0] = 0xaa;
+ id_buf[1] = 0x55;
+ id_buf[2] = 0xaa;
+ *id_size = 3;
+ return 0;
+}
+
+static int ast_mem_write(struct spi_flash_ctrl *ctrl, uint32_t pos,
+ const void *buf, uint32_t len)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+
+ /*
+ * This only works when the ahb is pointed at system memory.
+ */
+ return ast_copy_to_ahb(ct->flash + pos, buf, len);
+}
+
+static int ast_mem_erase(struct spi_flash_ctrl *ctrl, uint32_t addr, uint32_t size)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ uint32_t pos, len, end = addr + size;
+ uint64_t zero = 0;
+ int ret;
+
+ for (pos = addr; pos < end; pos += sizeof(zero)) {
+ if (pos + sizeof(zero) > end)
+ len = end - pos;
+ else
+ len = sizeof(zero);
+
+ ret = ast_copy_to_ahb(ct->flash + pos, &zero, len);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int ast_sf_open(uint8_t type, struct spi_flash_ctrl **ctrl)
+{
+ struct ast_sf_ctrl *ct;
+
+ if (type != AST_SF_TYPE_PNOR && type != AST_SF_TYPE_BMC
+ && type != AST_SF_TYPE_MEM)
+ return -EINVAL;
+
+ *ctrl = NULL;
+ ct = malloc(sizeof(*ct));
+ if (!ct) {
+ FL_ERR("AST_SF: Failed to allocate\n");
+ return -ENOMEM;
+ }
+ memset(ct, 0, sizeof(*ct));
+ ct->type = type;
+
+ if (type == AST_SF_TYPE_MEM) {
+ ct->ops.cmd_wr = NULL;
+ ct->ops.cmd_rd = NULL;
+ ct->ops.read = ast_sf_read;
+ ct->ops.set_4b = ast_mem_set4b;
+ ct->ops.write = ast_mem_write;
+ ct->ops.erase = ast_mem_erase;
+ ct->ops.setup = ast_mem_setup;
+ ct->ops.chip_id = ast_mem_chipid;
+ ct->flash = PNOR_FLASH_BASE;
+ } else {
+ ct->ops.cmd_wr = ast_sf_cmd_wr;
+ ct->ops.cmd_rd = ast_sf_cmd_rd;
+ ct->ops.set_4b = ast_sf_set_4b;
+ 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;
+ } else if (type == AST_SF_TYPE_BMC) {
+ if (!ast_sf_init_bmc(ct))
+ goto fail;
+ }
+
+ *ctrl = &ct->ops;
+
+ return 0;
+ fail:
+ free(ct);
+ return -EIO;
+}
+
+void ast_sf_close(struct spi_flash_ctrl *ctrl)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+
+ /* Restore control reg to read */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ /* Additional cleanup */
+ if (ct->type == AST_SF_TYPE_PNOR) {
+ uint32_t reg = ast_ahb_readl(PNOR_SPI_FCTL_CONF);
+ if (reg != 0xffffffff)
+ ast_ahb_writel(reg & ~1, PNOR_SPI_FCTL_CONF);
+ }
+
+ /* Free the whole lot */
+ free(ct);
+}