@@ -393,6 +393,9 @@ static void nand_clock_setup(void)
#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I || \
defined CONFIG_MACH_SUN9I || defined CONFIG_MACH_SUN50I
setbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_GATE_OFFSET_NAND0));
+#endif
+#if defined(CONFIG_MACH_SUN5I) && defined(CONFIG_NAND_SUNXI)
+ setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
#endif
setbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
}
@@ -12,6 +12,7 @@
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/delay.h>
+#include <cpu_func.h>
#include <linux/mtd/rawnand.h>
/* registers */
@@ -85,6 +86,22 @@
#define NFC_CMD_RNDOUT 0x05
#define NFC_CMD_READSTART 0x30
+#define SUNXI_DMA_CFG_REG0 0x300
+#define SUNXI_DMA_SRC_START_ADDR_REG0 0x304
+#define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308
+#define SUNXI_DMA_DDMA_BC_REG0 0x30C
+#define SUNXI_DMA_DDMA_PARA_REG0 0x318
+
+#define SUNXI_DMA_DDMA_CFG_REG_LOADING (1 << 31)
+#define SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25)
+#define SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM (1 << 16)
+#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9)
+#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5)
+#define SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0)
+
+#define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0)
+#define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8)
+
struct nfc_config {
int page_size;
int ecc_strength;
@@ -254,6 +271,93 @@ static int nand_change_column(u16 column)
static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116};
+#if defined(CONFIG_MACH_SUN5I)
+static int nand_read_page(const struct nfc_config *conf, u32 offs,
+ void *dest, int len)
+{
+ dma_addr_t dst = (dma_addr_t)dest;
+ int nsectors = len / conf->ecc_size;
+ u16 rand_seed = 0;
+ u32 val;
+ int page;
+
+ page = offs / conf->page_size;
+
+ if (offs % conf->page_size || len % conf->ecc_size ||
+ len > conf->page_size || len < 0)
+ return -EINVAL;
+
+ /* clear ecc status */
+ writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
+
+ /* Choose correct seed if randomized */
+ if (conf->randomize)
+ rand_seed = random_seed[page % conf->nseeds];
+
+ writel((rand_seed << 16) | (conf->ecc_strength << 12) |
+ (conf->randomize ? NFC_ECC_RANDOM_EN : 0) |
+ (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) |
+ NFC_ECC_EN | NFC_ECC_PIPELINE | NFC_ECC_EXCEPTION,
+ SUNXI_NFC_BASE + NFC_ECC_CTL);
+
+ flush_dcache_range(dst, ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
+
+ /* SUNXI_DMA */
+ writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */
+ /* read from REG_IO_DATA */
+ writel(SUNXI_NFC_BASE + NFC_IO_DATA,
+ SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0);
+ /* read to RAM */
+ writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0);
+ writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC |
+ SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE,
+ SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0);
+ writel(len, SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0);
+ writel(SUNXI_DMA_DDMA_CFG_REG_LOADING |
+ SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 |
+ SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM |
+ SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 |
+ SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO |
+ SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC,
+ SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0);
+
+ writel(nsectors, SUNXI_NFC_BASE + NFC_SECTOR_NUM);
+ writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
+ writel(NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_DATA_SWAP_METHOD,
+ SUNXI_NFC_BASE + NFC_CMD);
+
+ if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG,
+ DEFAULT_TIMEOUT_US)) {
+ printf("Error while initializing dma interrupt\n");
+ return -EIO;
+ }
+
+ writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
+
+ if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0,
+ SUNXI_DMA_DDMA_CFG_REG_LOADING,
+ DEFAULT_TIMEOUT_US)) {
+ printf("Error while waiting for dma transfer to finish\n");
+ return -EIO;
+ }
+
+ invalidate_dcache_range(dst,
+ ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
+
+ val = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
+
+ /* ECC error detected. */
+ if (val & 0xffff)
+ return -EIO;
+
+ /*
+ * Return 1 if the page is empty.
+ * We consider the page as empty if the first ECC block is marked
+ * empty.
+ */
+ return (val & 0x10000) ? 1 : 0;
+}
+#else
static int nand_read_page(const struct nfc_config *conf, u32 offs,
void *dest, int len)
{
@@ -326,6 +430,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,
return 0;
}
+#endif
static int nand_max_ecc_strength(struct nfc_config *conf)
{