Skip to content

Commit

Permalink
spl: nand: sunxi: use PIO instead of DMA
Browse files Browse the repository at this point in the history
SPL support was first written to support only the earlier generations of
Allwinner SoCs, and was only really enabled on the A13 / GR8. However,
those old SoCs had a DMA engine that has been replaced since the A31 by
another DMA controller that is no longer compatible.

Since the code directly uses that DMA controller, it cannot operate
properly on the later SoCs, while the NAND controller has not changed.

There's two paths forward, the first one would have been to add support
for that DMA controller too, the second to just remove the DMA usage
entirely and rely on PIO.

The later has been chosen because CPU overload at this stage is not an
issue and it makes the driver more generic, and easier to understand.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Acked-by: Boris Brezillon <boris.brezillon@bootlin.com>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
  • Loading branch information
miquelraynal authored and mripard committed Apr 3, 2018
1 parent 7440bd7 commit 6ddbb1e
Showing 1 changed file with 55 additions and 80 deletions.
135 changes: 55 additions & 80 deletions drivers/mtd/nand/sunxi_nand_spl.c
Expand Up @@ -10,6 +10,7 @@
#include <common.h>
#include <config.h>
#include <nand.h>
#include <linux/ctype.h>

/* registers */
#define NFC_CTL 0x00000000
Expand Down Expand Up @@ -67,10 +68,12 @@
#define NFC_SEND_CMD3 (1 << 28)
#define NFC_SEND_CMD4 (1 << 29)
#define NFC_RAW_CMD (0 << 30)
#define NFC_ECC_CMD (1 << 30)
#define NFC_PAGE_CMD (2 << 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
Expand All @@ -80,22 +83,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;
Expand Down Expand Up @@ -268,86 +255,74 @@ 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);

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);
/* 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);
writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
nand_exec_cmd(NFC_DATA_TRANS);

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;
}
/*
* Let the ECC engine consume the ECC bytes and possibly correct
* the data.
*/
nand_change_column(oob_off);
nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD);

invalidate_dcache_range(dst,
ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
/* Get the ECC status */
ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);

val = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
/* ECC error detected. */
if (ecc_st & 0xffff)
return -EIO;

/* ECC error detected. */
if (val & 0xffff)
return -EIO;
/*
* Return 1 if the first chunk is empty (needed for
* configuration detection).
*/
if (!i && (ecc_st & 0x10000))
return 1;

/*
* 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;
/* Retrieve the data from SRAM */
memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE,
conf->ecc_size);

/* Stop the ECC engine */
writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN,
SUNXI_NFC_BASE + NFC_ECC_CTL);

if (data_off + conf->ecc_size >= len)
break;
}

return 0;
}

static int nand_max_ecc_strength(struct nfc_config *conf)
Expand Down

0 comments on commit 6ddbb1e

Please sign in to comment.