* SPDX-License-Identifier: GPL-2.0+
*/
+#include <asm/arch/clock.h>
+#include <asm/io.h>
#include <common.h>
#include <config.h>
-#include <asm/io.h>
#include <nand.h>
/* registers */
#define NFC_CTL_EN (1 << 0)
#define NFC_CTL_RESET (1 << 1)
#define NFC_CTL_RAM_METHOD (1 << 14)
+#define NFC_CTL_PAGE_SIZE_MASK (0xf << 8)
+#define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8)
#define NFC_ECC_EN (1 << 0)
{
uint32_t val;
+ board_nand_init();
+
val = readl(SUNXI_NFC_BASE + NFC_CTL);
/* enable and reset CTL */
writel(val | NFC_CTL_EN | NFC_CTL_RESET,
NFC_CTL_RESET, MAX_RETRIES)) {
printf("Couldn't initialize nand\n");
}
+
+ /* reset NAND */
+ writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET,
+ SUNXI_NFC_BASE + NFC_CMD);
+
+ if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_CMD_INT_FLAG,
+ MAX_RETRIES)) {
+ printf("Error timeout waiting for nand reset\n");
+ return;
+ }
}
static void nand_read_page(unsigned int real_addr, dma_addr_t dst,
int syndrome, uint32_t *ecc_errors)
{
uint32_t val;
- int ecc_off = 0;
+ int i, ecc_off = 0;
uint16_t ecc_mode = 0;
uint16_t rand_seed;
uint32_t page;
uint16_t column;
uint32_t oob_offset;
+ static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
- switch (CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH) {
- case 16:
- ecc_mode = 0;
- ecc_off = 0x20;
- break;
- case 24:
- ecc_mode = 1;
- ecc_off = 0x2e;
- break;
- case 28:
- ecc_mode = 2;
- ecc_off = 0x32;
- break;
- case 32:
- ecc_mode = 3;
- ecc_off = 0x3c;
- break;
- case 40:
- ecc_mode = 4;
- ecc_off = 0x4a;
- break;
- case 48:
- ecc_mode = 4;
- ecc_off = 0x52;
- break;
- case 56:
- ecc_mode = 4;
- ecc_off = 0x60;
- break;
- case 60:
- ecc_mode = 4;
- ecc_off = 0x0;
- break;
- case 64:
- ecc_mode = 4;
- ecc_off = 0x0;
- break;
- default:
- ecc_mode = 0;
- ecc_off = 0;
- }
-
- if (ecc_off == 0) {
- printf("Unsupported ECC strength (%d)!\n",
- CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH);
- return;
+ for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+ if (CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH == strengths[i]) {
+ ecc_mode = i;
+ break;
+ }
}
- /* set CMD */
- writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET,
- SUNXI_NFC_BASE + NFC_CMD);
-
- if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_CMD_INT_FLAG,
- MAX_RETRIES)) {
- printf("Error while initilizing command interrupt\n");
- return;
- }
+ /* HW ECC always request ECC bytes for 1024 bytes blocks */
+ ecc_off = DIV_ROUND_UP(CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH * fls(8 * 1024), 8);
+ /* HW ECC always work with even numbers of ECC bytes */
+ ecc_off += (ecc_off & 1);
+ ecc_off += 4; /* prepad */
page = real_addr / CONFIG_NAND_SUNXI_SPL_PAGE_SIZE;
column = real_addr % CONFIG_NAND_SUNXI_SPL_PAGE_SIZE;
val = readl(SUNXI_NFC_BASE + NFC_CTL);
writel(val | NFC_CTL_RAM_METHOD, SUNXI_NFC_BASE + NFC_CTL);
- if (syndrome) {
- writel(CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE,
- SUNXI_NFC_BASE + NFC_SPARE_AREA);
- } else {
+ if (!syndrome) {
oob_offset = CONFIG_NAND_SUNXI_SPL_PAGE_SIZE
+ (column / CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE)
* ecc_off;
writel(oob_offset, SUNXI_NFC_BASE + NFC_SPARE_AREA);
}
+ flush_dcache_range(dst,
+ ALIGN(dst + CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE,
+ ARCH_DMA_MINALIGN));
+
/* SUNXI_DMA */
writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */
/* read from REG_IO_DATA */
return;
}
+ invalidate_dcache_range(dst,
+ ALIGN(dst + CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE,
+ ARCH_DMA_MINALIGN));
+
if (readl(SUNXI_NFC_BASE + NFC_ECC_ST))
(*ecc_errors)++;
}
void *current_dest;
uint32_t ecc_errors = 0;
+ clrsetbits_le32(SUNXI_NFC_BASE + NFC_CTL, NFC_CTL_PAGE_SIZE_MASK,
+ NFC_CTL_PAGE_SIZE(CONFIG_NAND_SUNXI_SPL_PAGE_SIZE));
+
for (current_dest = dest;
current_dest < (dest + size);
current_dest += CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE) {
return ecc_errors ? -1 : 0;
}
-void nand_deselect(void) {}
+void nand_deselect(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+ clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
+#ifdef CONFIG_MACH_SUN9I
+ clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
+#else
+ clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
+#endif
+ clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
+}