[PATCH v4] mtd: sunxi-nand: add mdma support for allwinner h3
Manuel Dipolt
mdipolt at robart.cc
Thu Oct 8 10:36:48 EDT 2020
The Allwinner H3 soc supports mdma mode, this patch enable support for it
see old sunxi drivers https://github.com/allwinner-zh/linux-3.4-sunxi/tree/master/modules/nand
Signed-off-by: Manuel Dipolt <manuel.dipolt at robart.cc>
---
drivers/mtd/nand/raw/sunxi_nand.c | 196 ++++++++++++++++++++++--------
1 file changed, 147 insertions(+), 49 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 2a7ca3072f35..28ee2f44f179 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -51,6 +51,7 @@
#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
#define NFC_REG_SPARE_AREA 0x00A0
#define NFC_REG_PAT_ID 0x00A4
+#define NFC_REG_MDMA_ADDR 0x00C0
#define NFC_REG_MDMA_CNT 0x00C4
#define NFC_RAM0_BASE 0x0400
#define NFC_RAM1_BASE 0x0800
@@ -60,7 +61,7 @@
#define NFC_RESET BIT(1)
#define NFC_BUS_WIDTH_MSK BIT(2)
#define NFC_BUS_WIDTH_8 (0 << 2)
-#define NFC_BUS_WIDTH_16 (1 << 2)
+#define NFC_BUS_WIDTH_16 BIT(2)
#define NFC_RB_SEL_MSK BIT(3)
#define NFC_RB_SEL(x) ((x) << 3)
#define NFC_CE_SEL_MSK GENMASK(26, 24)
@@ -118,7 +119,7 @@
#define NFC_SEND_CMD4 BIT(29)
#define NFC_CMD_TYPE_MSK GENMASK(31, 30)
#define NFC_NORMAL_OP (0 << 30)
-#define NFC_ECC_OP (1 << 30)
+#define NFC_ECC_OP BIT(30)
#define NFC_PAGE_OP (2U << 30)
/* define bit use in NFC_RCMD_SET */
@@ -207,12 +208,14 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
* NAND Controller capabilities structure: stores NAND controller capabilities
* for distinction between compatible strings.
*
+ * @has_mdma: use mbus dma mode, otherwise general dma
* @extra_mbus_conf: Contrary to A10, A10s and A13, accessing internal RAM
* through MBUS on A23/A33 needs extra configuration.
* @reg_io_data: I/O data register
* @dma_maxburst: DMA maxburst
*/
struct sunxi_nfc_caps {
+ bool has_mdma;
bool extra_mbus_conf;
unsigned int reg_io_data;
unsigned int dma_maxburst;
@@ -289,7 +292,7 @@ static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
writel(events, nfc->regs + NFC_REG_INT);
ret = wait_for_completion_timeout(&nfc->complete,
- msecs_to_jiffies(timeout_ms));
+ msecs_to_jiffies(timeout_ms));
if (!ret)
ret = -ETIMEDOUT;
else
@@ -393,6 +396,35 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
return ret;
}
+static int sunxi_nfc_mdma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
+ int chunksize, int nchunks,
+ enum dma_data_direction ddir,
+ __u32 *mem_addr)
+{
+ enum dma_transfer_direction tdir;
+ int ret;
+
+ if (ddir == DMA_FROM_DEVICE)
+ tdir = DMA_DEV_TO_MEM;
+ else
+ tdir = DMA_MEM_TO_DEV;
+
+ *mem_addr = (__u32)dma_map_single(nfc->dev, (void *)buf, nchunks * chunksize, tdir);
+ ret = dma_mapping_error(nfc->dev, *mem_addr);
+ if (ret) {
+ dev_err(nfc->dev, "DMA mapping error\n");
+ return ret;
+ }
+
+ 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 * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
+ writel(*mem_addr, nfc->regs + NFC_REG_MDMA_ADDR);
+ writel(chunksize, nfc->regs + NFC_REG_CNT);
+
+ return 0;
+}
+
static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
enum dma_data_direction ddir,
struct scatterlist *sg)
@@ -402,6 +434,21 @@ static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
nfc->regs + NFC_REG_CTL);
}
+static void sunxi_nfc_mdma_op_cleanup(struct sunxi_nfc *nfc,
+ enum dma_data_direction ddir,
+ __u32 *mem_addr, size_t size)
+{
+ enum dma_transfer_direction tdir;
+
+ if (ddir == DMA_FROM_DEVICE)
+ tdir = DMA_DEV_TO_MEM;
+ else
+ tdir = DMA_MEM_TO_DEV;
+
+ dma_unmap_single(nfc->dev, *mem_addr, size, tdir);
+ writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, nfc->regs + NFC_REG_CTL);
+}
+
static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
{
struct mtd_info *mtd = nand_to_mtd(nand);
@@ -656,7 +703,7 @@ static void sunxi_nfc_randomize_bbm(struct nand_chip *nand, int page, u8 *bbm)
}
static void sunxi_nfc_randomizer_write_buf(struct nand_chip *nand,
- const uint8_t *buf, int len,
+ const u8 *buf, int len,
bool ecc, int page)
{
sunxi_nfc_randomizer_config(nand, page, ecc);
@@ -911,14 +958,20 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
unsigned int max_bitflips = 0;
int ret, i, raw_mode = 0;
struct scatterlist sg;
- u32 status;
+ u32 status, wait_event_flags;
+ __u32 mem_addr;
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return ret;
- ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks,
- DMA_FROM_DEVICE, &sg);
+ if (nfc->caps->has_mdma)
+ ret = sunxi_nfc_mdma_op_prepare(nfc, buf, ecc->size, nchunks,
+ DMA_FROM_DEVICE, &mem_addr);
+ else
+ ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks,
+ DMA_FROM_DEVICE, &sg);
+
if (ret)
return ret;
@@ -929,19 +982,27 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
- dma_async_issue_pending(nfc->dmac);
+ wait_event_flags = NFC_CMD_INT_FLAG;
+
+ if (nfc->caps->has_mdma)
+ wait_event_flags = NFC_DMA_INT_FLAG;
+ else
+ dma_async_issue_pending(nfc->dmac);
writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
nfc->regs + NFC_REG_CMD);
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
- if (ret)
+ ret = sunxi_nfc_wait_events(nfc, wait_event_flags, false, 0);
+ if (ret && !nfc->caps->has_mdma)
dmaengine_terminate_all(nfc->dmac);
sunxi_nfc_randomizer_disable(nand);
sunxi_nfc_hw_ecc_disable(nand);
- sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg);
+ if (nfc->caps->has_mdma)
+ sunxi_nfc_mdma_op_cleanup(nfc, DMA_FROM_DEVICE, &mem_addr, nchunks * ecc->size);
+ else
+ sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg);
if (ret)
return ret;
@@ -1199,7 +1260,7 @@ static int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand,
}
static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
- const uint8_t *buf, int oob_required,
+ const u8 *buf, int oob_required,
int page)
{
struct mtd_info *mtd = nand_to_mtd(nand);
@@ -1277,6 +1338,8 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
struct nand_ecc_ctrl *ecc = &nand->ecc;
struct scatterlist sg;
int ret, i;
+ u32 wait_event_flags;
+ __u32 mem_addr;
sunxi_nfc_select_chip(nand, nand->cur_cs);
@@ -1284,10 +1347,14 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
if (ret)
return ret;
- ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps,
- DMA_TO_DEVICE, &sg);
+ if (nfc->caps->has_mdma)
+ ret = sunxi_nfc_mdma_op_prepare(nfc, buf, ecc->size, ecc->steps,
+ DMA_TO_DEVICE, &mem_addr);
+ else
+ ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps, DMA_TO_DEVICE, &sg);
+
if (ret)
- goto pio_fallback;
+ return ret;
for (i = 0; i < ecc->steps; i++) {
const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
@@ -1304,20 +1371,28 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
nfc->regs + NFC_REG_WCMD_SET);
- dma_async_issue_pending(nfc->dmac);
+ wait_event_flags = NFC_CMD_INT_FLAG;
+
+ if (nfc->caps->has_mdma)
+ wait_event_flags = NFC_DMA_INT_FLAG;
+ else
+ dma_async_issue_pending(nfc->dmac);
writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
- NFC_DATA_TRANS | NFC_ACCESS_DIR,
- nfc->regs + NFC_REG_CMD);
+ NFC_DATA_TRANS | NFC_ACCESS_DIR,
+ nfc->regs + NFC_REG_CMD);
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
- if (ret)
+ ret = sunxi_nfc_wait_events(nfc, wait_event_flags, false, 0);
+ if (ret && !nfc->caps->has_mdma)
dmaengine_terminate_all(nfc->dmac);
sunxi_nfc_randomizer_disable(nand);
sunxi_nfc_hw_ecc_disable(nand);
- sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg);
+ if (nfc->caps->has_mdma)
+ sunxi_nfc_mdma_op_cleanup(nfc, DMA_TO_DEVICE, &mem_addr, ecc->size * ecc->steps);
+ else
+ sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg);
if (ret)
return ret;
@@ -1359,7 +1434,7 @@ static const s32 tWB_lut[] = {6, 12, 16, 20};
static const s32 tRHW_lut[] = {4, 8, 12, 20};
static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
- u32 clk_period)
+ u32 clk_period)
{
u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
int i;
@@ -1695,7 +1770,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
ecc->priv = data;
- if (nfc->dmac) {
+ if (nfc->dmac || nfc->caps->has_mdma) {
ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
@@ -1949,10 +2024,8 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
sunxi_nand = devm_kzalloc(dev, struct_size(sunxi_nand, sels, nsels),
GFP_KERNEL);
- if (!sunxi_nand) {
- dev_err(dev, "could not allocate chip\n");
+ if (!sunxi_nand)
return -ENOMEM;
- }
sunxi_nand->nsels = nsels;
@@ -2058,6 +2131,41 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
}
}
+static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
+{
+ int ret;
+
+ if (nfc->caps->has_mdma) {
+ writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
+ nfc->regs + NFC_REG_CTL);
+ } else {
+ nfc->dmac = dma_request_chan(nfc->dev, "rxtx");
+ if (IS_ERR(nfc->dmac)) {
+ ret = PTR_ERR(nfc->dmac);
+ if (ret)
+ return ret;
+
+ /* Ignore errors to fall back to PIO mode */
+ dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret);
+ } else {
+ struct dma_slave_config dmac_cfg = { };
+
+ 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 = nfc->caps->dma_maxburst;
+ dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
+ dmaengine_slave_config(nfc->dmac, &dmac_cfg);
+
+ if (nfc->caps->extra_mbus_conf)
+ writel(readl(nfc->regs + NFC_REG_CTL) |
+ NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL);
+ }
+ }
+ return 0;
+}
+
static int sunxi_nfc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -2132,30 +2240,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
if (ret)
goto out_ahb_reset_reassert;
- nfc->dmac = dma_request_chan(dev, "rxtx");
- if (IS_ERR(nfc->dmac)) {
- ret = PTR_ERR(nfc->dmac);
- if (ret == -EPROBE_DEFER)
- goto out_ahb_reset_reassert;
+ ret = sunxi_nfc_dma_init(nfc, r);
- /* Ignore errors to fall back to PIO mode */
- dev_warn(dev, "failed to request rxtx DMA channel: %d\n", ret);
- nfc->dmac = NULL;
- } else {
- struct dma_slave_config dmac_cfg = { };
-
- 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 = nfc->caps->dma_maxburst;
- dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
- dmaengine_slave_config(nfc->dmac, &dmac_cfg);
-
- if (nfc->caps->extra_mbus_conf)
- writel(readl(nfc->regs + NFC_REG_CTL) |
- NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL);
- }
+ if (ret)
+ goto out_ahb_reset_reassert;
platform_set_drvdata(pdev, nfc);
@@ -2197,16 +2285,22 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
}
static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
+ .has_mdma = false,
.reg_io_data = NFC_REG_A10_IO_DATA,
.dma_maxburst = 4,
};
static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
+ .has_mdma = false,
.extra_mbus_conf = true,
.reg_io_data = NFC_REG_A23_IO_DATA,
.dma_maxburst = 8,
};
+static const struct sunxi_nfc_caps sunxi_nfc_h3_caps = {
+ .has_mdma = true,
+};
+
static const struct of_device_id sunxi_nfc_ids[] = {
{
.compatible = "allwinner,sun4i-a10-nand",
@@ -2216,6 +2310,10 @@ static const struct of_device_id sunxi_nfc_ids[] = {
.compatible = "allwinner,sun8i-a23-nand-controller",
.data = &sunxi_nfc_a23_caps,
},
+ {
+ .compatible = "allwinner,sun8i-h3-nand-controller",
+ .data = &sunxi_nfc_h3_caps,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
--
2.20.1
More information about the linux-mtd
mailing list