diff options
author | Boris Brezillon <boris.brezillon@collabora.com> | 2019-02-25 09:28:54 +0100 |
---|---|---|
committer | Boris Brezillon <boris.brezillon@collabora.com> | 2019-02-25 09:28:54 +0100 |
commit | 9220d7befc9c28bf714701e6e26163644750d871 (patch) | |
tree | 10eabdf3fb0a962b37ed4a04ece1f48795f9f181 | |
parent | dfbd39956a979029d779d47855ef84834dd4b203 (diff) | |
parent | 53bcbb839438df54024d97e8e698d21329d2c9a0 (diff) |
Merge tag 'nand/for-5.1' of git://git.infradead.org/linux-mtd into mtd/next
NAND core changes:
- Fourth batch of fixes/cleanup to the raw NAND core impacting various
controller drivers (Sunxi, Marvell, MTK, TMIO, OMAP2).
- Checking the return code of nand_reset() and nand_readid_op().
- Removing ->legacy.erase and single_erase().
- Simplifying the locking.
- Several implicit fall through annotations.
Raw NAND controllers drivers changes:
- Fixing various possible object reference leaks (MTK, JZ4780, Atmel).
- ST:
* Adding support for STM32 FMC2 NAND flash controller.
- Meson:
* Adding support for Amlogic NAND flash controller.
- Denali:
* Several cleanup patches.
- Sunxi:
* Several cleanup patches.
- FSMC:
* Disabling NAND on remove().
* Resetting NAND timings on resume().
SPI-NAND drivers changes:
- Toshiba:
* Adding support for all Toshiba products.
- Macronix:
* Fixing ECC status read.
- Gigadevice:
* Adding support for GD5F1GQ4UExxG.
26 files changed, 4380 insertions, 570 deletions
diff --git a/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt new file mode 100644 index 000000000000..3983c11e062c --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt @@ -0,0 +1,60 @@ +Amlogic NAND Flash Controller (NFC) for GXBB/GXL/AXG family SoCs + +This file documents the properties in addition to those available in +the MTD NAND bindings. + +Required properties: +- compatible : contains one of: + - "amlogic,meson-gxl-nfc" + - "amlogic,meson-axg-nfc" +- clocks : + A list of phandle + clock-specifier pairs for the clocks listed + in clock-names. + +- clock-names: Should contain the following: + "core" - NFC module gate clock + "device" - device clock from eMMC sub clock controller + "rx" - rx clock phase + "tx" - tx clock phase + +- amlogic,mmc-syscon : Required for NAND clocks, it's shared with SD/eMMC + controller port C + +Optional children nodes: +Children nodes represent the available nand chips. + +Other properties: +see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings. + +Example demonstrate on AXG SoC: + + sd_emmc_c_clkc: mmc@7000 { + compatible = "amlogic,meson-axg-mmc-clkc", "syscon"; + reg = <0x0 0x7000 0x0 0x800>; + }; + + nand-controller@7800 { + compatible = "amlogic,meson-axg-nfc"; + reg = <0x0 0x7800 0x0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 34 IRQ_TYPE_EDGE_RISING>; + + clocks = <&clkc CLKID_SD_EMMC_C>, + <&sd_emmc_c_clkc CLKID_MMC_DIV>, + <&sd_emmc_c_clkc CLKID_MMC_PHASE_RX>, + <&sd_emmc_c_clkc CLKID_MMC_PHASE_TX>; + clock-names = "core", "device", "rx", "tx"; + amlogic,mmc-syscon = <&sd_emmc_c_clkc>; + + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins>; + + nand@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + + nand-on-flash-bbt; + }; + }; diff --git a/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt new file mode 100644 index 000000000000..ad2bef826582 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt @@ -0,0 +1,61 @@ +STMicroelectronics Flexible Memory Controller 2 (FMC2) +NAND Interface + +Required properties: +- compatible: Should be one of: + * st,stm32mp15-fmc2 +- reg: NAND flash controller memory areas. + First region contains the register location. + Regions 2 to 4 respectively contain the data, command, + and address space for CS0. + Regions 5 to 7 contain the same areas for CS1. +- interrupts: The interrupt number +- pinctrl-0: Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt) +- clocks: The clock needed by the NAND flash controller + +Optional properties: +- resets: Reference to a reset controller asserting the FMC controller +- dmas: DMA specifiers (see: dma/stm32-mdma.txt) +- dma-names: Must be "tx", "rx" and "ecc" + +* NAND device bindings: + +Required properties: +- reg: describes the CS lines assigned to the NAND device. + +Optional properties: +- nand-on-flash-bbt: see nand.txt +- nand-ecc-strength: see nand.txt +- nand-ecc-step-size: see nand.txt + +The following ECC strength and step size are currently supported: + - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Hamming) + - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4) + - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8) (default) + +Example: + + fmc: nand-controller@58002000 { + compatible = "st,stm32mp15-fmc2"; + reg = <0x58002000 0x1000>, + <0x80000000 0x1000>, + <0x88010000 0x1000>, + <0x88020000 0x1000>, + <0x81000000 0x1000>, + <0x89010000 0x1000>, + <0x89020000 0x1000>; + interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rcc FMC_K>; + resets = <&rcc FMC_R>; + pinctrl-names = "default"; + pinctrl-0 = <&fmc_pins_a>; + #address-cells = <1>; + #size-cells = <0>; + + nand@0 { + reg = <0>; + nand-on-flash-bbt; + #address-cells = <1>; + #size-cells = <1>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 5af03bbfc46e..0a516c843e77 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9849,6 +9849,13 @@ F: drivers/media/platform/meson/ao-cec.c F: Documentation/devicetree/bindings/media/meson-ao-cec.txt T: git git://linuxtv.org/media_tree.git +MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS +M: Liang Yang <liang.yang@amlogic.com> +L: linux-mtd@lists.infradead.org +S: Maintained +F: drivers/mtd/nand/raw/meson_* +F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt + MICROBLAZE ARCHITECTURE M: Michal Simek <monstr@monstr.eu> W: http://www.monstr.eu/fdt/ diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 1a55d3e3d4c5..e604625e2dfa 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -541,4 +541,21 @@ config MTD_NAND_TEGRA is supported. Extra OOB bytes when using HW ECC are currently not supported. +config MTD_NAND_STM32_FMC2 + tristate "Support for NAND controller on STM32MP SoCs" + depends on MACH_STM32MP157 || COMPILE_TEST + help + Enables support for NAND Flash chips on SoCs containing the FMC2 + NAND controller. This controller is found on STM32MP SoCs. + The controller supports a maximum 8k page size and supports + a maximum 8-bit correction error per sector of 512 bytes. + +config MTD_NAND_MESON + tristate "Support for NAND controller on Amlogic's Meson SoCs" + depends on ARCH_MESON || COMPILE_TEST + select MFD_SYSCON + help + Enables support for NAND controller on Amlogic's Meson SoCs. + This controller is found on Meson SoCs. + endif # MTD_NAND diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 57159b349054..5a5a72f0793e 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -56,6 +56,8 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o +obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o +obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o nand-objs += nand_onfi.o diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c index 555a74e15269..9d3997840889 100644 --- a/drivers/mtd/nand/raw/atmel/pmecc.c +++ b/drivers/mtd/nand/raw/atmel/pmecc.c @@ -876,23 +876,32 @@ static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev, { struct platform_device *pdev; struct atmel_pmecc *pmecc, **ptr; + int ret; pdev = of_find_device_by_node(np); - if (!pdev || !platform_get_drvdata(pdev)) + if (!pdev) return ERR_PTR(-EPROBE_DEFER); + pmecc = platform_get_drvdata(pdev); + if (!pmecc) { + ret = -EPROBE_DEFER; + goto err_put_device; + } ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - get_device(&pdev->dev); - pmecc = platform_get_drvdata(pdev); + if (!ptr) { + ret = -ENOMEM; + goto err_put_device; + } *ptr = pmecc; devres_add(userdev, ptr); return pmecc; + +err_put_device: + put_device(&pdev->dev); + return ERR_PTR(ret); } static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 }; diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index eebac35304c6..40c035028640 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -37,9 +37,6 @@ #define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */ #define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */ -/* MAP10 commands */ -#define DENALI_ERASE 0x01 - #define DENALI_BANK(denali) ((denali)->active_bank << 24) #define DENALI_INVALID_BANK -1 @@ -476,7 +473,7 @@ static void denali_setup_dma32(struct denali_nand_info *denali, } static int denali_pio_read(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw) + size_t size, int page) { u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; uint32_t *buf32 = (uint32_t *)buf; @@ -504,7 +501,7 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf, } static int denali_pio_write(struct denali_nand_info *denali, - const void *buf, size_t size, int page, int raw) + const void *buf, size_t size, int page) { u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; const uint32_t *buf32 = (uint32_t *)buf; @@ -525,16 +522,16 @@ static int denali_pio_write(struct denali_nand_info *denali, } static int denali_pio_xfer(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw, int write) + size_t size, int page, int write) { if (write) - return denali_pio_write(denali, buf, size, page, raw); + return denali_pio_write(denali, buf, size, page); else - return denali_pio_read(denali, buf, size, page, raw); + return denali_pio_read(denali, buf, size, page); } static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw, int write) + size_t size, int page, int write) { dma_addr_t dma_addr; uint32_t irq_mask, irq_status, ecc_err_mask; @@ -544,7 +541,7 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, dma_addr = dma_map_single(denali->dev, buf, size, dir); if (dma_mapping_error(denali->dev, dma_addr)) { dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n"); - return denali_pio_xfer(denali, buf, size, page, raw, write); + return denali_pio_xfer(denali, buf, size, page, write); } if (write) { @@ -598,9 +595,9 @@ static int denali_data_xfer(struct denali_nand_info *denali, void *buf, denali->reg + TRANSFER_SPARE_REG); if (denali->dma_avail) - return denali_dma_xfer(denali, buf, size, page, raw, write); + return denali_dma_xfer(denali, buf, size, page, write); else - return denali_pio_xfer(denali, buf, size, page, raw, write); + return denali_pio_xfer(denali, buf, size, page, write); } static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, @@ -754,9 +751,6 @@ static int denali_read_oob(struct nand_chip *chip, int page) static int denali_write_oob(struct nand_chip *chip, int page) { struct mtd_info *mtd = nand_to_mtd(chip); - struct denali_nand_info *denali = mtd_to_denali(mtd); - - denali_reset_irq(denali); denali_oob_xfer(mtd, chip, page, 1); @@ -903,23 +897,6 @@ static int denali_waitfunc(struct nand_chip *chip) return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL; } -static int denali_erase(struct nand_chip *chip, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip)); - uint32_t irq_status; - - denali_reset_irq(denali); - - denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page, - DENALI_ERASE); - - /* wait for erase to complete or failure to occur */ - irq_status = denali_wait_for_irq(denali, - INTR__ERASE_COMP | INTR__ERASE_FAIL); - - return irq_status & INTR__ERASE_COMP ? 0 : -EIO; -} - static int denali_setup_data_interface(struct nand_chip *chip, int chipnr, const struct nand_data_interface *conf) { @@ -1244,7 +1221,6 @@ static int denali_attach_chip(struct nand_chip *chip) chip->ecc.write_page_raw = denali_write_page_raw; chip->ecc.read_oob = denali_read_oob; chip->ecc.write_oob = denali_write_oob; - chip->legacy.erase = denali_erase; ret = denali_multidev_fixup(denali); if (ret) diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h index 25c00601b8b3..c8c2620fc736 100644 --- a/drivers/mtd/nand/raw/denali.h +++ b/drivers/mtd/nand/raw/denali.h @@ -304,7 +304,6 @@ struct denali_nand_info { u32 irq_status; /* interrupts that have happened */ int irq; void *buf; /* for syndrome layout conversion */ - dma_addr_t dma_addr; int dma_avail; /* can support DMA? */ int devs_per_cs; /* devices connected in parallel */ int oob_skip_bytes; /* number of bytes reserved for BBM */ diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index 7c6a8a426606..0b5ae2418815 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -109,25 +109,17 @@ static int denali_dt_probe(struct platform_device *pdev) if (IS_ERR(denali->host)) return PTR_ERR(denali->host); - /* - * A single anonymous clock is supported for the backward compatibility. - * New platforms should support all the named clocks. - */ dt->clk = devm_clk_get(dev, "nand"); if (IS_ERR(dt->clk)) - dt->clk = devm_clk_get(dev, NULL); - if (IS_ERR(dt->clk)) { - dev_err(dev, "no clk available\n"); return PTR_ERR(dt->clk); - } dt->clk_x = devm_clk_get(dev, "nand_x"); if (IS_ERR(dt->clk_x)) - dt->clk_x = NULL; + return PTR_ERR(dt->clk_x); dt->clk_ecc = devm_clk_get(dev, "ecc"); if (IS_ERR(dt->clk_ecc)) - dt->clk_ecc = NULL; + return PTR_ERR(dt->clk_ecc); ret = clk_prepare_enable(dt->clk); if (ret) @@ -141,19 +133,8 @@ static int denali_dt_probe(struct platform_device *pdev) if (ret) goto out_disable_clk_x; - if (dt->clk_x) { - denali->clk_rate = clk_get_rate(dt->clk); - denali->clk_x_rate = clk_get_rate(dt->clk_x); - } else { - /* - * Hardcode the clock rates for the backward compatibility. - * This works for both SOCFPGA and UniPhier. - */ - dev_notice(dev, - "necessary clock is missing. default clock rates are used.\n"); - denali->clk_rate = 50000000; - denali->clk_x_rate = 200000000; - } + denali->clk_rate = clk_get_rate(dt->clk); + denali->clk_x_rate = clk_get_rate(dt->clk_x); ret = denali_init(denali); if (ret) diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 325b4414dccc..0c31089215d4 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -986,6 +986,19 @@ static const struct nand_controller_ops fsmc_nand_controller_ops = { .setup_data_interface = fsmc_setup_data_interface, }; +/** + * fsmc_nand_disable() - Disables the NAND bank + * @host: The instance to disable + */ +static void fsmc_nand_disable(struct fsmc_nand_data *host) +{ + u32 val; + + val = readl(host->regs_va + FSMC_PC); + val &= ~FSMC_ENABLE; + writel(val, host->regs_va + FSMC_PC); +} + /* * fsmc_nand_probe - Probe function * @pdev: platform device structure @@ -1141,6 +1154,7 @@ release_dma_read_chan: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->read_dma_chan); disable_clk: + fsmc_nand_disable(host); clk_disable_unprepare(host->clk); return ret; @@ -1155,6 +1169,7 @@ static int fsmc_nand_remove(struct platform_device *pdev) if (host) { nand_release(&host->nand); + fsmc_nand_disable(host); if (host->mode == USE_DMA_ACCESS) { dma_release_channel(host->write_dma_chan); @@ -1185,6 +1200,7 @@ static int fsmc_nand_resume(struct device *dev) clk_prepare_enable(host->clk); if (host->dev_timings) fsmc_nand_setup(host, host->dev_timings); + nand_reset(&host->nand, 0); } return 0; diff --git a/drivers/mtd/nand/raw/jz4780_bch.c b/drivers/mtd/nand/raw/jz4780_bch.c index 7201827809e9..c5f74ed85862 100644 --- a/drivers/mtd/nand/raw/jz4780_bch.c +++ b/drivers/mtd/nand/raw/jz4780_bch.c @@ -281,12 +281,15 @@ static struct jz4780_bch *jz4780_bch_get(struct device_node *np) struct jz4780_bch *bch; pdev = of_find_device_by_node(np); - if (!pdev || !platform_get_drvdata(pdev)) + if (!pdev) return ERR_PTR(-EPROBE_DEFER); - get_device(&pdev->dev); - bch = platform_get_drvdata(pdev); + if (!bch) { + put_device(&pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + clk_prepare_enable(bch->clk); return bch; diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 84283c6bb0ff..f38e5c1b87e4 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2550,9 +2550,8 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, } /* Alloc the nand chip structure */ - marvell_nand = devm_kzalloc(dev, sizeof(*marvell_nand) + - (nsels * - sizeof(struct marvell_nand_chip_sel)), + marvell_nand = devm_kzalloc(dev, + struct_size(marvell_nand, sels, nsels), GFP_KERNEL); if (!marvell_nand) { dev_err(dev, "could not allocate chip structure\n"); diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c new file mode 100644 index 000000000000..3e8aa71407b5 --- /dev/null +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -0,0 +1,1464 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Amlogic Meson Nand Flash Controller Driver + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Liang Yang <liang.yang@amlogic.com> + */ + +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/mtd/rawnand.h> +#include <linux/mtd/mtd.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/iopoll.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/sched/task_stack.h> + +#define NFC_REG_CMD 0x00 +#define NFC_CMD_IDLE (0xc << 14) +#define NFC_CMD_CLE (0x5 << 14) +#define NFC_CMD_ALE (0x6 << 14) +#define NFC_CMD_ADL ((0 << 16) | (3 << 20)) +#define NFC_CMD_ADH ((1 << 16) | (3 << 20)) +#define NFC_CMD_AIL ((2 << 16) | (3 << 20)) +#define NFC_CMD_AIH ((3 << 16) | (3 << 20)) +#define NFC_CMD_SEED ((8 << 16) | (3 << 20)) +#define NFC_CMD_M2N ((0 << 17) | (2 << 20)) +#define NFC_CMD_N2M ((1 << 17) | (2 << 20)) +#define NFC_CMD_RB BIT(20) +#define NFC_CMD_SCRAMBLER_ENABLE BIT(19) +#define NFC_CMD_SCRAMBLER_DISABLE 0 +#define NFC_CMD_SHORTMODE_DISABLE 0 +#define NFC_CMD_RB_INT BIT(14) + +#define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0)) + +#define NFC_REG_CFG 0x04 +#define NFC_REG_DADR 0x08 +#define NFC_REG_IADR 0x0c +#define NFC_REG_BUF 0x10 +#define NFC_REG_INFO 0x14 +#define NFC_REG_DC 0x18 +#define NFC_REG_ADR 0x1c +#define NFC_REG_DL 0x20 +#define NFC_REG_DH 0x24 +#define NFC_REG_CADR 0x28 +#define NFC_REG_SADR 0x2c +#define NFC_REG_PINS 0x30 +#define NFC_REG_VER 0x38 + +#define NFC_RB_IRQ_EN BIT(21) + +#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \ + ( \ + (cmd_dir) | \ + ((ran) << 19) | \ + ((bch) << 14) | \ + ((short_mode) << 13) | \ + (((page_size) & 0x7f) << 6) | \ + ((pages) & 0x3f) \ + ) + +#define GENCMDDADDRL(adl, addr) ((adl) | ((addr) & 0xffff)) +#define GENCMDDADDRH(adh, addr) ((adh) | (((addr) >> 16) & 0xffff)) +#define GENCMDIADDRL(ail, addr) ((ail) | ((addr) & 0xffff)) +#define GENCMDIADDRH(aih, addr) ((aih) | (((addr) >> 16) & 0xffff)) + +#define DMA_DIR(dir) ((dir) ? NFC_CMD_N2M : NFC_CMD_M2N) + +#define ECC_CHECK_RETURN_FF (-1) + +#define NAND_CE0 (0xe << 10) +#define NAND_CE1 (0xd << 10) + +#define DMA_BUSY_TIMEOUT 0x100000 +#define CMD_FIFO_EMPTY_TIMEOUT 1000 + +#define MAX_CE_NUM 2 + +/* eMMC clock register, misc control */ +#define CLK_SELECT_NAND BIT(31) + +#define NFC_CLK_CYCLE 6 + +/* nand flash controller delay 3 ns */ +#define NFC_DEFAULT_DELAY 3000 + +#define ROW_ADDER(page, index) (((page) >> (8 * (index))) & 0xff) +#define MAX_CYCLE_ADDRS 5 +#define DIRREAD 1 +#define DIRWRITE 0 + +#define ECC_PARITY_BCH8_512B 14 +#define ECC_COMPLETE BIT(31) +#define ECC_ERR_CNT(x) (((x) >> 24) & GENMASK(5, 0)) +#define ECC_ZERO_CNT(x) (((x) >> 16) & GENMASK(5, 0)) +#define ECC_UNCORRECTABLE 0x3f + +#define PER_INFO_BYTE 8 + +struct meson_nfc_nand_chip { + struct list_head node; + struct nand_chip nand; + unsigned long clk_rate; + unsigned long level1_divider; + u32 bus_timing; + u32 twb; + u32 tadl; + u32 tbers_max; + + u32 bch_mode; + u8 *data_buf; + __le64 *info_buf; + u32 nsels; + u8 sels[0]; +}; + +struct meson_nand_ecc { + u32 bch; + u32 strength; +}; + +struct meson_nfc_data { + const struct nand_ecc_caps *ecc_caps; +}; + +struct meson_nfc_param { + u32 chip_select; + u32 rb_select; +}; + +struct nand_rw_cmd { + u32 cmd0; + u32 addrs[MAX_CYCLE_ADDRS]; + u32 cmd1; +}; + +struct nand_timing { + u32 twb; + u32 tadl; + u32 tbers_max; +}; + +struct meson_nfc { + struct nand_controller controller; + struct clk *core_clk; + struct clk *device_clk; + struct clk *phase_tx; + struct clk *phase_rx; + + unsigned long clk_rate; + u32 bus_timing; + + struct device *dev; + void __iomem *reg_base; + struct regmap *reg_clk; + struct completion completion; + struct list_head chips; + const struct meson_nfc_data *data; + struct meson_nfc_param param; + struct nand_timing timing; + union { + int cmd[32]; + struct nand_rw_cmd rw; + } cmdfifo; + + dma_addr_t daddr; + dma_addr_t iaddr; + + unsigned long assigned_cs; +}; + +enum { + NFC_ECC_BCH8_1K = 2, + NFC_ECC_BCH24_1K, + NFC_ECC_BCH30_1K, + NFC_ECC_BCH40_1K, + NFC_ECC_BCH50_1K, + NFC_ECC_BCH60_1K, +}; + +#define MESON_ECC_DATA(b, s) { .bch = (b), .strength = (s)} + +static struct meson_nand_ecc meson_ecc[] = { + MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8), + MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24), + MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30), + MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40), + MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50), + MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60), +}; + +static int meson_nand_calc_ecc_bytes(int step_size, int strength) +{ + int ecc_bytes; + + if (step_size == 512 && strength == 8) + return ECC_PARITY_BCH8_512B; + + ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8); + ecc_bytes = ALIGN(ecc_bytes, 2); + + return ecc_bytes; +} + +NAND_ECC_CAPS_SINGLE(meson_gxl_ecc_caps, + meson_nand_calc_ecc_bytes, 1024, 8, 24, 30, 40, 50, 60); +NAND_ECC_CAPS_SINGLE(meson_axg_ecc_caps, + meson_nand_calc_ecc_bytes, 1024, 8); + +static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand) +{ + return container_of(nand, struct meson_nfc_nand_chip, nand); +} + +static void meson_nfc_select_chip(struct nand_chip *nand, int chip) +{ + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + struct meson_nfc *nfc = nand_get_controller_data(nand); + int ret, value; + + if (chip < 0 || WARN_ON_ONCE(chip >= meson_chip->nsels)) + return; + + nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0; + nfc->param.rb_select = nfc->param.chip_select; + nfc->timing.twb = meson_chip->twb; + nfc->timing.tadl = meson_chip->tadl; + nfc->timing.tbers_max = meson_chip->tbers_max; + + if (nfc->clk_rate != meson_chip->clk_rate) { + ret = clk_set_rate(nfc->device_clk, meson_chip->clk_rate); + if (ret) { + dev_err(nfc->dev, "failed to set clock rate\n"); + return; + } + nfc->clk_rate = meson_chip->clk_rate; + } + if (nfc->bus_timing != meson_chip->bus_timing) { + value = (NFC_CLK_CYCLE - 1) | (meson_chip->bus_timing << 5); + writel(value, nfc->reg_base + NFC_REG_CFG); + writel((1 << 31), nfc->reg_base + NFC_REG_CMD); + nfc->bus_timing = meson_chip->bus_timing; + } +} + +static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time) +{ + writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff), + nfc->reg_base + NFC_REG_CMD); +} + +static void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed) +{ + writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)), + nfc->reg_base + NFC_REG_CMD); +} + +static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir, + int scrambler) +{ + struct mtd_info *mtd = nand_to_mtd(nand); + struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + u32 bch = meson_chip->bch_mode, cmd; + int len = mtd->writesize, pagesize, pages; + + pagesize = nand->ecc.size; + + if (raw) { + len = mtd->writesize + mtd->oobsize; + cmd = (len & GENMASK(5, 0)) | scrambler | DMA_DIR(dir); + writel(cmd, nfc->reg_base + NFC_REG_CMD); + return; + } + + pages = len / nand->ecc.size; + + cmd = CMDRWGEN(DMA_DIR(dir), scrambler, bch, + NFC_CMD_SHORTMODE_DISABLE, pagesize, pages); + + writel(cmd, nfc->reg_base + NFC_REG_CMD); +} + +static void meson_nfc_drain_cmd(struct meson_nfc *nfc) +{ + /* + * Insert two commands to make sure all valid commands are finished. + * + * The Nand flash controller is designed as two stages pipleline - + * a) fetch and b) excute. + * There might be cases when the driver see command queue is empty, + * but the Nand flash controller still has two commands buffered, + * one is fetched into NFC request queue (ready to run), and another + * is actively executing. So pushing 2 "IDLE" commands guarantees that + * the pipeline is emptied. + */ + meson_nfc_cmd_idle(nfc, 0); + meson_nfc_cmd_idle(nfc, 0); +} + +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc, + unsigned int timeout_ms) +{ + u32 cmd_size = 0; + int ret; + + /* wait cmd fifo is empty */ + ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size, + !NFC_CMD_GET_SIZE(cmd_size), + 10, timeout_ms * 1000); + if (ret) + dev_err(nfc->dev, "wait for empty CMD FIFO time out\n"); + + return ret; +} + +static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc) +{ + meson_nfc_drain_cmd(nfc); + + return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT); +} + +static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i) +{ + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + int len; + + len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i; + + return meson_chip->data_buf + len; +} + +static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i) +{ + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + int len, temp; + + temp = nand->ecc.size + nand->ecc.bytes; + len = (temp + 2) * i; + + return meson_chip->data_buf + len; +} + +static void meson_nfc_get_data_oob(struct nand_chip *nand, + u8 *buf, u8 *oobbuf) +{ + int i, oob_len = 0; + u8 *dsrc, *osrc; + + oob_len = nand->ecc.bytes + 2; + for (i = 0; i < nand->ecc.steps; i++) { + if (buf) { + dsrc = meson_nfc_data_ptr(nand, i); + memcpy(buf, dsrc, nand->ecc.size); + buf += nand->ecc.size; < |