| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| /* Copyright 2013-2018 IBM Corp. */ |
| |
| #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> |
| #ifdef __SKIBOOT__ |
| #include "lpc.h" |
| #endif |
| |
| #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 */ |
| }; |
| |
| #ifdef __SKIBOOT__ |
| #define PNOR_AHB_ADDR 0x30000000 |
| static uint32_t pnor_lpc_offset; |
| |
| static int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len) |
| { |
| /* Check we don't cross IDSEL segments */ |
| if ((reg ^ (reg + len - 1)) >> 28) |
| return -EINVAL; |
| |
| /* SPI flash, use LPC->AHB bridge */ |
| if ((reg >> 28) == (PNOR_AHB_ADDR >> 28)) { |
| uint32_t chunk, off = reg - PNOR_AHB_ADDR + pnor_lpc_offset; |
| int64_t rc; |
| |
| while(len) { |
| /* Chose access size */ |
| if (len > 3 && !(off & 3)) { |
| /* endian swap: see ast_copy_from_ahb */ |
| uint32_t dat = be32_to_cpu(*(__be32 *)src); |
| |
| rc = lpc_write(OPAL_LPC_FW, off, |
| dat, 4); |
| chunk = 4; |
| } else { |
| rc = lpc_write(OPAL_LPC_FW, off, |
| *(uint8_t *)src, 1); |
| chunk = 1; |
| } |
| if (rc) { |
| prerror("AST_IO: lpc_write.sb failure %lld" |
| " to FW 0x%08x\n", rc, off); |
| return rc; |
| } |
| len -= chunk; |
| off += chunk; |
| src += chunk; |
| } |
| return 0; |
| } |
| |
| /* Otherwise we don't do byte access (... yet) */ |
| prerror("AST_IO: Attempted write bytes access to %08x\n", reg); |
| return -EINVAL; |
| } |
| |
| static int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len) |
| { |
| /* Check we don't cross IDSEL segments */ |
| if ((reg ^ (reg + len - 1)) >> 28) |
| return -EINVAL; |
| |
| /* SPI flash, use LPC->AHB bridge */ |
| if ((reg >> 28) == (PNOR_AHB_ADDR >> 28)) { |
| uint32_t chunk, off = reg - PNOR_AHB_ADDR + pnor_lpc_offset; |
| int64_t rc; |
| |
| while(len) { |
| uint32_t dat; |
| |
| /* Chose access size */ |
| if (len > 3 && !(off & 3)) { |
| rc = lpc_read(OPAL_LPC_FW, off, &dat, 4); |
| if (!rc) { |
| /* |
| * lpc_read swaps to CPU endian but it's not |
| * really a 32-bit value, so convert back. |
| */ |
| *(__be32 *)dst = cpu_to_be32(dat); |
| } |
| chunk = 4; |
| } else { |
| rc = lpc_read(OPAL_LPC_FW, off, &dat, 1); |
| if (!rc) |
| *(uint8_t *)dst = dat; |
| chunk = 1; |
| } |
| if (rc) { |
| prerror("AST_IO: lpc_read.sb failure %lld" |
| " to FW 0x%08x\n", rc, off); |
| return rc; |
| } |
| len -= chunk; |
| off += chunk; |
| dst += chunk; |
| } |
| return 0; |
| } |
| /* Otherwise we don't do byte access (... yet) */ |
| prerror("AST_IO: Attempted read bytes access to %08x\n", reg); |
| return -EINVAL; |
| } |
| #endif /* __SKIBOOT__ */ |
| |
| 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; |
| beint32_t tmp; |
| |
| /* Layout address MSB first in memory */ |
| tmp = cpu_to_be32(addr); |
| |
| /* Send the right amount of bytes */ |
| ap = (char *)&tmp; |
| |
| 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); |
| else 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; |
| 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 && ct->type == AST_SF_TYPE_BMC) |
| 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 */ |
| case 0xc2201b: /* MX66L1G45G */ |
| 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; |
| #ifdef __SKIBOOT__ |
| uint32_t hicr7; |
| |
| if (!ast_sio_is_enabled()) |
| return -ENODEV; |
| #endif /* __SKIBOOT__ */ |
| |
| 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; |
| } |
| |
| #ifdef __SKIBOOT__ |
| /* Read the configuration of the LPC->AHB bridge for PNOR |
| * to extract the PNOR LPC offset which can be different |
| * depending on flash size |
| */ |
| hicr7 = ast_ahb_readl(LPC_HICR7); |
| pnor_lpc_offset = (hicr7 & 0xffffu) << 16; |
| prlog(PR_DEBUG, "AST: PNOR LPC offset: 0x%08x\n", pnor_lpc_offset); |
| #endif /* __SKIBOOT__ */ |
| |
| *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); |
| } |