From: Miquel Raynal <miquel.raynal@free-electrons.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH 5/8] spl: nand: sunxi: use PIO instead of DMA
Date: Fri, 26 Jan 2018 01:09:38 +0100 [thread overview]
Message-ID: <20180126010938.3ee64eba@xps13> (raw)
In-Reply-To: <20180124091619.40d29d03@bbrezillon>
Hi Boris,
On Wed, 24 Jan 2018 09:16:19 +0100
Boris Brezillon <boris.brezillon@free-electrons.com> wrote:
> On Wed, 24 Jan 2018 01:44:51 +0100
> Miquel Raynal <miquel.raynal@free-electrons.com> wrote:
>
> > Because using DMA implementation is not generic and was not developped
> > to work on SoCs like A33, migrate the SPL to use PIO.
> >
> > Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com>
> > ---
> > board/sunxi/board.c | 4 +-
> > drivers/mtd/nand/sunxi_nand_spl.c | 167 +++++++++++++++++---------------------
> > 2 files changed, 79 insertions(+), 92 deletions(-)
> >
> > diff --git a/board/sunxi/board.c b/board/sunxi/board.c
> > index 70e01437c4..512e2c17a6 100644
> > --- a/board/sunxi/board.c
> > +++ b/board/sunxi/board.c
> > @@ -266,11 +266,13 @@ static void nand_clock_setup(void)
> > struct sunxi_ccm_reg *const ccm =
> > (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> >
> > - setbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
> > + setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0));
> > + setbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_GATE_OFFSET_NAND0));
> > #ifdef CONFIG_MACH_SUN9I
> > setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
> > #else
> > setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
> > + setbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_GATE_OFFSET_DMA));
> > #endif
> > setbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
> > }
> > diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c
> > index 2488d5cb51..5de6825994 100644
> > --- a/drivers/mtd/nand/sunxi_nand_spl.c
> > +++ b/drivers/mtd/nand/sunxi_nand_spl.c
> > @@ -10,6 +10,7 @@
> > #include <common.h>
> > #include <config.h>
> > #include <nand.h>
> > +#include <linux/ctype.h>
> >
> > /* registers */
> > #define NFC_CTL 0x00000000
> > @@ -45,32 +46,22 @@
> > #define NFC_CTL_PAGE_SIZE_MASK (0xf << 8)
> > #define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8)
> >
> > -
> > #define NFC_ECC_EN (1 << 0)
> > -#define NFC_ECC_PIPELINE (1 << 3)
> > #define NFC_ECC_EXCEPTION (1 << 4)
> > #define NFC_ECC_BLOCK_SIZE (1 << 5)
> > #define NFC_ECC_RANDOM_EN (1 << 9)
> > -#define NFC_ECC_RANDOM_DIRECTION (1 << 10)
> > -
> >
> > #define NFC_ADDR_NUM_OFFSET 16
> > -#define NFC_ACCESS_DIR (1 << 20)
> > #define NFC_SEND_ADDR (1 << 19)
> > #define NFC_DATA_TRANS (1 << 21)
> > #define NFC_SEND_CMD1 (1 << 22)
> > #define NFC_WAIT_FLAG (1 << 23)
> > #define NFC_SEND_CMD2 (1 << 24)
> > -#define NFC_SEQ (1 << 25)
> > -#define NFC_DATA_SWAP_METHOD (1 << 26)
> > -#define NFC_ROW_AUTO_INC (1 << 27)
> > -#define NFC_SEND_CMD3 (1 << 28)
> > -#define NFC_SEND_CMD4 (1 << 29)
> > #define NFC_RAW_CMD (0 << 30)
> > -#define NFC_PAGE_CMD (2 << 30)
> > +#define NFC_ECC_CMD (1 << 30)
> >
> > #define NFC_ST_CMD_INT_FLAG (1 << 1)
> > -#define NFC_ST_DMA_INT_FLAG (1 << 2)
> > +#define NFC_ST_CMD_FIFO_STAT (1 << 3)
> >
> > #define NFC_READ_CMD_OFFSET 0
> > #define NFC_RANDOM_READ_CMD0_OFFSET 8
> > @@ -80,22 +71,6 @@
> > #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,7 +229,23 @@ static int nand_reset_column(void)
> > SUNXI_NFC_BASE + NFC_CMD);
> >
> > return nand_wait_int();
> > +}
> >
> > +static int nand_change_column(u16 column)
> > +{
> > + nand_wait_cmd_fifo_empty();
> > +
> > + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
> > + (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
> > + (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET),
> > + SUNXI_NFC_BASE + NFC_RCMD_SET);
> > + writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
> > + writel(column, SUNXI_NFC_BASE + NFC_ADDR_LOW);
> > + writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
> > + (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR | NFC_CMD_RNDOUT,
> > + SUNXI_NFC_BASE + NFC_CMD);
> > +
> > + return nand_wait_int();
> > }
>
> nand_reset_column() and nand_change_column() are almost the same,
> except one is taking an argument to specify where you want to place the
> pointer. I'd recommend getting rid of nand_reset_column() entirely and
> patching all call sites to call nand_change_column(0) instead.
Done in a separate commit.
>
> >
> > static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116};
> > @@ -262,86 +253,80 @@ static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116};
> > 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;
> > + int oob_chunk_sz = ecc_bytes[conf->ecc_strength];
> > + int page = offs / conf->page_size;
> > + u32 ecc_st;
> > + int i;
> >
> > 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);
> > + /* Retrieve data from SRAM (PIO) */
> > + for (i = 0; i < nsectors; i++) {
> > + int data_off = i * conf->ecc_size;
> > + int oob_off = conf->page_size + (i * oob_chunk_sz);
> > + u8 *data = dest + data_off;
> > +
> > + /* Clear ECC status and restart ECC engine */
> > + writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
> > + 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_EXCEPTION,
> > + SUNXI_NFC_BASE + NFC_ECC_CTL);
> > +
> > + /* Move the data in SRAM */
> > + nand_change_column(data_off);
> > + nand_wait_cmd_fifo_empty();
> > + writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
> > + writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
> > + writel(NFC_DATA_TRANS, SUNXI_NFC_BASE + NFC_CMD);
> > + nand_wait_int();
> >
> > - 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);
> > + /*
> > + * Let the ECC engine consume the ECC bytes and possibly correct
> > + * the data.
> > + */
> > + nand_change_column(oob_off);
> > + nand_wait_cmd_fifo_empty();
> > + writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
> > + writel(NFC_DATA_TRANS | NFC_ECC_CMD, SUNXI_NFC_BASE + NFC_CMD);
> > + nand_wait_int();
> >
> > - 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;
> > - }
> > + /* Get the ECC status */
> > + ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
> >
> > - invalidate_dcache_range(dst,
> > - ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
> > + /* Retrieve the data from SRAM */
> > + memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE,
> > + conf->ecc_size);
>
> Not a big deal, but should we really copy the data if the ECC engine
> reports an error? Maybe you can move this memcpy_fromio() after checking
> ecc_st.
Ok.
>
> >
> > - val = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
> > + /* Stop ECC engine */
> > + writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN,
> > + SUNXI_NFC_BASE + NFC_ECC_CTL);
> >
> > - /* ECC error detected. */
> > - if (val & 0xffff)
> > - return -EIO;
> > + /* ECC error detected. */
> > + if (ecc_st & 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;
> > + /*
> > + * Return 1 if the page is empty. We consider the page as empty
> > + * if the first ECC block is marked empty.
> > + */
> > + if (ecc_st & 0x10000)
> > + return 1;
>
> This is no longer valid, because now you iterate over all ECC chunks
> manually. The test should be:
>
> if (!i && (ecc_st & 0x10000))
>
That is right and could have caused troubles, thanks for spotting it!
> > +
> > + if (data_off + conf->ecc_size >= len)
> > + break;
> > + }
> > +
> > + return 0;
> > }
> >
> > static int nand_max_ecc_strength(struct nfc_config *conf)
>
Thanks for reviewing,
Miquèl
--
Miquel Raynal, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
next prev parent reply other threads:[~2018-01-26 0:09 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-01-24 0:44 [U-Boot] [PATCH 0/8] Bring NAND support to Nintendo NES Classic Miquel Raynal
2018-01-24 0:44 ` [U-Boot] [PATCH 1/8] mtd: nand: sunxi: Fix strength minimum value Miquel Raynal
2018-01-24 7:46 ` Maxime Ripard
2018-01-24 22:21 ` Miquel Raynal
2018-01-24 7:57 ` Boris Brezillon
2018-01-24 22:19 ` Miquel Raynal
2018-01-24 0:44 ` [U-Boot] [PATCH 2/8] spl: nand: sunxi: Fix second case of modulo by zero error Miquel Raynal
2018-01-24 7:47 ` Maxime Ripard
2018-01-24 22:32 ` Miquel Raynal
2018-01-24 0:44 ` [U-Boot] [PATCH 3/8] sunxi: Allow SPL to be compiled for sun8i platforms Miquel Raynal
2018-01-24 7:49 ` Maxime Ripard
2018-01-24 22:37 ` Miquel Raynal
2018-01-24 0:44 ` [U-Boot] [PATCH 4/8] spl: nand: sunxi: Enhancements and cleaning Miquel Raynal
2018-01-24 7:56 ` Maxime Ripard
2018-01-25 23:34 ` Miquel Raynal
2018-01-24 8:06 ` Boris Brezillon
2018-01-25 23:37 ` Miquel Raynal
2018-01-24 0:44 ` [U-Boot] [PATCH 5/8] spl: nand: sunxi: use PIO instead of DMA Miquel Raynal
2018-01-24 8:06 ` Maxime Ripard
2018-01-24 8:26 ` Boris Brezillon
2018-01-24 8:39 ` Maxime Ripard
2018-01-25 23:58 ` Miquel Raynal
2018-01-24 8:16 ` Boris Brezillon
2018-01-26 0:09 ` Miquel Raynal [this message]
2018-01-24 0:44 ` [U-Boot] [PATCH 6/8] configs: Add NAND support for NES Classic Miquel Raynal
2018-01-24 8:10 ` Maxime Ripard
2018-01-24 0:44 ` [U-Boot] [PATCH 7/8] sunxi: dts: Add NAND node to sun8i DTSI Miquel Raynal
2018-01-24 8:16 ` Maxime Ripard
2018-01-24 0:44 ` [U-Boot] [PATCH 8/8] sunxi: dts: Enable NAND on NES classic Miquel Raynal
2018-01-24 8:18 ` Boris Brezillon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180126010938.3ee64eba@xps13 \
--to=miquel.raynal@free-electrons.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox