mtd: rawnand: sunxi: Add A23/A33 DMA support
[sfrench/cifs-2.6.git] / drivers / mtd / nand / raw / sunxi_nand.c
index 4282bc47776177cc95dfa7bed5805b403c352688..b021a5720b42b350d679cd43a55968796d9a8f73 100644 (file)
@@ -42,7 +42,8 @@
 #define NFC_REG_CMD            0x0024
 #define NFC_REG_RCMD_SET       0x0028
 #define NFC_REG_WCMD_SET       0x002C
-#define NFC_REG_IO_DATA                0x0030
+#define NFC_REG_A10_IO_DATA    0x0030
+#define NFC_REG_A23_IO_DATA    0x0300
 #define NFC_REG_ECC_CTL                0x0034
 #define NFC_REG_ECC_ST         0x0038
 #define NFC_REG_DEBUG          0x003C
@@ -200,6 +201,22 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
        return container_of(nand, struct sunxi_nand_chip, nand);
 }
 
+/*
+ * NAND Controller capabilities structure: stores NAND controller capabilities
+ * for distinction between compatible strings.
+ *
+ * @sram_through_ahb:  On A23, we choose to access the internal RAM through AHB
+ *                      instead of MBUS (less configuration). A10, A10s, A13 and
+ *                      A20 use the MBUS but no extra configuration is needed.
+ * @reg_io_data:       I/O data register
+ * @dma_maxburst:      DMA maxburst
+ */
+struct sunxi_nfc_caps {
+       bool sram_through_ahb;
+       unsigned int reg_io_data;
+       unsigned int dma_maxburst;
+};
+
 /**
  * struct sunxi_nfc - stores sunxi NAND controller information
  *
@@ -228,6 +245,7 @@ struct sunxi_nfc {
        struct list_head chips;
        struct completion complete;
        struct dma_chan *dmac;
+       const struct sunxi_nfc_caps *caps;
 };
 
 static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl)
@@ -350,10 +368,29 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
                goto err_unmap_buf;
        }
 
-       writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
-              nfc->regs + NFC_REG_CTL);
+       /*
+        * On A23, we suppose the "internal RAM" (p.12 of the NFC user manual)
+        * refers to the NAND controller's internal SRAM. This memory is mapped
+        * and so is accessible from the AHB. It seems that it can also be
+        * accessed by the MBUS. MBUS accesses are mandatory when using the
+        * internal DMA instead of the external DMA engine.
+        *
+        * During DMA I/O operation, either we access this memory from the AHB
+        * by clearing the NFC_RAM_METHOD bit, or we set the bit and use the
+        * MBUS. In this case, we should also configure the MBUS DMA length
+        * NFC_REG_MDMA_CNT(0xC4) to be chunksize * nchunks. NAND I/O over MBUS
+        * are also limited to 32kiB pages.
+        */
+       if (nfc->caps->sram_through_ahb)
+               writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+                      nfc->regs + NFC_REG_CTL);
+       else
+               writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
+                      nfc->regs + NFC_REG_CTL);
+
        writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
        writel(chunksize, nfc->regs + NFC_REG_CNT);
+
        dmat = dmaengine_submit(dmad);
 
        ret = dma_submit_error(dmat);
@@ -1313,20 +1350,19 @@ pio_fallback:
 
 static int sunxi_nfc_hw_ecc_read_oob(struct nand_chip *nand, int page)
 {
-       nand->pagebuf = -1;
+       u8 *buf = nand_get_data_buf(nand);
 
-       return nand->ecc.read_page(nand, nand->data_buf, 1, page);
+       return nand->ecc.read_page(nand, buf, 1, page);
 }
 
 static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
 {
        struct mtd_info *mtd = nand_to_mtd(nand);
+       u8 *buf = nand_get_data_buf(nand);
        int ret;
 
-       nand->pagebuf = -1;
-
-       memset(nand->data_buf, 0xff, mtd->writesize);
-       ret = nand->ecc.write_page(nand, nand->data_buf, 1, page);
+       memset(buf, 0xff, mtd->writesize);
+       ret = nand->ecc.write_page(nand, buf, 1, page);
        if (ret)
                return ret;
 
@@ -1724,8 +1760,8 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand)
        nand->options |= NAND_SUBPAGE_READ;
 
        if (!ecc->size) {
-               ecc->size = nand->ecc_step_ds;
-               ecc->strength = nand->ecc_strength_ds;
+               ecc->size = nand->base.eccreq.step_size;
+               ecc->strength = nand->base.eccreq.strength;
        }
 
        if (!ecc->size || !ecc->strength)
@@ -2088,6 +2124,12 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
                goto out_mod_clk_unprepare;
        }
 
+       nfc->caps = of_device_get_match_data(&pdev->dev);
+       if (!nfc->caps) {
+               ret = -EINVAL;
+               goto out_ahb_reset_reassert;
+       }
+
        ret = sunxi_nfc_rst(nfc);
        if (ret)
                goto out_ahb_reset_reassert;
@@ -2102,12 +2144,12 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
        if (nfc->dmac) {
                struct dma_slave_config dmac_cfg = { };
 
-               dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA;
+               dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
                dmac_cfg.dst_addr = dmac_cfg.src_addr;
                dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
                dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
-               dmac_cfg.src_maxburst = 4;
-               dmac_cfg.dst_maxburst = 4;
+               dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
+               dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
                dmaengine_slave_config(nfc->dmac, &dmac_cfg);
        } else {
                dev_warn(dev, "failed to request rxtx DMA channel\n");
@@ -2152,8 +2194,26 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
+       .reg_io_data = NFC_REG_A10_IO_DATA,
+       .dma_maxburst = 4,
+};
+
+static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
+       .sram_through_ahb = true,
+       .reg_io_data = NFC_REG_A23_IO_DATA,
+       .dma_maxburst = 8,
+};
+
 static const struct of_device_id sunxi_nfc_ids[] = {
-       { .compatible = "allwinner,sun4i-a10-nand" },
+       {
+               .compatible = "allwinner,sun4i-a10-nand",
+               .data = &sunxi_nfc_a10_caps,
+       },
+       {
+               .compatible = "allwinner,sun8i-a23-nand-controller",
+               .data = &sunxi_nfc_a23_caps,
+       },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);