diff options
Diffstat (limited to 'drivers/mtd/nand/raw/sunxi_nand.c')
-rw-r--r-- | drivers/mtd/nand/raw/sunxi_nand.c | 90 |
1 files changed, 75 insertions, 15 deletions
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 4282bc477761..b021a5720b42 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -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); |