diff options
author | Miquel Raynal <miquel.raynal@bootlin.com> | 2019-11-17 18:34:25 +0100 |
---|---|---|
committer | Miquel Raynal <miquel.raynal@bootlin.com> | 2019-11-17 18:34:25 +0100 |
commit | 589e1b6c47ce72fcae103c2e45d899610c92c11e (patch) | |
tree | 125fe159c2800ba70c7bff6b5f0f35e51efb8cdd /drivers/mtd | |
parent | 8389a7b909f252e74ea92b2794de8d660cfee96e (diff) | |
parent | 777260a5c99226cfa58325b6a7c2f39bb46f5aaf (diff) |
Merge tag 'nand/for-5.5' into mtd/next
Raw NAND core
* Useless extra checks dropped.
* Updated the detection of the bad block markers position
Raw NAND controller drivers:
* Cadence : New driver
* Brcmnand: Support for flash-dma v0 + fixes
* Denali : Support for the legacy controller/chip DT representation
dropped
* Superfluous dev_err() calls removed
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/raw/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/brcmnand/brcmnand.c | 23 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/cadence-nand-controller.c | 3030 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/denali_dt.c | 55 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mxic_nand.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_base.c | 8 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_micron.c | 4 |
8 files changed, 3072 insertions, 60 deletions
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index e59de3f60cf6..74fb91adeb46 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -450,6 +450,13 @@ config MTD_NAND_PLATFORM devices. You will need to provide platform-specific functions via platform_data. +config MTD_NAND_CADENCE + tristate "Support Cadence NAND (HPNFC) controller" + depends on OF || COMPILE_TEST + help + Enable the driver for NAND flash on platforms using a Cadence NAND + controller. + comment "Misc" config MTD_SM_COMMON diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index a98721988e61..2d136b158fb7 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_MTD_NAND_MXIC) += mxic_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 +obj-$(CONFIG_MTD_NAND_CADENCE) += cadence-nand-controller.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/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 15ef30b368a5..1a66b1cd51c0 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -117,6 +117,18 @@ enum flash_dma_reg { FLASH_DMA_CURRENT_DESC_EXT, }; +/* flash_dma registers v0*/ +static const u16 flash_dma_regs_v0[] = { + [FLASH_DMA_REVISION] = 0x00, + [FLASH_DMA_FIRST_DESC] = 0x04, + [FLASH_DMA_CTRL] = 0x08, + [FLASH_DMA_MODE] = 0x0c, + [FLASH_DMA_STATUS] = 0x10, + [FLASH_DMA_INTERRUPT_DESC] = 0x14, + [FLASH_DMA_ERROR_STATUS] = 0x18, + [FLASH_DMA_CURRENT_DESC] = 0x1c, +}; + /* flash_dma registers v1*/ static const u16 flash_dma_regs_v1[] = { [FLASH_DMA_REVISION] = 0x00, @@ -597,6 +609,8 @@ static void brcmnand_flash_dma_revision_init(struct brcmnand_controller *ctrl) /* flash_dma register offsets */ if (ctrl->nand_version >= 0x0703) ctrl->flash_dma_offsets = flash_dma_regs_v4; + else if (ctrl->nand_version == 0x0602) + ctrl->flash_dma_offsets = flash_dma_regs_v0; else ctrl->flash_dma_offsets = flash_dma_regs_v1; } @@ -918,7 +932,7 @@ static inline void disable_ctrl_irqs(struct brcmnand_controller *ctrl) return; if (has_flash_dma(ctrl)) { - ctrl->flash_dma_base = 0; + ctrl->flash_dma_base = NULL; disable_irq(ctrl->dma_irq); } @@ -1673,8 +1687,11 @@ static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); - flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); - (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + if (ctrl->nand_version > 0x0602) { + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, + upper_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + } /* Start FLASH_DMA engine */ ctrl->dma_pending = true; diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c new file mode 100644 index 000000000000..3a36285a8d8a --- /dev/null +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -0,0 +1,3030 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Cadence NAND flash controller driver + * + * Copyright (C) 2019 Cadence + * + * Author: Piotr Sroka <piotrs@cadence.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/rawnand.h> +#include <linux/of_device.h> +#include <linux/iopoll.h> + +/* + * HPNFC can work in 3 modes: + * - PIO - can work in master or slave DMA + * - CDMA - needs Master DMA for accessing command descriptors. + * - Generic mode - can use only slave DMA. + * CDMA and PIO modes can be used to execute only base commands. + * Generic mode can be used to execute any command + * on NAND flash memory. Driver uses CDMA mode for + * block erasing, page reading, page programing. + * Generic mode is used for executing rest of commands. + */ + +#define MAX_OOB_SIZE_PER_SECTOR 32 +#define MAX_ADDRESS_CYC 6 +#define MAX_ERASE_ADDRESS_CYC 3 +#define MAX_DATA_SIZE 0xFFFC +#define DMA_DATA_SIZE_ALIGN 8 + +/* Register definition. */ +/* + * Command register 0. + * Writing data to this register will initiate a new transaction + * of the NF controller. + */ +#define CMD_REG0 0x0000 +/* Command type field mask. */ +#define CMD_REG0_CT GENMASK(31, 30) +/* Command type CDMA. */ +#define CMD_REG0_CT_CDMA 0uL +/* Command type generic. */ +#define CMD_REG0_CT_GEN 3uL +/* Command thread number field mask. */ +#define CMD_REG0_TN GENMASK(27, 24) + +/* Command register 2. */ +#define CMD_REG2 0x0008 +/* Command register 3. */ +#define CMD_REG3 0x000C +/* Pointer register to select which thread status will be selected. */ +#define CMD_STATUS_PTR 0x0010 +/* Command status register for selected thread. */ +#define CMD_STATUS 0x0014 + +/* Interrupt status register. */ +#define INTR_STATUS 0x0110 +#define INTR_STATUS_SDMA_ERR BIT(22) +#define INTR_STATUS_SDMA_TRIGG BIT(21) +#define INTR_STATUS_UNSUPP_CMD BIT(19) +#define INTR_STATUS_DDMA_TERR BIT(18) +#define INTR_STATUS_CDMA_TERR BIT(17) +#define INTR_STATUS_CDMA_IDL BIT(16) + +/* Interrupt enable register. */ +#define INTR_ENABLE 0x0114 +#define INTR_ENABLE_INTR_EN BIT(31) +#define INTR_ENABLE_SDMA_ERR_EN BIT(22) +#define INTR_ENABLE_SDMA_TRIGG_EN BIT(21) +#define INTR_ENABLE_UNSUPP_CMD_EN BIT(19) +#define INTR_ENABLE_DDMA_TERR_EN BIT(18) +#define INTR_ENABLE_CDMA_TERR_EN BIT(17) +#define INTR_ENABLE_CDMA_IDLE_EN BIT(16) + +/* Controller internal state. */ +#define CTRL_STATUS 0x0118 +#define CTRL_STATUS_INIT_COMP BIT(9) +#define CTRL_STATUS_CTRL_BUSY BIT(8) + +/* Command Engine threads state. */ +#define TRD_STATUS 0x0120 + +/* Command Engine interrupt thread error status. */ +#define TRD_ERR_INT_STATUS 0x0128 +/* Command Engine interrupt thread error enable. */ +#define TRD_ERR_INT_STATUS_EN 0x0130 +/* Command Engine interrupt thread complete status. */ +#define TRD_COMP_INT_STATUS 0x0138 + +/* + * Transfer config 0 register. + * Configures data transfer parameters. + */ +#define TRAN_CFG_0 0x0400 +/* Offset value from the beginning of the page. */ +#define TRAN_CFG_0_OFFSET GENMASK(31, 16) +/* Numbers of sectors to transfer within singlNF device's page. */ +#define TRAN_CFG_0_SEC_CNT GENMASK(7, 0) + +/* + * Transfer config 1 register. + * Configures data transfer parameters. + */ +#define TRAN_CFG_1 0x0404 +/* Size of last data sector. */ +#define TRAN_CFG_1_LAST_SEC_SIZE GENMASK(31, 16) +/* Size of not-last data sector. */ +#define TRAN_CFG_1_SECTOR_SIZE GENMASK(15, 0) + +/* ECC engine configuration register 0. */ +#define ECC_CONFIG_0 0x0428 +/* Correction strength. */ +#define ECC_CONFIG_0_CORR_STR GENMASK(10, 8) +/* Enable erased pages detection mechanism. */ +#define ECC_CONFIG_0_ERASE_DET_EN BIT(1) +/* Enable controller ECC check bits generation and correction. */ +#define ECC_CONFIG_0_ECC_EN BIT(0) + +/* ECC engine configuration register 1. */ +#define ECC_CONFIG_1 0x042C + +/* Multiplane settings register. */ +#define MULTIPLANE_CFG 0x0434 +/* Cache operation settings. */ +#define CACHE_CFG 0x0438 + +/* DMA settings register. */ +#define DMA_SETINGS 0x043C +/* Enable SDMA error report on access unprepared slave DMA interface. */ +#define DMA_SETINGS_SDMA_ERR_RSP BIT(17) + +/* Transferred data block size for the slave DMA module. */ +#define SDMA_SIZE 0x0440 + +/* Thread number associated with transferred data block + * for the slave DMA module. + */ +#define SDMA_TRD_NUM 0x0444 +/* Thread number mask. */ +#define SDMA_TRD_NUM_SDMA_TRD GENMASK(2, 0) + +#define CONTROL_DATA_CTRL 0x0494 +/* Thread number mask. */ +#define CONTROL_DATA_CTRL_SIZE GENMASK(15, 0) + +#define CTRL_VERSION 0x800 +#define CTRL_VERSION_REV GENMASK(7, 0) + +/* Available hardware features of the controller. */ +#define CTRL_FEATURES 0x804 +/* Support for NV-DDR2/3 work mode. */ +#define CTRL_FEATURES_NVDDR_2_3 BIT(28) +/* Support for NV-DDR work mode. */ +#define CTRL_FEATURES_NVDDR BIT(27) +/* Support for asynchronous work mode. */ +#define CTRL_FEATURES_ASYNC BIT(26) +/* Support for asynchronous work mode. */ +#define CTRL_FEATURES_N_BANKS GENMASK(25, 24) +/* Slave and Master DMA data width. */ +#define CTRL_FEATURES_DMA_DWITH64 BIT(21) +/* Availability of Control Data feature.*/ +#define CTRL_FEATURES_CONTROL_DATA BIT(10) + +/* BCH Engine identification register 0 - correction strengths. */ +#define BCH_CFG_0 0x838 +#define BCH_CFG_0_CORR_CAP_0 GENMASK(7, 0) +#define BCH_CFG_0_CORR_CAP_1 GENMASK(15, 8) +#define BCH_CFG_0_CORR_CAP_2 GENMASK(23, 16) +#define BCH_CFG_0_CORR_CAP_3 GENMASK(31, 24) + +/* BCH Engine identification register 1 - correction strengths. */ +#define BCH_CFG_1 0x83C +#define BCH_CFG_1_CORR_CAP_4 GENMASK(7, 0) +#define BCH_CFG_1_CORR_CAP_5 GENMASK(15, 8) +#define BCH_CFG_1_CORR_CAP_6 GENMASK(23, 16) +#define BCH_CFG_1_CORR_CAP_7 GENMASK(31, 24) + +/* BCH Engine identification register 2 - sector sizes. */ +#define BCH_CFG_2 0x840 +#define BCH_CFG_2_SECT_0 GENMASK(15, 0) +#define BCH_CFG_2_SECT_1 GENMASK(31, 16) + +/* BCH Engine identification register 3. */ +#define BCH_CFG_3 0x844 + +/* Ready/Busy# line status. */ +#define RBN_SETINGS 0x1004 + +/* Common settings. */ +#define COMMON_SET 0x1008 +/* 16 bit device connected to the NAND Flash interface. */ +#define COMMON_SET_DEVICE_16BIT BIT(8) + +/* Skip_bytes registers. */ +#define SKIP_BYTES_CONF 0x100C +#define SKIP_BYTES_MARKER_VALUE GENMASK(31, 16) +#define SKIP_BYTES_NUM_OF_BYTES GENMASK(7, 0) + +#define SKIP_BYTES_OFFSET 0x1010 +#define SKIP_BYTES_OFFSET_VALUE GENMASK(23, 0) + +/* Timings configuration. */ +#define ASYNC_TOGGLE_TIMINGS 0x101c +#define ASYNC_TOGGLE_TIMINGS_TRH GENMASK(28, 24) +#define ASYNC_TOGGLE_TIMINGS_TRP GENMASK(20, 16) +#define ASYNC_TOGGLE_TIMINGS_TWH GENMASK(12, 8) +#define ASYNC_TOGGLE_TIMINGS_TWP GENMASK(4, 0) + +#define TIMINGS0 0x1024 +#define TIMINGS0_TADL GENMASK(31, 24) +#define TIMINGS0_TCCS GENMASK(23, 16) +#define TIMINGS0_TWHR GENMASK(15, 8) +#define TIMINGS0_TRHW GENMASK(7, 0) + +#define TIMINGS1 0x1028 +#define TIMINGS1_TRHZ GENMASK(31, 24) +#define TIMINGS1_TWB GENMASK(23, 16) +#define TIMINGS1_TVDLY GENMASK(7, 0) + +#define TIMINGS2 0x102c +#define TIMINGS2_TFEAT GENMASK(25, 16) +#define TIMINGS2_CS_HOLD_TIME GENMASK(13, 8) +#define TIMINGS2_CS_SETUP_TIME GENMASK(5, 0) + +/* Configuration of the resynchronization of slave DLL of PHY. */ +#define DLL_PHY_CTRL 0x1034 +#define DLL_PHY_CTRL_DLL_RST_N BIT(24) +#define DLL_PHY_CTRL_EXTENDED_WR_MODE BIT(17) +#define DLL_PHY_CTRL_EXTENDED_RD_MODE BIT(16) +#define DLL_PHY_CTRL_RS_HIGH_WAIT_CNT GENMASK(11, 8) +#define DLL_PHY_CTRL_RS_IDLE_CNT GENMASK(7, 0) + +/* Register controlling DQ related timing. */ +#define PHY_DQ_TIMING 0x2000 +/* Register controlling DSQ related timing. */ +#define PHY_DQS_TIMING 0x2004 +#define PHY_DQS_TIMING_DQS_SEL_OE_END GENMASK(3, 0) +#define PHY_DQS_TIMING_PHONY_DQS_SEL BIT(16) +#define PHY_DQS_TIMING_USE_PHONY_DQS BIT(20) + +/* Register controlling the gate and loopback control related timing. */ +#define PHY_GATE_LPBK_CTRL 0x2008 +#define PHY_GATE_LPBK_CTRL_RDS GENMASK(24, 19) + +/* Register holds the control for the master DLL logic. */ +#define PHY_DLL_MASTER_CTRL 0x200C +#define PHY_DLL_MASTER_CTRL_BYPASS_MODE BIT(23) + +/* Register holds the control for the slave DLL logic. */ +#define PHY_DLL_SLAVE_CTRL 0x2010 + +/* This register handles the global control settings for the PHY. */ +#define PHY_CTRL 0x2080 +#define PHY_CTRL_SDR_DQS BIT(14) +#define PHY_CTRL_PHONY_DQS GENMASK(9, 4) + +/* + * This register handles the global control settings + * for the termination selects for reads. + */ +#define PHY_TSEL 0x2084 + +/* Generic command layout. */ +#define GCMD_LAY_CS GENMASK_ULL(11, 8) +/* + * This bit informs the minicotroller if it has to wait for tWB + * after sending the last CMD/ADDR/DATA in the sequence. + */ +#define GCMD_LAY_TWB BIT_ULL(6) +/* Type of generic instruction. */ +#define GCMD_LAY_INSTR GENMASK_ULL(5, 0) + +/* Generic CMD sequence type. */ +#define GCMD_LAY_INSTR_CMD 0 +/* Generic ADDR sequence type. */ +#define GCMD_LAY_INSTR_ADDR 1 +/* Generic data transfer sequence type. */ +#define GCMD_LAY_INSTR_DATA 2 + +/* Input part of generic command type of input is command. */ +#define GCMD_LAY_INPUT_CMD GENMASK_ULL(23, 16) + +/* Generic command address sequence - address fields. */ +#define GCMD_LAY_INPUT_ADDR GENMASK_ULL(63, 16) +/* Generic command address sequence - address size. */ +#define GCMD_LAY_INPUT_ADDR_SIZE GENMASK_ULL(13, 11) + +/* Transfer direction field of generic command data sequence. */ +#define GCMD_DIR BIT_ULL(11) +/* Read transfer direction of generic command data sequence. */ +#define GCMD_DIR_READ 0 +/* Write transfer direction of generic command data sequence. */ +#define GCMD_DIR_WRITE 1 + +/* ECC enabled flag of generic command data sequence - ECC enabled. */ +#define GCMD_ECC_EN BIT_ULL(12) +/* Generic command data sequence - sector size. */ +#define GCMD_SECT_SIZE GENMASK_ULL(31, 16) +/* Generic command data sequence - sector count. */ +#define GCMD_SECT_CNT GENMASK_ULL(39, 32) +/* Generic command data sequence - last sector size. */ +#define GCMD_LAST_SIZE GENMASK_ULL(55, 40) + +/* CDMA descriptor fields. */ +/* Erase command type of CDMA descriptor. */ +#define CDMA_CT_ERASE 0x1000 +/* Program page command type of CDMA descriptor. */ +#define CDMA_CT_WR 0x2100 +/* Read page command type of CDMA descriptor. */ +#define CDMA_CT_RD 0x2200 + +/* Flash pointer memory shift. */ +#define CDMA_CFPTR_MEM_SHIFT 24 +/* Flash pointer memory mask. */ +#define CDMA_CFPTR_MEM GENMASK(26, 24) + +/* + * Command DMA descriptor flags. If set causes issue interrupt after + * the completion of descriptor processing. + */ +#define CDMA_CF_INT BIT(8) +/* + * Command DMA descriptor flags - the next descriptor + * address field is valid and descriptor processing should continue. + */ +#define CDMA_CF_CONT BIT(9) +/* DMA master flag of command DMA descriptor. */ +#define CDMA_CF_DMA_MASTER BIT(10) + +/* Operation complete status of command descriptor. */ +#define CDMA_CS_COMP BIT(15) +/* Operation complete status of command descriptor. */ +/* Command descriptor status - operation fail. */ +#define CDMA_CS_FAIL BIT(14) +/* Command descriptor status - page erased. */ +#define CDMA_CS_ERP BIT(11) +/* Command descriptor status - timeout occurred. */ +#define CDMA_CS_TOUT BIT(10) +/* + * Maximum amount of correction applied to one ECC sector. + * It is part of command descriptor status. + */ +#define CDMA_CS_MAXERR GENMASK(9, 2) +/* Command descriptor status - uncorrectable ECC error. */ +#define CDMA_CS_UNCE BIT(1) +/* Command descriptor status - descriptor error. */ +#define CDMA_CS_ERR BIT(0) + +/* Status of operation - OK. */ +#define STAT_OK 0 +/* Status of operation - FAIL. */ +#define STAT_FAIL 2 +/* Status of operation - uncorrectable ECC error. */ +#define STAT_ECC_UNCORR 3 +/* Status of operation - page erased. */ +#define STAT_ERASED 5 +/* Status of operation - correctable ECC error. */ +#define STAT_ECC_CORR 6 +/* Status of operation - unsuspected state. */ +#define STAT_UNKNOWN 7 +/* Status of operation - operation is not completed yet. */ +#define STAT_BUSY 0xFF + +#define BCH_MAX_NUM_CORR_CAPS 8 +#define BCH_MAX_NUM_SECTOR_SIZES 2 + +struct cadence_nand_timings { + u32 async_toggle_timings; + u32 timings0; + u32 timings1; + u32 timings2; + u32 dll_phy_ctrl; + u32 phy_ctrl; + u32 phy_dqs_timing; + u32 phy_gate_lpbk_ctrl; +}; + +/* Command DMA descriptor. */ +struct cadence_nand_cdma_desc { + /* Next descriptor address. */ + u64 next_pointer; + + /* Flash address is a 32-bit address comprising of BANK and ROW ADDR. */ + u32 flash_pointer; + /*field appears in HPNFC version 13*/ + u16 bank; + u16 rsvd0; + + /* Operation the controller needs to perform. */ + u16 command_type; + u16 rsvd1; + /* Flags for operation of this command. */ + u16 command_flags; + u16 rsvd2; + + /* System/host memory address required for data DMA commands. */ + u64 memory_pointer; + + /* Status of operation. */ + u32 status; + u32 rsvd3; + + /* Address pointer to sync buffer location. */ + u64 sync_flag_pointer; + + /* Controls the buffer sync mechanism. */ + u32 sync_arguments; + u32 rsvd4; + + /* Control data pointer. */ + u64 ctrl_data_ptr; +}; + +/* Interrupt status. */ +struct cadence_nand_irq_status { + /* Thread operation complete status. */ + u32 trd_status; + /* Thread operation error. */ + u32 trd_error; + /* Controller status. */ + u32 status; +}; + +/* Cadence NAND flash controller capabilities get from driver data. */ +struct cadence_nand_dt_devdata { + /* Skew value of the output signals of the NAND Flash interface. */ + u32 if_skew; + /* It informs if slave DMA interface is connected to DMA engine. */ + unsigned int has_dma:1; +}; + +/* Cadence NAND flash controller capabilities read from registers. */ +struct cdns_nand_caps { + /* Maximum number of banks supported by hardware. */ + u8 max_banks; + /* Slave and Master DMA data width in bytes (4 or 8). */ + u8 data_dma_width; + /* Control Data feature supported. */ + bool data_control_supp; + /* Is PHY type DLL. */ + bool is_phy_type_dll; +}; + +struct cdns_nand_ctrl { + struct device *dev; + struct nand_controller controller; + struct cadence_nand_cdma_desc *cdma_desc; + /* IP capability. */ + const struct cadence_nand_dt_devdata *caps1; + struct cdns_nand_caps caps2; + u8 ctrl_rev; + dma_addr_t dma_cdma_desc; + u8 *buf; + u32 buf_size; + u8 curr_corr_str_idx; + + /* Register interface. */ + void __iomem *reg; + + struct { + void __iomem *virt; + dma_addr_t dma; + } io; + + int irq; + /* Interrupts that have happened. */ + struct cadence_nand_irq_status irq_status; + /* Interrupts we are waiting for. */ + struct cadence_nand_irq_status irq_mask; + struct completion complete; + /* Protect irq_mask and irq_status. */ + spinlock_t irq_lock; + + int ecc_strengths[BCH_MAX_NUM_CORR_CAPS]; + struct nand_ecc_step_info ecc_stepinfos[BCH_MAX_NUM_SECTOR_SIZES]; + struct nand_ecc_caps ecc_caps; + + int curr_trans_type; + + struct dma_chan *dmac; + + u32 nf_clk_rate; + /* + * Estimated Board delay. The value includes the total + * round trip delay for the signals and is used for deciding on values + * associated with data read capture. + */ + u32 board_delay; + + struct nand_chip *selected_chip; + + unsigned long assigned_cs; + struct list_head chips; +}; + +struct cdns_nand_chip { + struct cadence_nand_timings timings; + struct nand_chip chip; + u8 nsels; + struct list_head node; + + /* + * part of oob area of NAND flash memory page. + * This part is available for user to read or write. + */ + u32 avail_oob_size; + + /* Sector size. There are few sectors per mtd->writesize */ + u32 sector_size; + u32 sector_count; + + /* Offset of BBM. */ + u8 bbm_offs; + /* Number of bytes reserved for BBM. */ + u8 bbm_len; + /* ECC strength index. */ + u8 corr_str_idx; + + u8 cs[]; +}; + +struct ecc_info { + int (*calc_ecc_bytes)(int step_size, int strength); + int max_step_size; +}; + +static inline struct +cdns_nand_chip *to_cdns_nand_chip(struct nand_chip *chip) +{ + return container_of(chip, struct cdns_nand_chip, chip); +} + +static inline struct +cdns_nand_ctrl *to_cdns_nand_ctrl(struct nand_controller *controller) +{ + return container_of(controller, struct cdns_nand_ctrl, controller); +} + +static bool +cadence_nand_dma_buf_ok(struct cdns_nand_ctrl *cdns_ctrl, const void *buf, + u32 buf_len) +{ + u8 data_dma_width = cdns_ctrl->caps2.data_dma_width; + + return buf && virt_addr_valid(buf) && + likely(IS_ALIGNED((uintptr_t)buf, data_dma_width)) && + likely(IS_ALIGNED(buf_len, DMA_DATA_SIZE_ALIGN)); +} + +static int cadence_nand_wait_for_value(struct cdns_nand_ctrl *cdns_ctrl, + u32 reg_offset, u32 timeout_us, + u32 mask, bool is_clear) +{ + u32 val; + int ret; + + ret = readl_relaxed_poll_timeout(cdns_ctrl->reg + reg_offset, + val, !(val & mask) == is_clear, + 10, timeout_us); + + if (ret < 0) { + dev_err(cdns_ctrl->dev, + "Timeout while waiting for reg %x with mask %x is clear %d\n", + reg_offset, mask, is_clear); + } + + return ret; +} + +static int cadence_nand_set_ecc_enable(struct cdns_nand_ctrl *cdns_ctrl, + bool enable) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cdns_ctrl->reg + ECC_CONFIG_0); + + if (enable) + reg |= ECC_CONFIG_0_ECC_EN; + else + reg &= ~ECC_CONFIG_0_ECC_EN; + + writel_relaxed(reg, cdns_ctrl->reg + ECC_CONFIG_0); + + return 0; +} + +static void cadence_nand_set_ecc_strength(struct cdns_nand_ctrl *cdns_ctrl, + u8 corr_str_idx) +{ + u32 reg; + + if (cdns_ctrl->curr_corr_str_idx == corr_str_idx) + return; + + reg = readl_relaxed(cdns_ctrl->reg + ECC_CONFIG_0); + reg &= ~ECC_CONFIG_0_CORR_STR; + reg |= FIELD_PREP(ECC_CONFIG_0_CORR_STR, corr_str_idx); + writel_relaxed(reg, cdns_ctrl->reg + ECC_CONFIG_0); + + cdns_ctrl->curr_corr_str_idx = corr_str_idx; +} + +static int cadence_nand_get_ecc_strength_idx(struct cdns_nand_ctrl *cdns_ctrl, + u8 strength) +{ + int i, corr_str_idx = -1; + + for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) { + if (cdns_ctrl->ecc_strengths[i] == strength) { + corr_str_idx = i; + break; + } + } + + return corr_str_idx; +} + +static int cadence_nand_set_skip_marker_val(struct cdns_nand_ctrl *cdns_ctrl, + u16 marker_value) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cdns_ctrl->reg + SKIP_BYTES_CONF); + reg &= ~SKIP_BYTES_MARKER_VALUE; + reg |= FIELD_PREP(SKIP_BYTES_MARKER_VALUE, + marker_value); + + writel_relaxed(reg, cdns_ctrl->reg + SKIP_BYTES_CONF); + + return 0; +} + +static int cadence_nand_set_skip_bytes_conf(struct cdns_nand_ctrl *cdns_ctrl, + u8 num_of_bytes, + u32 offset_value, + int enable) +{ + u32 reg, skip_bytes_offset; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + if (!enable) { + num_of_bytes = 0; + offset_value = 0; + } + + reg = readl_relaxed(cdns_ctrl->reg + SKIP_BYTES_CONF); + reg &= ~SKIP_BYTES_NUM_OF_BYTES; + reg |= FIELD_PREP(SKIP_BYTES_NUM_OF_BYTES, + num_of_bytes); + skip_bytes_offset = FIELD_PREP(SKIP_BYTES_OFFSET_VALUE, + offset_value); + + writel_relaxed(reg, cdns_ctrl->reg + SKIP_BYTES_CONF); + writel_relaxed(skip_bytes_offset, cdns_ctrl->reg + SKIP_BYTES_OFFSET); + + return 0; +} + +/* Functions enables/disables hardware detection of erased data */ +static void cadence_nand_set_erase_detection(struct cdns_nand_ctrl *cdns_ctrl, + bool enable, + u8 bitflips_threshold) +{ + u32 reg; + + reg = readl_relaxed(cdns_ctrl->reg + ECC_CONFIG_0); + + if (enable) + reg |= ECC_CONFIG_0_ERASE_DET_EN; + else + reg &= ~ECC_CONFIG_0_ERASE_DET_EN; + + writel_relaxed(reg, cdns_ctrl->reg + ECC_CONFIG_0); + writel_relaxed(bitflips_threshold, cdns_ctrl->reg + ECC_CONFIG_1); +} + +static int cadence_nand_set_access_width16(struct cdns_nand_ctrl *cdns_ctrl, + bool bit_bus16) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cdns_ctrl->reg + COMMON_SET); + + if (!bit_bus16) + reg &= ~COMMON_SET_DEVICE_16BIT; + else + reg |= COMMON_SET_DEVICE_16BIT; + writel_relaxed(reg, cdns_ctrl->reg + COMMON_SET); + + return 0; +} + +static void +cadence_nand_clear_interrupt(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_status) +{ + writel_relaxed(irq_status->status, cdns_ctrl->reg + INTR_STATUS); + writel_relaxed(irq_status->trd_status, + cdns_ctrl->reg + TRD_COMP_INT_STATUS); + writel_relaxed(irq_status->trd_error, + cdns_ctrl->reg + TRD_ERR_INT_STATUS); +} + +static void +cadence_nand_read_int_status(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_status) +{ + irq_status->status = readl_relaxed(cdns_ctrl->reg + INTR_STATUS); + irq_status->trd_status = readl_relaxed(cdns_ctrl->reg + + TRD_COMP_INT_STATUS); + irq_status->trd_error = readl_relaxed(cdns_ctrl->reg + + TRD_ERR_INT_STATUS); +} + +static u32 irq_detected(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_status) +{ + cadence_nand_read_int_status(cdns_ctrl, irq_status); + + return irq_status->status || irq_status->trd_status || + irq_status->trd_error; +} + +static void cadence_nand_reset_irq(struct cdns_nand_ctrl *cdns_ctrl) +{ + unsigned long flags; + + spin_lock_irqsave(&cdns_ctrl->irq_lock, flags); + memset(&cdns_ctrl->irq_status, 0, sizeof(cdns_ctrl->irq_status)); + memset(&cdns_ctrl->irq_mask, 0, sizeof(cdns_ctrl->irq_mask)); + spin_unlock_irqrestore(&cdns_ctrl->irq_lock, flags); +} + +/* + * This is the interrupt service routine. It handles all interrupts + * sent to this device. + */ +static irqreturn_t cadence_nand_isr(int irq, void *dev_id) +{ + struct cdns_nand_ctrl *cdns_ctrl = dev_id; + struct cadence_nand_irq_status irq_status; + irqreturn_t result = IRQ_NONE; + + spin_lock(&cdns_ctrl->irq_lock); + + if (irq_detected(cdns_ctrl, &irq_status)) { + /* Handle interrupt. */ + /* First acknowledge it. */ + cadence_nand_clear_interrupt(cdns_ctrl, &irq_status); + /* Status in the device context for someone to read. */ + cdns_ctrl->irq_status.status |= irq_status.status; + cdns_ctrl->irq_status.trd_status |= irq_status.trd_status; + cdns_ctrl->irq_status.trd_error |= irq_status.trd_error; + /* Notify anyone who cares that it happened. */ + complete(&cdns_ctrl->complete); + /* Tell the OS that we've handled this. */ + result = IRQ_HANDLED; + } + spin_unlock(&cdns_ctrl->irq_lock); + + return result; +} + +static void cadence_nand_set_irq_mask(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_mask) +{ + writel_relaxed(INTR_ENABLE_INTR_EN | irq_mask->status, + cdns_ctrl->reg + INTR_ENABLE); + + writel_relaxed(irq_mask->trd_error, + cdns_ctrl->reg + TRD_ERR_INT_STATUS_EN); +} + +static void +cadence_nand_wait_for_irq(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_mask, + struct cadence_nand_irq_status *irq_status) +{ + unsigned long timeout = msecs_to_jiffies(10000); + unsigned long time_left; + + time_left = wait_for_completion_timeout(&cdns_ctrl->complete, + timeout); + + *irq_status = cdns_ctrl->irq_status; + if (time_left == 0) { + /* Timeout error. */ + dev_err(cdns_ctrl->dev, "timeout occurred:\n"); + dev_err(cdns_ctrl->dev, "\tstatus = 0x%x, mask = 0x%x\n", + irq_status->status, irq_mask->status); + dev_err(cdns_ctrl->dev, + "\ttrd_status = 0x%x, trd_status mask = 0x%x\n", + irq_status->trd_status, irq_mask->trd_status); + dev_err(cdns_ctrl->dev, + "\t trd_error = 0x%x, trd_error mask = 0x%x\n", + irq_status->trd_error, irq_mask->trd_error); + } +} + +/* Execute generic command on NAND controller. */ +static int cadence_nand_generic_cmd_send(struct cdns_nand_ctrl *cdns_ctrl, + u8 chip_nr, + u64 mini_ctrl_cmd) +{ + u32 mini_ctrl_cmd_l, mini_ctrl_cmd_h, reg; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_CS, chip_nr); + mini_ctrl_cmd_l = mini_ctrl_cmd & 0xFFFFFFFF; + mini_ctrl_cmd_h = mini_ctrl_cmd >> 32; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_reset_irq(cdns_ctrl); + + writel_relaxed(mini_ctrl_cmd_l, cdns_ctrl->reg + CMD_REG2); + writel_relaxed(mini_ctrl_cmd_h, cdns_ctrl->reg + CMD_REG3); + + /* Select generic command. */ + reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_GEN); + /* Thread number. */ + reg |= FIELD_PREP(CMD_REG0_TN, 0); + + /* Issue command. */ + writel_relaxed(reg, cdns_ctrl->reg + CMD_REG0); + + return 0; +} + +/* Wait for data on slave DMA interface. */ +static int cadence_nand_wait_on_sdma(struct cdns_nand_ctrl *cdns_ctrl, + u8 *out_sdma_trd, + u32 *out_sdma_size) +{ + struct cadence_nand_irq_status irq_mask, irq_status; + + irq_mask.trd_status = 0; + irq_mask.trd_error = 0; + irq_mask.status = INTR_STATUS_SDMA_TRIGG + | INTR_STATUS_SDMA_ERR + | INTR_STATUS_UNSUPP_CMD; + + cadence_nand_set_irq_mask(cdns_ctrl, &irq_mask); + cadence_nand_wait_for_irq(cdns_ctrl, &irq_mask, &irq_status); + if (irq_status.status == 0) { + dev_err(cdns_ctrl->dev, "Timeout while waiting for SDMA\n"); + return -ETIMEDOUT; + } + + if (irq_status.status & INTR_STATUS_SDMA_TRIGG) { + *out_sdma_size = readl_relaxed(cdns_ctrl->reg + SDMA_SIZE); + *out_sdma_trd = readl_relaxed(cdns_ctrl->reg + SDMA_TRD_NUM); + *out_sdma_trd = + FIELD_GET(SDMA_TRD_NUM_SDMA_TRD, *out_sdma_trd); + } else { + dev_err(cdns_ctrl->dev, "SDMA error - irq_status %x\n", + irq_status.status); + return -EIO; + } + + return 0; +} + +static void cadence_nand_get_caps(struct cdns_nand_ctrl *cdns_ctrl) +{ + u32 reg; + + reg = readl_relaxed(cdns_ctrl->reg + CTRL_FEATURES); + + cdns_ctrl->caps2.max_banks = 1 << FIELD_GET(CTRL_FEATURES_N_BANKS, reg); + + if (FIELD_GET(CTRL_FEATURES_DMA_DWITH64, reg)) + cdns_ctrl->caps2.data_dma_width = 8; + else + cdns_ctrl->caps2.data_dma_width = 4; + + if (reg & CTRL_FEATURES_CONTROL_DATA) + cdns_ctrl->caps2.data_control_supp = true; + + if (reg & (CTRL_FEATURES_NVDDR_2_3 + | CTRL_FEATURES_NVDDR)) + cdns_ctrl->caps2.is_phy_type_dll = true; +} + +/* Prepare CDMA descriptor. */ +static void +cadence_nand_cdma_desc_prepare(struct cdns_nand_ctrl *cdns_ctrl, + char nf_mem, u32 flash_ptr, char *mem_ptr, + char *ctrl_data_ptr, u16 ctype) +{ + struct cadence_nand_cdma_desc *cdma_desc = cdns_ctrl->cdma_desc; + + memset(cdma_desc, 0, sizeof(struct cadence_nand_cdma_desc)); + + /* Set fields for one descriptor. */ + cdma_desc->flash_pointer = flash_ptr; + if (cdns_ctrl->ctrl_rev >= 13) + cdma_desc->bank = nf_mem; + else + cdma_desc->flash_pointer |= (nf_mem << CDMA_CFPTR_MEM_SHIFT); + + cdma_desc->command_flags |= CDMA_CF_DMA_MASTER; + cdma_desc->command_flags |= CDMA_CF_INT; + + cdma_desc->memory_pointer = (uintptr_t)mem_ptr; + cdma_desc->status = 0; + cdma_desc->sync_flag_pointer = 0; + cdma_desc->sync_arguments = 0; + + cdma_desc->command_type = ctype; + cdma_desc->ctrl_data_ptr = (uintptr_t)ctrl_data_ptr; +} + +static u8 cadence_nand_check_desc_error(struct cdns_nand_ctrl *cdns_ctrl, + u32 desc_status) +{ + if (desc_status & CDMA_CS_ERP) + return STAT_ERASED; + + if (desc_status & CDMA_CS_UNCE) + return STAT_ECC_UNCORR; + + if (desc_status & CDMA_CS_ERR) { + dev_err(cdns_ctrl->dev, ":CDMA desc error flag detected.\n"); + return STAT_FAIL; + } + + if (FIELD_GET(CDMA_CS_MAXERR, desc_status)) + return |