From mboxrd@z Thu Jan 1 00:00:00 1970 From: Lubomir Rintel Date: Tue, 17 Nov 2020 22:00:12 +0100 Subject: [PATCH RFC 14/20] nand/raw: Add Ingenic JZ4730 NAND flash driver In-Reply-To: <20201117210018.751469-1-lkundrak@v3.sk> References: <20201117210018.751469-1-lkundrak@v3.sk> Message-ID: <20201117210018.751469-15-lkundrak@v3.sk> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de This adds driver for the NAND flash driver for JZ4730 SoC. Can also be used in the NAND SPL that is too constrained (needs to fit in 4K) to be DT-driven. Signed-off-by: Lubomir Rintel --- drivers/mtd/nand/raw/Kconfig | 10 ++ drivers/mtd/nand/raw/Makefile | 1 + drivers/mtd/nand/raw/jz4730_nand.c | 212 +++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 drivers/mtd/nand/raw/jz4730_nand.c diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 3cf3b14f05b..be6146a71f2 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -121,6 +121,16 @@ config NAND_DENALI_DT Enable the driver for NAND flash on platforms using a Denali NAND controller as a DT device. +config NAND_JZ4730 + bool "Support JZ4730 NAND controller" + select SYS_NAND_SELF_INIT + depends on DM_MTD && SOC_JZ4730 + imply CMD_NAND + default y + help + Enable this driver for NAND flash controllers available in + Ingenic JZ4730 SoCs. + config NAND_LPC32XX_SLC bool "Support LPC32XX_SLC controller" help diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 24c51b6924a..8a4b18728eb 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o obj-$(CONFIG_NAND_FSMC) += fsmc_nand.o +obj-$(CONFIG_NAND_JZ4730) += jz4730_nand.o obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o diff --git a/drivers/mtd/nand/raw/jz4730_nand.c b/drivers/mtd/nand/raw/jz4730_nand.c new file mode 100644 index 00000000000..aa4631971fc --- /dev/null +++ b/drivers/mtd/nand/raw/jz4730_nand.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * JZ4730 NAND flash driver. + * + * Copyright (c) 2007 Ingenic Semiconductor Inc. + * Author: + * + * Copyright (C) 2020 Lubomir Rintel + */ + +#include +#include +#include +#include + +#include +#include + +#define EMC_SMCR3 0x1c +#define EMC_NFCSR 0x50 +#define EMC_NFECC 0x54 + +#define EMC_NFCSR_NFE BIT(0) +#define EMC_NFCSR_FCE BIT(1) +#define EMC_NFCSR_ECCE BIT(2) +#define EMC_NFCSR_ERST BIT(3) +#define EMC_NFCSR_RB BIT(7) + +struct jz4730_nand_priv { + void __iomem *base; + struct nand_chip nand; +}; + +static inline void __iomem *mtd_to_base(struct mtd_info *mtd); + +static void jz4730_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, uint ctrl) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + void __iomem *base = mtd_to_base(mtd); + unsigned long IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W = (unsigned long)nand->IO_ADDR_W; + + if (ctrl & NAND_NCE) + setbits_32(base + EMC_NFCSR, EMC_NFCSR_FCE); + else + clrbits_32(base + EMC_NFCSR, EMC_NFCSR_FCE); + + IO_ADDR_W &= ~(BIT(18) | BIT(19)); + if (ctrl & NAND_CLE) + IO_ADDR_W |= BIT(18); + if (ctrl & NAND_ALE) + IO_ADDR_W |= BIT(19); + + nand->IO_ADDR_W = (void *)IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, nand->IO_ADDR_W); +} + +static int jz4730_nand_dev_ready(struct mtd_info *mtd) +{ + void __iomem *base = mtd_to_base(mtd); + + return (readl(base + EMC_NFCSR) & EMC_NFCSR_RB) ? 1 : 0; +} + +static void jz4730_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + unsigned long IO_ADDR_R = (unsigned long)nand->IO_ADDR_R; + unsigned long IO_ADDR_W = (unsigned long)nand->IO_ADDR_W; + + if (chip == 0) { + IO_ADDR_R &= ~(BIT(16) | BIT(17)); + IO_ADDR_W &= ~(BIT(16) | BIT(17)); + } else if (chip == 1) { + IO_ADDR_R |= (BIT(16) | BIT(17)); + IO_ADDR_W |= (BIT(16) | BIT(17)); + } + + nand->IO_ADDR_R = (void *)IO_ADDR_R; + nand->IO_ADDR_W = (void *)IO_ADDR_W; +} + +static void jz4730_nand_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + void __iomem *base = mtd_to_base(mtd); + + setbits_32(base + EMC_NFCSR, EMC_NFCSR_ECCE | EMC_NFCSR_ERST); +} + +static int jz4730_nand_ecc_calculate(struct mtd_info *mtd, + const u_char *dat, u_char *ecc) +{ + void __iomem *base = mtd_to_base(mtd); + u32 val = readl(base + EMC_NFECC); + + clrbits_32(base + EMC_NFCSR, EMC_NFCSR_ECCE); + val = readl(base + EMC_NFECC); + val = ~val | 0x00030000; + + ecc[0] = val >> 8; + ecc[1] = val; + ecc[2] = val >> 16; + + return 0; +} + +void jz4730_nand_chip_init(struct nand_chip *nand) +{ + struct mtd_info *mtd = nand_to_mtd(nand); + void __iomem *base = mtd_to_base(mtd); + + if (!IS_ENABLED(SPL_BUILD) || IS_ENABLED(SPL_NAND_ECC)) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.hwctl = jz4730_nand_ecc_hwctl; + nand->ecc.calculate = jz4730_nand_ecc_calculate; + nand->ecc.correct = nand_correct_data; + nand->ecc.bytes = 3; + } + + nand->cmd_ctrl = jz4730_nand_cmd_ctrl; + nand->dev_ready = jz4730_nand_dev_ready; + nand->select_chip = jz4730_nand_select_chip; + nand->IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE; + nand->IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; + nand->chip_delay = 20; + + /* Enable the NAND functionality. */ + setbits_32(base + EMC_NFCSR, EMC_NFCSR_NFE); + + /* Nobody knows what does this do. */ + writel(0x06644400, base + EMC_SMCR3); +} + +#if !defined(CONFIG_SPL_BUILD) + +static inline void __iomem *mtd_to_base(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + + return container_of(nand, struct jz4730_nand_priv, nand)->base; +} + +static int jz4730_nand_probe(struct udevice *dev) +{ + struct jz4730_nand_priv *priv = dev_get_priv(dev); + struct nand_chip *nand = &priv->nand; + struct mtd_info *mtd = nand_to_mtd(nand); + ofnode node; + int ret; + + priv->base = dev_remap_addr(dev->parent); + if (!priv->base) + return -EINVAL; + + jz4730_nand_chip_init(nand); + + ofnode_for_each_subnode(node, dev->node) + nand_set_flash_node(nand, node); + + ret = nand_scan(mtd, CONFIG_SYS_NAND_MAX_CHIPS); + if (ret) + return ret; + + return nand_register(0, mtd); +} + +static const struct udevice_id jz4730_nand_dt_ids[] = { + { .compatible = "ingenic,jz4730-nand", }, + { } +}; + +U_BOOT_DRIVER(jz4730_nand_dt) = { + .name = "jz4730-nand", + .id = UCLASS_MTD, + .of_match = jz4730_nand_dt_ids, + .probe = jz4730_nand_probe, + .priv_auto_alloc_size = sizeof(struct jz4730_nand_priv), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_GET_DRIVER(jz4730_nand_dt), + &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize %s. (error %d)\n", dev->name, ret); +} + +#else /* CONFIG_SPL_BUILD */ + +static inline void __iomem *mtd_to_base(struct mtd_info *mtd) +{ + return (void __iomem *)KSEG1 + 0x13010000; +} + +int board_nand_init(struct nand_chip *nand) +{ + jz4730_nand_chip_init(nand); + nand->read_buf = nand_read_buf; + + return 0; +} + +#endif /* CONFIG_SPL_BUILD */ -- 2.28.0