Re: [PATCH 3/4] mtd: nand: Add support for Evatronix NANDFLASH-CTRL
From: Boris Brezillon
Date: Fri Jun 03 2016 - 10:59:28 EST
On Thu, 2 Jun 2016 09:48:32 +0200
Ricard Wanderlof <ricard.wanderlof@xxxxxxxx> wrote:
> Driver for the Evatronix NANDFLASH-CTRL NAND flash controller IP. This
> controller is used in the Axis ARTPEC-6 SoC.
>
> The driver supports BCH ECC using the controller's hardware, but there is
> also an option to use software BCH ECC. However, the ECC layouts are not
> compatible so it's not possible to mix them. The main advantage to using
> software ECC is that there are more OOB bytes free, as the hardware is
> slightly wasteful on OOB space.
>
> BCH ECC from 4 to 32 bits over 256, 512 or 1024 byte ECC blocks is supported.
>
> Only large-page flash chips are supported, using 4 or 5 address cycles.
>
> The driver has been extensively tested using hardware ECC on 2 Mbit flash chips,
> with 8 bit ECC over 512 bytes ECC blocks.
>
> Signed-off-by: Ricard Wanderlof <ricardw@xxxxxxxx>
> ---
> drivers/mtd/nand/Kconfig | 6 +
> drivers/mtd/nand/Makefile | 1 +
> drivers/mtd/nand/evatronix_nand.c | 1909 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 1916 insertions(+)
> create mode 100644 drivers/mtd/nand/evatronix_nand.c
Please run checkpatch.pl and fix all the ERRORS and WARNINGS.
>
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index f05e0e9..30fba73 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -295,6 +295,12 @@ config MTD_NAND_DOCG4
> by the block containing the saftl partition table. This is probably
> typical.
>
> +config MTD_NAND_EVATRONIX
> + tristate "Enable Evatronix NANDFLASH-CTRL driver"
> + help
> + NAND hardware driver for Evatronix NANDFLASH-CTRL
> + NAND flash controller.
> +
> config MTD_NAND_SHARPSL
> tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
> depends on ARCH_PXA
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index f553353..ac89b12 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
> obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
> obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
> obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
> +obj-$(CONFIG_MTD_NAND_EVATRONIX) += evatronix_nand.o
> obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
> obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
> obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
> diff --git a/drivers/mtd/nand/evatronix_nand.c b/drivers/mtd/nand/evatronix_nand.c
> new file mode 100644
> index 0000000..94eb582
> --- /dev/null
> +++ b/drivers/mtd/nand/evatronix_nand.c
> @@ -0,0 +1,1909 @@
> +/*
> + * evatronix_nand.c - NAND Flash Driver for Evatronix NANDFLASH-CTRL
> + * NAND Flash Controller IP.
> + *
> + * Intended to handle one NFC, with up to two connected NAND flash chips,
> + * one per bank.
> + *
> + * This implementation has been designed against Rev 1.15 and Rev 1.16 of the
> + * NANDFLASH-CTRL Design Specification.
> + * Note that Rev 1.15 specifies up to 8 chip selects, whereas Rev 1.16
> + * only specifies one. We keep the definitions for the multiple chip
> + * selects though for future reference.
> + *
> + * The corresponding IP version is NANDFLASH-CTRL-DES-6V09H02RE08 .
> + *
> + * Copyright (c) 2016 Axis Communication AB, Lund, Sweden.
> + * Portions Copyright (c) 2010 ST Microelectronics
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <asm/dma.h>
> +#include <linux/bitops.h> /* for ffs() */
> +#include <linux/io.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/concat.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/version.h>
You seem to include a lot of things, and even asm headers. Please make
sure you really need them.
> +
> +/* Driver configuration */
> +
> +/* Some of this could potentially be moved to DT, but it represents stuff
> + * that is either untested, only used for debugging, or things we really
> + * don't want anyone to change, so we keep it here until a clear use case
> + * emerges.
> + */
> +
> +#undef NFC_DMA64BIT /* NFC hardware support for 64-bit DMA transfers */
> +
> +#undef POLLED_XFERS /* Use polled rather than interrupt based transfers */
> +
> +#undef CLEAR_DMA_BUF_AFTER_WRITE /* Useful for debugging */
Then simply drop the code in those sections and add it back when it's
been tested.
> +
> +/* DMA buffer for page transfers. */
> +#define DMA_BUF_SIZE (8192 + 640) /* main + spare for 8k page flash */
This should clearly be dynamic.
> +
> +/* # bytes into the OOB we put our ECC */
> +#define ECC_OFFSET 2
> +
> +/* Number of bytes that we read using READID command.
> + * When reading IDs the IP requires us set up the number of bytes to read
> + * prior to executing the operation, whereas the NAND subsystem would rather
> + * like us to be able to read one byte at a time from the chip. So we fake
> + * this by reading a set number of ID bytes, and then let the NAND subsystem
> + * read from our DMA buffer.
> + */
> +#define READID_LENGTH 8
> +
> +/* Debugging */
> +
> +#define MTD_TRACE(FORMAT, ...) pr_debug("%s: " FORMAT, __func__, ## __VA_ARGS__)
Hm, I'm not a big fan of those custom pr_debug() macros, but if you
really wan to keep it you shouldn't prefix it with MTD_.
Reading at the code I see a lot of MTD_TRACE() calls, while I'm not
against debug traces, it seems to me that you've kept traces you used
while developing/debugging your implementation. Can you clean it up and
keep only the relevant ones.
> +
> +/* Register offsets for Evatronix NANDFLASH-CTRL IP */
> +
> +/* Register field shift values and masks are interespersed as it makes
> + * them easier to locate.
> + *
> + * We use shift values rather than direct masks (e.g. 0x0000d000), as the
> + * hardware manual lists the bit number, making the definitions below
> + * easier to verify against the manual.
> + *
> + * All (known) registers are here, but we only put in the bit fields
> + * for the fields we need.
> + *
> + * We try to be consistent regarding _SIZE/_MASK/_value macros so as to
> + * get a consistent layout here, except for trivial cases where there is
> + * only a single bit or field in a register at bit offset 0.
> + */
> +
> +#define COMMAND_REG 0x00
> +/* The masks reflect the input data to the MAKE_COMMAND macro, rather than
> + * the bits in the register itself. These macros are not intended to be
> + * used by the user, who should use the MAKE_COMMAND et al macros.
> + */
> +#define _CMD_SEQ_SHIFT 0
> +#define _INPUT_SEL_SHIFT 6
> +#define _DATA_SEL_SHIFT 7
> +#define _CMD_0_SHIFT 8
> +#define _CMD_1_3_SHIFT 16
> +#define _CMD_2_SHIFT 24
> +
> +#define _CMD_SEQ_MASK 0x3f
> +#define _INPUT_SEL_MASK 1
> +#define _DATA_SEL_MASK 1
> +#define _CMD_MASK 0xff /* for all CMD_foo */
> +
> +#define MAKE_COMMAND(CMD_SEQ, INPUT_SEL, DATA_SEL, CMD_0, CMD_1_3, CMD_2) \
> + ((((CMD_SEQ) & _CMD_SEQ_MASK) << _CMD_SEQ_SHIFT) | \
> + (((INPUT_SEL) & _INPUT_SEL_MASK) << _INPUT_SEL_SHIFT) | \
> + (((DATA_SEL) & _DATA_SEL_MASK) << _DATA_SEL_SHIFT) | \
> + (((CMD_0) & _CMD_MASK) << _CMD_0_SHIFT) | \
> + (((CMD_1_3) & _CMD_MASK) << _CMD_1_3_SHIFT) | \
> + (((CMD_2) & _CMD_MASK) << _CMD_2_SHIFT))
> +
> +#define INPUT_SEL_SIU 0
> +#define INPUT_SEL_DMA 1
> +#define DATA_SEL_FIFO 0
> +#define DATA_SEL_DATA_REG 1
> +
> +#define CONTROL_REG 0x04
> +#define CONTROL_BLOCK_SIZE_32 (0 << 6)
> +#define CONTROL_BLOCK_SIZE_64 (1 << 6)
> +#define CONTROL_BLOCK_SIZE_128 (2 << 6)
> +#define CONTROL_BLOCK_SIZE_256 (3 << 6)
> +#define CONTROL_BLOCK_SIZE(SIZE) ((ffs(SIZE) - 6) << 6)
> +#define CONTROL_ECC_EN (1 << 5)
> +#define CONTROL_INT_EN (1 << 4)
> +#define CONTROL_ECC_BLOCK_SIZE_256 (0 << 1)
> +#define CONTROL_ECC_BLOCK_SIZE_512 (1 << 1)
> +#define CONTROL_ECC_BLOCK_SIZE_1024 (2 << 1)
> +#define CONTROL_ECC_BLOCK_SIZE(SIZE) ((ffs(SIZE) - 9) << 1)
> +#define STATUS_REG 0x08
> +#define STATUS_MEM_ST(CS) (1 << (CS))
> +#define STATUS_CTRL_STAT (1 << 8)
> +#define STATUS_MASK_REG 0x0C
> +#define STATE_MASK_SHIFT 0
> +#define STATUS_MASK_STATE_MASK(MASK) (((MASK) & 0xff) << STATE_MASK_SHIFT)
> +#define ERROR_MASK_SHIFT 8
> +#define STATUS_MASK_ERROR_MASK(MASK) (((MASK) & 0xff) << ERROR_MASK_SHIFT)
> +#define INT_MASK_REG 0x10
> +#define INT_MASK_ECC_INT_EN(CS) (1 << (24 + (CS)))
> +#define INT_MASK_STAT_ERR_INT_EN(CS) (1 << (16 + (CS)))
> +#define INT_MASK_MEM_RDY_INT_EN(CS) (1 << (8 + (CS)))
> +#define INT_MASK_DMA_INT_EN (1 << 3)
> +#define INT_MASK_DATA_REG_EN (1 << 2)
> +#define INT_MASK_CMD_END_INT_EN (1 << 1)
> +#define INT_STATUS_REG 0x14
> +#define INT_STATUS_ECC_INT_FL(CS) (1 << (24 + (CS)))
> +#define INT_STATUS_STAT_ERR_INT_FL(CS) (1 << (16 + (CS)))
> +#define INT_STATUS_MEM_RDY_INT_FL(CS) (1 << (8 + (CS)))
> +#define INT_STATUS_DMA_INT_FL (1 << 3)
> +#define INT_STATUS_DATA_REG_FL (1 << 2)
> +#define INT_STATUS_CMD_END_INT_FL (1 << 1)
> +#define ECC_CTRL_REG 0x18
> +#define ECC_CTRL_ECC_CAP_2 (0 << 0)
> +#define ECC_CTRL_ECC_CAP_4 (1 << 0)
> +#define ECC_CTRL_ECC_CAP_8 (2 << 0)
> +#define ECC_CTRL_ECC_CAP_16 (3 << 0)
> +#define ECC_CTRL_ECC_CAP_24 (4 << 0)
> +#define ECC_CTRL_ECC_CAP_32 (5 << 0)
> +#define ECC_CTRL_ECC_CAP(B) ((B) < 24 ? ffs(B) - 2 : (B) / 6)
> +/* # ECC corrections that are acceptable during read before setting OVER flag */
> +#define ECC_CTRL_ECC_THRESHOLD(VAL) (((VAL) & 0x3f) << 8)
> +#define ECC_OFFSET_REG 0x1C
> +#define ECC_STAT_REG 0x20
> +/* Correctable error flag(s) */
> +#define ECC_STAT_ERROR(CS) (1 << (0 + (CS)))
> +/* Uncorrectable error flag(s) */
> +#define ECC_STAT_UNC(CS) (1 << (8 + (CS)))
> +/* Acceptable errors level overflow flag(s) */
> +#define ECC_STAT_OVER(CS) (1 << (16 + (CS)))
> +#define ADDR0_COL_REG 0x24
> +#define ADDR0_ROW_REG 0x28
> +#define ADDR1_COL_REG 0x2C
> +#define ADDR1_ROW_REG 0x30
> +#define PROTECT_REG 0x34
> +#define FIFO_DATA_REG 0x38
> +#define DATA_REG_REG 0x3C
> +#define DATA_REG_SIZE_REG 0x40
> +#define DATA_REG_SIZE_DATA_REG_SIZE(SIZE) (((SIZE) - 1) & 3)
> +#define DEV0_PTR_REG 0x44
> +#define DEV1_PTR_REG 0x48
> +#define DEV2_PTR_REG 0x4C
> +#define DEV3_PTR_REG 0x50
> +#define DEV4_PTR_REG 0x54
> +#define DEV5_PTR_REG 0x58
> +#define DEV6_PTR_REG 0x5C
> +#define DEV7_PTR_REG 0x60
> +#define DMA_ADDR_L_REG 0x64
> +#define DMA_ADDR_H_REG 0x68
> +#define DMA_CNT_REG 0x6C
> +#define DMA_CTRL_REG 0x70
> +#define DMA_CTRL_DMA_START (1 << 7) /* start on command */
> +#define DMA_CTRL_DMA_MODE_SG (1 << 5) /* scatter/gather mode */
> +#define DMA_CTRL_DMA_BURST_I_P_4 (0 << 2) /* incr. precise burst */
> +#define DMA_CTRL_DMA_BURST_S_P_16 (1 << 2) /* stream precise burst */
> +#define DMA_CTRL_DMA_BURST_SINGLE (2 << 2) /* single transfer */
> +#define DMA_CTRL_DMA_BURST_UNSPEC (3 << 2) /* burst of unspec. length */
> +#define DMA_CTRL_DMA_BURST_I_P_8 (4 << 2) /* incr. precise burst */
> +#define DMA_CTRL_DMA_BURST_I_P_16 (5 << 2) /* incr. precise burst */
> +#define DMA_CTRL_ERR_FLAG (1 << 1) /* read only */
> +#define DMA_CTRL_DMA_READY (1 << 0) /* read only */
> +#define BBM_CTRL_REG 0x74
> +#define MEM_CTRL_REG 0x80
> +#define MEM_CTRL_MEM_CE(CE) (((CE) & 7) << 0)
> +#define MEM_CTRL_BANK_SEL(BANK) (((BANK) & 7) << 16)
> +#define MEM_CTRL_MEM0_WR BIT(8)
> +#define DATA_SIZE_REG 0x84
> +#define TIMINGS_ASYN_REG 0x88
> +#define TIMINGS_SYN_REG 0x8C
> +#define TIME_SEQ_0_REG 0x90
> +#define TIME_SEQ_1_REG 0x94
> +#define TIME_GEN_SEQ_0_REG 0x98
> +#define TIME_GEN_SEQ_1_REG 0x9C
> +#define TIME_GEN_SEQ_2_REG 0xA0
> +#define FIFO_INIT_REG 0xB0
> +#define FIFO_INIT_FIFO_INIT 1 /* Flush FIFO */
> +#define FIFO_STATE_REG 0xB4
> +#define FIFO_STATE_DF_W_EMPTY (1 << 7)
> +#define FIFO_STATE_DF_R_FULL (1 << 6)
> +#define FIFO_STATE_CF_ACCPT_W (1 << 5)
> +#define FIFO_STATE_CF_ACCPT_R (1 << 4)
> +#define FIFO_STATE_CF_FULL (1 << 3)
> +#define FIFO_STATE_CF_EMPTY (1 << 2)
> +#define FIFO_STATE_DF_W_FULL (1 << 1)
> +#define FIFO_STATE_DF_R_EMPTY (1 << 0)
> +#define GEN_SEQ_CTRL_REG 0xB8 /* aka GENERIC_SEQ_CTRL */
> +#define _CMD0_EN_SHIFT 0
> +#define _CMD1_EN_SHIFT 1
> +#define _CMD2_EN_SHIFT 2
> +#define _CMD3_EN_SHIFT 3
> +#define _COL_A0_SHIFT 4
> +#define _COL_A1_SHIFT 6
> +#define _ROW_A0_SHIFT 8
> +#define _ROW_A1_SHIFT 10
> +#define _DATA_EN_SHIFT 12
> +#define _DELAY_EN_SHIFT 13
> +#define _IMD_SEQ_SHIFT 15
> +#define _CMD3_SHIFT 16
> +#define ECC_CNT_REG 0x14C
> +#define ECC_CNT_ERR_LVL_MASK 0x3F
> +
> +#define _CMD0_EN_MASK 1
> +#define _CMD1_EN_MASK 1
> +#define _CMD2_EN_MASK 1
> +#define _CMD3_EN_MASK 1
> +#define _COL_A0_MASK 3
> +#define _COL_A1_MASK 3
> +#define _ROW_A0_MASK 3
> +#define _ROW_A1_MASK 3
> +#define _DATA_EN_MASK 1
> +#define _DELAY_EN_MASK 3
> +#define _IMD_SEQ_MASK 1
> +#define _CMD3_MASK 0xff
> +
> +/* DELAY_EN field values, non-shifted */
> +#define _BUSY_NONE 0
> +#define _BUSY_0 1
> +#define _BUSY_1 2
> +
> +/* Slightly confusingly, the DELAYx_EN fields enable BUSY phases. */
> +#define MAKE_GEN_CMD(CMD0_EN, CMD1_EN, CMD2_EN, CMD3_EN, \
> + COL_A0, ROW_A0, COL_A1, ROW_A1, \
> + DATA_EN, BUSY_EN, IMMEDIATE_SEQ, CMD3) \
> + ((((CMD0_EN) & _CMD0_EN_MASK) << _CMD0_EN_SHIFT) | \
> + (((CMD1_EN) & _CMD1_EN_MASK) << _CMD1_EN_SHIFT) | \
> + (((CMD2_EN) & _CMD2_EN_MASK) << _CMD2_EN_SHIFT) | \
> + (((CMD3_EN) & _CMD3_EN_MASK) << _CMD3_EN_SHIFT) | \
> + (((COL_A0) & _COL_A0_MASK) << _COL_A0_SHIFT) | \
> + (((COL_A1) & _COL_A1_MASK) << _COL_A1_SHIFT) | \
> + (((ROW_A0) & _ROW_A0_MASK) << _ROW_A0_SHIFT) | \
> + (((ROW_A1) & _ROW_A1_MASK) << _ROW_A1_SHIFT) | \
> + (((DATA_EN) & _DATA_EN_MASK) << _DATA_EN_SHIFT) | \
> + (((BUSY_EN) & _DELAY_EN_MASK) << _DELAY_EN_SHIFT) | \
> + (((IMMEDIATE_SEQ) & _IMD_SEQ_MASK) << _IMD_SEQ_SHIFT) | \
> + (((CMD3) & _CMD3_MASK) << _CMD3_SHIFT))
> +
> +/* The sequence encodings are not trivial. The ones we use are listed here. */
> +#define _SEQ_0 0x00 /* send one cmd, then wait for ready */
> +#define _SEQ_1 0x21 /* send one cmd, one addr, fetch data */
> +#define _SEQ_2 0x22 /* send one cmd, one addr, fetch data */
> +#define _SEQ_4 0x24 /* single cycle write then read */
> +#define _SEQ_10 0x2A /* read page */
> +#define _SEQ_12 0x0C /* write page, don't wait for R/B */
> +#define _SEQ_18 0x32 /* read page using general cycle */
> +#define _SEQ_19 0x13 /* write page using general cycle */
> +#define _SEQ_14 0x0E /* 3 address cycles, for block erase */
> +
> +#define MLUN_REG 0xBC
> +#define DEV0_SIZE_REG 0xC0
> +#define DEV1_SIZE_REG 0xC4
> +#define DEV2_SIZE_REG 0xC8
> +#define DEV3_SIZE_REG 0xCC
> +#define DEV4_SIZE_REG 0xD0
> +#define DEV5_SIZE_REG 0xD4
> +#define DEV6_SIZE_REG 0xD8
> +#define DEV7_SIZE_REG 0xDC
> +#define SS_CCNT0_REG 0xE0
> +#define SS_CCNT1_REG 0xE4
> +#define SS_SCNT_REG 0xE8
> +#define SS_ADDR_DEV_CTRL_REG 0xEC
> +#define SS_CMD0_REG 0xF0
> +#define SS_CMD1_REG 0xF4
> +#define SS_CMD2_REG 0xF8
> +#define SS_CMD3_REG 0xFC
> +#define SS_ADDR_REG 0x100
> +#define SS_MSEL_REG 0x104
> +#define SS_REQ_REG 0x108
> +#define SS_BRK_REG 0x10C
> +#define DMA_TLVL_REG 0x114
> +#define DMA_TLVL_MAX 0xFF
> +#define AES_CTRL_REG 0x118
> +#define AES_DATAW_REG 0x11C
> +#define AES_SVECT_REG 0x120
> +#define CMD_MARK_REG 0x124
> +#define LUN_STATUS_0_REG 0x128
> +#define LUN_STATUS_1_REG 0x12C
> +#define TIMINGS_TOGGLE_REG 0x130
> +#define TIME_GEN_SEQ_3_REG 0x134
> +#define SQS_DELAY_REG 0x138
> +#define CNE_MASK_REG 0x13C
> +#define CNE_VAL_REG 0x140
> +#define CNA_CTRL_REG 0x144
> +#define INTERNAL_STATUS_REG 0x148
> +#define ECC_CNT_REG 0x14C
> +#define PARAM_REG_REG 0x150
> +
> +/* NAND flash command generation */
> +
> +/* NAND flash command codes */
> +#define NAND_RESET 0xff
> +#define NAND_READ_STATUS 0x70
> +#define NAND_READ_ID 0x90
> +#define NAND_READ_ID_ADDR_STD 0x00 /* address written to ADDR0_COL */
> +#define NAND_READ_ID_ADDR_ONFI 0x20 /* address written to ADDR0_COL */
> +#define NAND_READ_ID_ADDR_JEDEC 0x40 /* address written to ADDR0_COL */
> +#define NAND_PARAM 0xEC
> +#define NAND_PARAM_SIZE_MAX 768 /* bytes */
> +#define NAND_PAGE_READ 0x00
> +#define NAND_PAGE_READ_END 0x30
> +#define NAND_BLOCK_ERASE 0x60
> +#define NAND_BLOCK_ERASE_END 0xd0
> +#define NAND_PAGE_WRITE 0x80
> +#define NAND_PAGE_WRITE_END 0x10
> +
> +#define _DONT_CARE 0x00 /* When we don't have anything better to say */
> +
> +
> +/* Assembled values for putting into COMMAND register */
> +
> +/* Reset NAND flash */
> +
> +/* Uses SEQ_0: non-directional sequence, single command, wait for ready */
> +#define COMMAND_RESET \
> + MAKE_COMMAND(_SEQ_0, INPUT_SEL_SIU, DATA_SEL_FIFO, \
> + NAND_RESET, _DONT_CARE, _DONT_CARE)
> +
> +/* Read status */
> +
> +/* Uses SEQ_4: single command, then read data via DATA_REG */
> +#define COMMAND_READ_STATUS \
> + MAKE_COMMAND(_SEQ_4, INPUT_SEL_SIU, DATA_SEL_DATA_REG, \
> + NAND_READ_STATUS, _DONT_CARE, _DONT_CARE)
> +
> +/* Read ID */
> +
> +/* Uses SEQ_1: single command, ADDR0_COL, then read data via FIFO */
> +/* ADDR0_COL is set to NAND_READ_ID_ADDR_STD for non-ONFi, and
> + * NAND_READ_ID_ADDR_ONFI for ONFi.
> + * The controller reads 5 bytes in the non-ONFi case, and 4 bytes in the
> + * ONFi case, so the data reception (DMA or FIFO_REG) needs to be set up
> + * accordingly.
> + */
> +#define COMMAND_READ_ID \
> + MAKE_COMMAND(_SEQ_1, INPUT_SEL_DMA, DATA_SEL_FIFO, \
> + NAND_READ_ID, _DONT_CARE, _DONT_CARE)
> +
> +#define COMMAND_PARAM \
> + MAKE_COMMAND(_SEQ_2, INPUT_SEL_DMA, DATA_SEL_FIFO, \
> + NAND_PARAM, _DONT_CARE, _DONT_CARE)
> +
> +/* Page read via slave interface (FIFO_DATA register) */
> +
> +/* Standard 5-cycle read command, with 0x30 end-of-cycle marker */
> +/* Uses SEQ_10: CMD0 + 5 address cycles + CMD2, read data */
> +#define COMMAND_READ_PAGE_STD \
> + MAKE_COMMAND(_SEQ_10, INPUT_SEL_SIU, DATA_SEL_FIFO, \
> + NAND_PAGE_READ, _DONT_CARE, NAND_PAGE_READ_END)
> +
> +/* 4-cycle read command, together with GEN_SEQ_CTRL_READ_PAGE_4CYCLE */
> +/* Uses SEQ_18 (generic command sequence, see GEN_SEQ_ECTRL_READ_PAGE_4CYCLE)):
> + * CMD0 + 2+2 address cycles + CMD2, read data
> + */
> +#define COMMAND_READ_PAGE_GEN \
> + MAKE_COMMAND(_SEQ_18, INPUT_SEL_SIU, DATA_SEL_FIFO, \
> + NAND_PAGE_READ, _DONT_CARE, NAND_PAGE_READ_END)
> +
> +/* Page read via master interface (DMA) */
> +
> +/* Standard 5-cycle read command, with 0x30 end-of-cycle marker */
> +/* Uses SEQ_10: CMD0 + 5 address cycles + CMD2, read data */
> +#define COMMAND_READ_PAGE_DMA_STD \
> + MAKE_COMMAND(_SEQ_10, INPUT_SEL_DMA, DATA_SEL_FIFO, \
> + NAND_PAGE_READ, _DONT_CARE, NAND_PAGE_READ_END)
> +
> +/* 4-cycle read command, together with GEN_SEQ_CTRL_READ_PAGE_4CYCLE */
> +/* Uses SEQ_18 (generic command sequence, see GEN_SEQ_ECTRL_READ_PAGE_4CYCLE)):
> + * CMD0 + 2+2 address cycles + CMD2, read data
> + */
> +#define COMMAND_READ_PAGE_DMA_GEN \
> + MAKE_COMMAND(_SEQ_18, INPUT_SEL_DMA, DATA_SEL_FIFO, \
> + NAND_PAGE_READ, _DONT_CARE, NAND_PAGE_READ_END)
> +
> +/* Page write via master interface (DMA) */
> +
> +/* Uses SEQ_12: CMD0 + 5 address cycles + write data + CMD1 */
> +#define COMMAND_WRITE_PAGE_DMA_STD \
> + MAKE_COMMAND(_SEQ_12, INPUT_SEL_DMA, DATA_SEL_FIFO, \
> + NAND_PAGE_WRITE, NAND_PAGE_WRITE_END, _DONT_CARE)
> +
> +/* Uses SEQ_19: CMD0 + 4 address cycles + write data + CMD1 */
> +#define COMMAND_WRITE_PAGE_DMA_GEN \
> + MAKE_COMMAND(_SEQ_19, INPUT_SEL_DMA, DATA_SEL_FIFO, \
> + NAND_PAGE_WRITE, NAND_PAGE_WRITE_END, _DONT_CARE)
> +
> +/* Block erase */
> +
> +/* Uses SEQ_14: CMD0 + 3 address cycles + CMD1 */
> +#define COMMAND_BLOCK_ERASE \
> + MAKE_COMMAND(_SEQ_14, INPUT_SEL_SIU, DATA_SEL_FIFO, \
> + NAND_BLOCK_ERASE, NAND_BLOCK_ERASE_END, _DONT_CARE)
> +
> +/* Assembled values for putting into GEN_SEQ_CTRL register */
> +
> +/* General command sequence specification for 4 cycle PAGE_READ command */
> +#define GEN_SEQ_CTRL_READ_PAGE_4CYCLE \
> + MAKE_GEN_CMD(1, 0, 1, 0, /* enable command 0 and 2 phases */ \
> + 2, 2, /* col A0 2 cycles, row A0 2 cycles */ \
> + 0, 0, /* col A1, row A1 not used */ \
> + 1, /* data phase enabled */ \
> + _BUSY_0, /* busy0 phase enabled */ \
> + 0, /* immediate cmd execution disabled */ \
> + _DONT_CARE) /* command 3 code not needed */
> +
> +/* General command sequence specification for 4 cycle PAGE_PROGRAM command */
> +#define GEN_SEQ_CTRL_WRITE_PAGE_4CYCLE \
> + MAKE_GEN_CMD(1, 1, 0, 0, /* enable command 0 and 1 phases */ \
> + 2, 2, /* col A0 2 cycles, row A0 2 cycles */ \
> + 0, 0, /* col A1, row A1 not used */ \
> + 1, /* data phase enabled */ \
> + _BUSY_1, /* busy1 phase enabled */ \
> + 0, /* immediate cmd execution disabled */ \
> + _DONT_CARE) /* command 3 code not needed */
I think I already commented on that last time a driver for the same IP
was submitted by Oleksij.
I'm pretty sure you can implement ->cmd_ctrl() and rely on the default
cmdfunc() logic instead of manually converting those high-level NAND
commands into your own model (which seems to match pretty much the
->cmd_ctrl() model, where you can get the number of address and command
cycles).
Maybe I'm wrong, but I think it's worth a try, and if it works, it
would greatly simplify the driver.
> +
> +/* BCH ECC size calculations. */
> +/* From "Mr. NAND's Wild Ride: Warning: Suprises Ahead", by Robert Pierce,
> + * Denali Software Inc. 2009, table on page 5
> + */
> +/* Use 8 bit correction as base. */
> +#define ECC8_BYTES(BLKSIZE) (ffs(BLKSIZE) + 3)
> +/* The following would be valid for 4..24 bits of correction. */
> +#define ECC_BYTES_PACKED(CAP, BLKSIZE) ((ECC8_BYTES(BLKSIZE) * (CAP) + 7) / 8)
> +/* Our hardware however requires more bytes than strictly necessary due to
> + * the internal design.
> + */
> +#define ECC_BYTES(CAP, BLKSIZE) ((ECC8_BYTES(1024) * (CAP) + 7) / 8)
> +
> +/* Read modes */
> +enum nfc_read_mode {
> + NFC_READ_STD, /* Standard page read with ECC */
> + NFC_READ_RAW, /* Raw mode read of main area without ECC */
> + NFC_READ_OOB, /* Read oob only (no ECC) */
> + NFC_READ_ALL /* Read main+oob in raw mode (no ECC) */
> +};
> +
> +/* Timing parameters, from DT */
> +struct nfc_timings {
> + uint32_t time_seq_0;
> + uint32_t time_seq_1;
> + uint32_t timings_asyn;
> + uint32_t time_gen_seq_0;
> + uint32_t time_gen_seq_1;
> + uint32_t time_gen_seq_2;
> + uint32_t time_gen_seq_3;
> +};
> +
> +/* Configuration, from DT */
> +struct nfc_setup {
> + struct nfc_timings timings;
> + bool use_bank_select; /* CE selects 'bank' rather than 'chip' */
> + bool rb_wired_and; /* Ready/busy wired AND rather than per-chip */
> +};
> +
> +/* DMA buffer, from both software (buf) and hardware (phys) perspective. */
> +struct nfc_dma {
> + void *buf; /* mapped address */
> + dma_addr_t phys; /* physical address */
> + int bytes_left; /* how much data left to read from buffer? */
> + int buf_bytes; /* how much allocated data in the buffer? */
> + uint8_t *ptr; /* work pointer */
> +};
> +
> +#ifndef POLLED_XFERS
> +/* Interrupt management */
> +struct nfc_irq {
> + int done; /* interrupt triggered, consequently we're done. */
> + uint32_t int_status; /* INT_STATUS at time of interrupt */
> + wait_queue_head_t wq; /* For waiting on controller interrupt */
> +};
> +#endif
> +
> +/* Information common to all chips, including the NANDFLASH-CTRL IP */
> +struct nfc_info {
Should inherit from nand_hw_ctrl (see the sunxi_nand driver)...
> + void __iomem *regbase;
> + struct device *dev;
> + struct nand_hw_control *controller;
... and not point to it.
> + spinlock_t lock;
> + struct nfc_setup *setup;
> + struct nfc_dma dma;
> +#ifndef POLLED_XFERS
> + struct nfc_irq irq;
> +#endif
> +};
> +
> +/* Per-chip controller configuration */
> +struct nfc_config {
> + uint32_t mem_ctrl;
> + uint32_t control;
> + uint32_t ecc_ctrl;
> + uint32_t mem_status_mask;
> + uint32_t cs;
> +};
> +
> +/* Cache for info that we need to save across calls to nfc_command */
> +struct nfc_cmd_cache {
> + unsigned int command;
> + int page;
> + int column;
> + int write_size;
> + int oob_required;
> + int write_raw;
> +};
> +
> +/* Information for each physical NAND chip. */
> +struct chip_info {
> + struct nand_chip chip;
> + struct nfc_cmd_cache cmd_cache;
> + struct nfc_config nfc_config;
> +};
> +
> +/* What we tell mtd is an mtd_info actually is a complete chip_info */
> +#define TO_CHIP_INFO(mtd) ((struct chip_info *)(mtd_to_nand(mtd)))
Ouch! Please, don't do that, container_of() is here for a good reason.
And prefer static inline functions over macros for this kind of things.
static inline struct chip_info *mtd_to_chip_info(struct mtd_info *mtd)
{
return container_of(mtd_to_nand(mtd), struct chip_info, chip);
}
> +
> +/* This is a global pointer, as we only support one single instance of the NFC.
> + * For multiple instances, we would need to add nfc_info as a parameter to
> + * several functions, as well as adding it as a member of the chip_info struct.
> + * Since most likely a system would only have one NFC instance, we don't
> + * go all the way implementing that feature now.
> + */
> +static struct nfc_info *nfc_info;
Come on! Don't be so lazy, do the right thing.
> +
> +/* The timing setup is expected to come via DT. We keep some default timings
> + * here for reference, based on a 100 MHz reference clock.
> + */
> +
> +static const struct nfc_timings default_mode0_pll_enabled = {
> + 0x0d151533, 0x000b0515, 0x00000046,
> + 0x00150000, 0x00000000, 0x00000005, 0x00000015 };
Can you explain those magic values?
> +
> +/**** Utility routines. */
Please use regular comments: /* */
> +
> +/* Count the number of 0's in buff up to a max of max_bits */
> +/* Used to determine how many bitflips there are in an allegedly erased block */
> +static int count_zero_bits(uint8_t *buff, int size, int max_bits)
> +{
> + int k, zero_bits = 0;
> +
> + for (k = 0; k < size; k++) {
> + zero_bits += hweight8(~buff[k]);
> + if (zero_bits > max_bits)
> + break;
> + }
> +
> + return zero_bits;
> +}
We have an helper for that [1].
> +
> +/**** Low level stuff. Read and write registers, interrupt routine, etc. */
Ditto.
> +
> +/* Read and write NFC SFR registers */
> +
> +static uint32_t nfc_read(uint reg_offset)
> +{
> + return readl_relaxed(nfc_info->regbase + reg_offset);
> +}
> +
> +static void nfc_write(uint32_t data, uint reg_offset)
> +{
> + /* Note: According to NANDFLASH-CTRL Design Specification, rev 1.14,
> + * p19, the NFC SFR's can only be written when STATUS.CTRL_STAT is 0.
> + * However, this doesn't seem to be an issue in practice.
> + */
> + writel_relaxed(data, nfc_info->regbase + reg_offset);
> +}
Do you really want to use the _relaxed functions?
> +
> +#ifndef POLLED_XFERS
> +static irqreturn_t nfc_irq(int irq, void *device_info)
> +{
> + /* Note that device_info = nfc_info, so if we don't want a global
> + * nfc_info we can get it via device_info.
> + */
> +
> + /* Save interrupt status in case caller wants to check what actually
> + * happened.
> + */
> + nfc_info->irq.int_status = nfc_read(INT_STATUS_REG);
> +
> + MTD_TRACE("Got interrupt %d, INT_STATUS 0x%08x\n",
> + irq, nfc_info->irq.int_status);
> +
> + /* Note: We can't clear the interrupts by clearing CONTROL.INT_EN,
> + * as that does not disable the interrupt output port from the
> + * nfc towards the gic.
> + */
> + nfc_write(0, INT_STATUS_REG);
> +
> + nfc_info->irq.done = 1;
> + wake_up(&nfc_info->irq.wq);
> +
> + return IRQ_HANDLED;
> +}
> +#endif
Remove the #ifndef section, since it's always activated (see my first
comment).
> +
> +/* Get resources from platform: register bank mapping, irqs, etc */
> +static int nfc_init_resources(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct resource *resource;
> +#ifndef POLLED_XFERS
> + int irq;
> + int res;
> +#endif
Ditto.
> +
> + /* Register base for controller, ultimately from device tree */
> + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!resource) {
> + dev_err(dev, "No register addresses configured!\n");
> + return -ENOMEM;
> + }
> + nfc_info->regbase = devm_ioremap_resource(dev, resource);
> + if (IS_ERR(nfc_info->regbase))
> + return PTR_ERR(nfc_info->regbase);
> +
> + dev_dbg(dev, "Got SFRs at phys %p..%p, mapped to %p\n",
> + (void *)resource->start, (void *)resource->end,
> + nfc_info->regbase);
> +
> + /* A DMA buffer */
> + nfc_info->dma.buf =
> + dma_alloc_coherent(dev, DMA_BUF_SIZE,
> + &nfc_info->dma.phys, GFP_KERNEL);
> + if (nfc_info->dma.buf == NULL) {
> + dev_err(dev, "dma_alloc_coherent failed!\n");
> + return -ENOMEM;
> + }
> +
> + dev_dbg(dev, "DMA buffer %p at physical %p\n",
> + nfc_info->dma.buf, (void *)nfc_info->dma.phys);
> +
> +#ifndef POLLED_XFERS
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(dev, "No irq configured\n");
> + return irq;
> + }
> + res = request_irq(irq, nfc_irq, 0, "evatronix-nand", nfc_info);
devm_?
> + if (res < 0) {
> + dev_err(dev, "request_irq failed\n");
> + return res;
> + }
> + dev_dbg(dev, "Successfully registered IRQ %d\n", irq);
> +#endif
> +
> + return 0;
> +}
I'm stopping there for now.
Can you please remove everything that is not strictly required and
clean the things I pointed before submitting a new version.
To be honest, your driver seems really complicated compared to what
it's supposed to do, and I suspect it could be a lot simpler, but
again, maybe I'm wrong.
If you didn't try yet, please investigate the ->cmd_ctrl() approach,
and if you did, could you explain why it didn't work out?
Regards,
Boris
[1]http://lxr.free-electrons.com/source/drivers/mtd/nand/nand_base.c#L1183
--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com