Re: [PATCH V4] mtd: nand: add Loongson1 NAND driver

From: Boris Brezillon
Date: Wed Nov 02 2016 - 03:36:27 EST


On Wed, 2 Nov 2016 09:52:06 +0800
Keguang Zhang <keguang.zhang@xxxxxxxxx> wrote:

> From: Kelvin Cheung <keguang.zhang@xxxxxxxxx>
>
> This patch adds NAND driver for Loongson1B.
>
> Signed-off-by: Kelvin Cheung <keguang.zhang@xxxxxxxxx>
>
> ---
> v4:
> Retrieve the controller from nand_hw_control.
> v3:
> Replace __raw_readl/__raw_writel with readl/writel.
> Split ls1x_nand into two structures: ls1x_nand_chip and
> ls1x_nand_controller.
> V2:
> Modify the dependency in Kconfig due to the changes of DMA
> module.
> ---
> drivers/mtd/nand/Kconfig | 8 +
> drivers/mtd/nand/Makefile | 1 +
> drivers/mtd/nand/loongson1_nand.c | 571 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 580 insertions(+)
> create mode 100644 drivers/mtd/nand/loongson1_nand.c
>

[...]

> +static void ls1x_nand_cmdfunc(struct mtd_info *mtd, unsigned int command,
> + int column, int page_addr)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + struct ls1x_nand_controller *nandc =
> + to_ls1x_nand_controller(chip->controller);
> +
> + dev_dbg(mtd->dev.parent, "cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n",
> + command, column, page_addr);
> +
> + if (command == NAND_CMD_RNDOUT) {
> + nandc->buf_off = column;
> + return;
> + }
> +
> + /*set address, buffer length and buffer offset */
> + if (column != -1 || page_addr != -1)
> + set_addr_len(mtd, command, column, page_addr);
> +
> + /*prepare NAND command */
> + switch (command) {
> + case NAND_CMD_RESET:
> + nandc->cmd_ctrl = CMD_RESET;
> + break;
> + case NAND_CMD_STATUS:
> + nandc->cmd_ctrl = CMD_STATUS;
> + break;
> + case NAND_CMD_READID:
> + nandc->cmd_ctrl = CMD_READID;
> + break;
> + case NAND_CMD_READ0:
> + nandc->cmd_ctrl = OP_SPARE | OP_MAIN | CMD_READ;
> + break;
> + case NAND_CMD_READOOB:
> + nandc->cmd_ctrl = OP_SPARE | CMD_READ;
> + break;
> + case NAND_CMD_ERASE1:
> + nandc->cmd_ctrl = CMD_ERASE;
> + break;
> + case NAND_CMD_PAGEPROG:
> + break;
> + case NAND_CMD_SEQIN:
> + if (column < mtd->writesize)
> + nandc->cmd_ctrl = OP_SPARE | OP_MAIN | CMD_WRITE;
> + else
> + nandc->cmd_ctrl = OP_SPARE | CMD_WRITE;
> + default:
> + return;
> + }

Sorry, but I'm still convinced this driver does not fit in the NAND
subsystem, or at least not the one we have now.

You're just supporting a limited number of the commands a raw NAND can
support (for example, no SET_FEATURE support, which is required for
ONFI NANDs), and your ->cmdfunc() implementation clearly shows that
you're converting all low-level commands into high-level ones.

Can you try to implement the mtd_info hooks directly? This only things
you'll miss are the nand_ids table and the BBT code, but I guess this is
something we can expose so that you don't have to re-use it.

Of course, you'll also have to convert absolute addresses into
page/block numbers, but that's not the hard part of the job.

You may also want to have a look at [1]. I started to abstract away the
NAND interface type to share some code between SPI NANDs and raw NANDs.
By implementing this interface, you'll at least be able to re-use the
BBT code.

I'm really sorry to ask you that now, after you've reworked most of the
driver to address my comments, but the more I look at it the more I
feel it's just a big hack to make it fit in a framework that was not
designed to support such "high-level" controllers.

Regards,

Boris

> +
> + /*set NAND command */
> + set_cmd(nandc, nandc->cmd_ctrl);
> + /*trigger NAND operation */
> + start_nand(nandc);
> + /*trigger DMA for R/W operation */
> + if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB)
> + start_dma(mtd, nandc->buf_len, false);
> + else if (command == NAND_CMD_PAGEPROG)
> + start_dma(mtd, nandc->buf_len, true);
> + nand_wait_ready(mtd);
> +
> + if (command == NAND_CMD_STATUS) {
> + nandc->datareg[0] = (char)(nand_readl(nandc, NAND_STATUS) >> 8);
> + /*work around hardware bug for invalid STATUS */
> + nandc->datareg[0] |= 0xc0;
> + nandc->data_ptr = nandc->datareg;
> + } else if (command == NAND_CMD_READID) {
> + nandc->datareg[0] = (char)(nand_readl(nandc, NAND_IDH));
> + nandc->datareg[1] = (char)(nand_readl(nandc, NAND_IDL) >> 24);
> + nandc->datareg[2] = (char)(nand_readl(nandc, NAND_IDL) >> 16);
> + nandc->datareg[3] = (char)(nand_readl(nandc, NAND_IDL) >> 8);
> + nandc->datareg[4] = (char)(nand_readl(nandc, NAND_IDL));
> + nandc->data_ptr = nandc->datareg;
> + }
> +
> + nandc->cmd_ctrl = 0;
> +}

[1]http://lkml.iu.edu/hypermail/linux/kernel/1610.2/00066.html