18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2016, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/clk.h>
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/bitops.h>
88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
98c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h>
128c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/of_device.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/dma/qcom_bam_dma.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* NANDc reg offsets */
198c2ecf20Sopenharmony_ci#define	NAND_FLASH_CMD			0x00
208c2ecf20Sopenharmony_ci#define	NAND_ADDR0			0x04
218c2ecf20Sopenharmony_ci#define	NAND_ADDR1			0x08
228c2ecf20Sopenharmony_ci#define	NAND_FLASH_CHIP_SELECT		0x0c
238c2ecf20Sopenharmony_ci#define	NAND_EXEC_CMD			0x10
248c2ecf20Sopenharmony_ci#define	NAND_FLASH_STATUS		0x14
258c2ecf20Sopenharmony_ci#define	NAND_BUFFER_STATUS		0x18
268c2ecf20Sopenharmony_ci#define	NAND_DEV0_CFG0			0x20
278c2ecf20Sopenharmony_ci#define	NAND_DEV0_CFG1			0x24
288c2ecf20Sopenharmony_ci#define	NAND_DEV0_ECC_CFG		0x28
298c2ecf20Sopenharmony_ci#define	NAND_DEV1_ECC_CFG		0x2c
308c2ecf20Sopenharmony_ci#define	NAND_DEV1_CFG0			0x30
318c2ecf20Sopenharmony_ci#define	NAND_DEV1_CFG1			0x34
328c2ecf20Sopenharmony_ci#define	NAND_READ_ID			0x40
338c2ecf20Sopenharmony_ci#define	NAND_READ_STATUS		0x44
348c2ecf20Sopenharmony_ci#define	NAND_DEV_CMD0			0xa0
358c2ecf20Sopenharmony_ci#define	NAND_DEV_CMD1			0xa4
368c2ecf20Sopenharmony_ci#define	NAND_DEV_CMD2			0xa8
378c2ecf20Sopenharmony_ci#define	NAND_DEV_CMD_VLD		0xac
388c2ecf20Sopenharmony_ci#define	SFLASHC_BURST_CFG		0xe0
398c2ecf20Sopenharmony_ci#define	NAND_ERASED_CW_DETECT_CFG	0xe8
408c2ecf20Sopenharmony_ci#define	NAND_ERASED_CW_DETECT_STATUS	0xec
418c2ecf20Sopenharmony_ci#define	NAND_EBI2_ECC_BUF_CFG		0xf0
428c2ecf20Sopenharmony_ci#define	FLASH_BUF_ACC			0x100
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define	NAND_CTRL			0xf00
458c2ecf20Sopenharmony_ci#define	NAND_VERSION			0xf08
468c2ecf20Sopenharmony_ci#define	NAND_READ_LOCATION_0		0xf20
478c2ecf20Sopenharmony_ci#define	NAND_READ_LOCATION_1		0xf24
488c2ecf20Sopenharmony_ci#define	NAND_READ_LOCATION_2		0xf28
498c2ecf20Sopenharmony_ci#define	NAND_READ_LOCATION_3		0xf2c
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* dummy register offsets, used by write_reg_dma */
528c2ecf20Sopenharmony_ci#define	NAND_DEV_CMD1_RESTORE		0xdead
538c2ecf20Sopenharmony_ci#define	NAND_DEV_CMD_VLD_RESTORE	0xbeef
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* NAND_FLASH_CMD bits */
568c2ecf20Sopenharmony_ci#define	PAGE_ACC			BIT(4)
578c2ecf20Sopenharmony_ci#define	LAST_PAGE			BIT(5)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* NAND_FLASH_CHIP_SELECT bits */
608c2ecf20Sopenharmony_ci#define	NAND_DEV_SEL			0
618c2ecf20Sopenharmony_ci#define	DM_EN				BIT(2)
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* NAND_FLASH_STATUS bits */
648c2ecf20Sopenharmony_ci#define	FS_OP_ERR			BIT(4)
658c2ecf20Sopenharmony_ci#define	FS_READY_BSY_N			BIT(5)
668c2ecf20Sopenharmony_ci#define	FS_MPU_ERR			BIT(8)
678c2ecf20Sopenharmony_ci#define	FS_DEVICE_STS_ERR		BIT(16)
688c2ecf20Sopenharmony_ci#define	FS_DEVICE_WP			BIT(23)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* NAND_BUFFER_STATUS bits */
718c2ecf20Sopenharmony_ci#define	BS_UNCORRECTABLE_BIT		BIT(8)
728c2ecf20Sopenharmony_ci#define	BS_CORRECTABLE_ERR_MSK		0x1f
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* NAND_DEVn_CFG0 bits */
758c2ecf20Sopenharmony_ci#define	DISABLE_STATUS_AFTER_WRITE	4
768c2ecf20Sopenharmony_ci#define	CW_PER_PAGE			6
778c2ecf20Sopenharmony_ci#define	UD_SIZE_BYTES			9
788c2ecf20Sopenharmony_ci#define	ECC_PARITY_SIZE_BYTES_RS	19
798c2ecf20Sopenharmony_ci#define	SPARE_SIZE_BYTES		23
808c2ecf20Sopenharmony_ci#define	NUM_ADDR_CYCLES			27
818c2ecf20Sopenharmony_ci#define	STATUS_BFR_READ			30
828c2ecf20Sopenharmony_ci#define	SET_RD_MODE_AFTER_STATUS	31
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* NAND_DEVn_CFG0 bits */
858c2ecf20Sopenharmony_ci#define	DEV0_CFG1_ECC_DISABLE		0
868c2ecf20Sopenharmony_ci#define	WIDE_FLASH			1
878c2ecf20Sopenharmony_ci#define	NAND_RECOVERY_CYCLES		2
888c2ecf20Sopenharmony_ci#define	CS_ACTIVE_BSY			5
898c2ecf20Sopenharmony_ci#define	BAD_BLOCK_BYTE_NUM		6
908c2ecf20Sopenharmony_ci#define	BAD_BLOCK_IN_SPARE_AREA		16
918c2ecf20Sopenharmony_ci#define	WR_RD_BSY_GAP			17
928c2ecf20Sopenharmony_ci#define	ENABLE_BCH_ECC			27
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* NAND_DEV0_ECC_CFG bits */
958c2ecf20Sopenharmony_ci#define	ECC_CFG_ECC_DISABLE		0
968c2ecf20Sopenharmony_ci#define	ECC_SW_RESET			1
978c2ecf20Sopenharmony_ci#define	ECC_MODE			4
988c2ecf20Sopenharmony_ci#define	ECC_PARITY_SIZE_BYTES_BCH	8
998c2ecf20Sopenharmony_ci#define	ECC_NUM_DATA_BYTES		16
1008c2ecf20Sopenharmony_ci#define	ECC_FORCE_CLK_OPEN		30
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* NAND_DEV_CMD1 bits */
1038c2ecf20Sopenharmony_ci#define	READ_ADDR			0
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* NAND_DEV_CMD_VLD bits */
1068c2ecf20Sopenharmony_ci#define	READ_START_VLD			BIT(0)
1078c2ecf20Sopenharmony_ci#define	READ_STOP_VLD			BIT(1)
1088c2ecf20Sopenharmony_ci#define	WRITE_START_VLD			BIT(2)
1098c2ecf20Sopenharmony_ci#define	ERASE_START_VLD			BIT(3)
1108c2ecf20Sopenharmony_ci#define	SEQ_READ_START_VLD		BIT(4)
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* NAND_EBI2_ECC_BUF_CFG bits */
1138c2ecf20Sopenharmony_ci#define	NUM_STEPS			0
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* NAND_ERASED_CW_DETECT_CFG bits */
1168c2ecf20Sopenharmony_ci#define	ERASED_CW_ECC_MASK		1
1178c2ecf20Sopenharmony_ci#define	AUTO_DETECT_RES			0
1188c2ecf20Sopenharmony_ci#define	MASK_ECC			(1 << ERASED_CW_ECC_MASK)
1198c2ecf20Sopenharmony_ci#define	RESET_ERASED_DET		(1 << AUTO_DETECT_RES)
1208c2ecf20Sopenharmony_ci#define	ACTIVE_ERASED_DET		(0 << AUTO_DETECT_RES)
1218c2ecf20Sopenharmony_ci#define	CLR_ERASED_PAGE_DET		(RESET_ERASED_DET | MASK_ECC)
1228c2ecf20Sopenharmony_ci#define	SET_ERASED_PAGE_DET		(ACTIVE_ERASED_DET | MASK_ECC)
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/* NAND_ERASED_CW_DETECT_STATUS bits */
1258c2ecf20Sopenharmony_ci#define	PAGE_ALL_ERASED			BIT(7)
1268c2ecf20Sopenharmony_ci#define	CODEWORD_ALL_ERASED		BIT(6)
1278c2ecf20Sopenharmony_ci#define	PAGE_ERASED			BIT(5)
1288c2ecf20Sopenharmony_ci#define	CODEWORD_ERASED			BIT(4)
1298c2ecf20Sopenharmony_ci#define	ERASED_PAGE			(PAGE_ALL_ERASED | PAGE_ERASED)
1308c2ecf20Sopenharmony_ci#define	ERASED_CW			(CODEWORD_ALL_ERASED | CODEWORD_ERASED)
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/* NAND_READ_LOCATION_n bits */
1338c2ecf20Sopenharmony_ci#define READ_LOCATION_OFFSET		0
1348c2ecf20Sopenharmony_ci#define READ_LOCATION_SIZE		16
1358c2ecf20Sopenharmony_ci#define READ_LOCATION_LAST		31
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/* Version Mask */
1388c2ecf20Sopenharmony_ci#define	NAND_VERSION_MAJOR_MASK		0xf0000000
1398c2ecf20Sopenharmony_ci#define	NAND_VERSION_MAJOR_SHIFT	28
1408c2ecf20Sopenharmony_ci#define	NAND_VERSION_MINOR_MASK		0x0fff0000
1418c2ecf20Sopenharmony_ci#define	NAND_VERSION_MINOR_SHIFT	16
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/* NAND OP_CMDs */
1448c2ecf20Sopenharmony_ci#define	OP_PAGE_READ			0x2
1458c2ecf20Sopenharmony_ci#define	OP_PAGE_READ_WITH_ECC		0x3
1468c2ecf20Sopenharmony_ci#define	OP_PAGE_READ_WITH_ECC_SPARE	0x4
1478c2ecf20Sopenharmony_ci#define	OP_PROGRAM_PAGE			0x6
1488c2ecf20Sopenharmony_ci#define	OP_PAGE_PROGRAM_WITH_ECC	0x7
1498c2ecf20Sopenharmony_ci#define	OP_PROGRAM_PAGE_SPARE		0x9
1508c2ecf20Sopenharmony_ci#define	OP_BLOCK_ERASE			0xa
1518c2ecf20Sopenharmony_ci#define	OP_FETCH_ID			0xb
1528c2ecf20Sopenharmony_ci#define	OP_RESET_DEVICE			0xd
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/* Default Value for NAND_DEV_CMD_VLD */
1558c2ecf20Sopenharmony_ci#define NAND_DEV_CMD_VLD_VAL		(READ_START_VLD | WRITE_START_VLD | \
1568c2ecf20Sopenharmony_ci					 ERASE_START_VLD | SEQ_READ_START_VLD)
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* NAND_CTRL bits */
1598c2ecf20Sopenharmony_ci#define	BAM_MODE_EN			BIT(0)
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/*
1628c2ecf20Sopenharmony_ci * the NAND controller performs reads/writes with ECC in 516 byte chunks.
1638c2ecf20Sopenharmony_ci * the driver calls the chunks 'step' or 'codeword' interchangeably
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_ci#define	NANDC_STEP_SIZE			512
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/*
1688c2ecf20Sopenharmony_ci * the largest page size we support is 8K, this will have 16 steps/codewords
1698c2ecf20Sopenharmony_ci * of 512 bytes each
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_ci#define	MAX_NUM_STEPS			(SZ_8K / NANDC_STEP_SIZE)
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/* we read at most 3 registers per codeword scan */
1748c2ecf20Sopenharmony_ci#define	MAX_REG_RD			(3 * MAX_NUM_STEPS)
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/* ECC modes supported by the controller */
1778c2ecf20Sopenharmony_ci#define	ECC_NONE	BIT(0)
1788c2ecf20Sopenharmony_ci#define	ECC_RS_4BIT	BIT(1)
1798c2ecf20Sopenharmony_ci#define	ECC_BCH_4BIT	BIT(2)
1808c2ecf20Sopenharmony_ci#define	ECC_BCH_8BIT	BIT(3)
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci#define nandc_set_read_loc(nandc, reg, offset, size, is_last)	\
1838c2ecf20Sopenharmony_cinandc_set_reg(nandc, NAND_READ_LOCATION_##reg,			\
1848c2ecf20Sopenharmony_ci	      ((offset) << READ_LOCATION_OFFSET) |		\
1858c2ecf20Sopenharmony_ci	      ((size) << READ_LOCATION_SIZE) |			\
1868c2ecf20Sopenharmony_ci	      ((is_last) << READ_LOCATION_LAST))
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/*
1898c2ecf20Sopenharmony_ci * Returns the actual register address for all NAND_DEV_ registers
1908c2ecf20Sopenharmony_ci * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_ci#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/* Returns the NAND register physical address */
1958c2ecf20Sopenharmony_ci#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset))
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* Returns the dma address for reg read buffer */
1988c2ecf20Sopenharmony_ci#define reg_buf_dma_addr(chip, vaddr) \
1998c2ecf20Sopenharmony_ci	((chip)->reg_read_dma + \
2008c2ecf20Sopenharmony_ci	((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf))
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci#define QPIC_PER_CW_CMD_ELEMENTS	32
2038c2ecf20Sopenharmony_ci#define QPIC_PER_CW_CMD_SGL		32
2048c2ecf20Sopenharmony_ci#define QPIC_PER_CW_DATA_SGL		8
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci#define QPIC_NAND_COMPLETION_TIMEOUT	msecs_to_jiffies(2000)
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/*
2098c2ecf20Sopenharmony_ci * Flags used in DMA descriptor preparation helper functions
2108c2ecf20Sopenharmony_ci * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma)
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_ci/* Don't set the EOT in current tx BAM sgl */
2138c2ecf20Sopenharmony_ci#define NAND_BAM_NO_EOT			BIT(0)
2148c2ecf20Sopenharmony_ci/* Set the NWD flag in current BAM sgl */
2158c2ecf20Sopenharmony_ci#define NAND_BAM_NWD			BIT(1)
2168c2ecf20Sopenharmony_ci/* Finish writing in the current BAM sgl and start writing in another BAM sgl */
2178c2ecf20Sopenharmony_ci#define NAND_BAM_NEXT_SGL		BIT(2)
2188c2ecf20Sopenharmony_ci/*
2198c2ecf20Sopenharmony_ci * Erased codeword status is being used two times in single transfer so this
2208c2ecf20Sopenharmony_ci * flag will determine the current value of erased codeword status register
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ci#define NAND_ERASED_CW_SET		BIT(4)
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/*
2258c2ecf20Sopenharmony_ci * This data type corresponds to the BAM transaction which will be used for all
2268c2ecf20Sopenharmony_ci * NAND transfers.
2278c2ecf20Sopenharmony_ci * @bam_ce - the array of BAM command elements
2288c2ecf20Sopenharmony_ci * @cmd_sgl - sgl for NAND BAM command pipe
2298c2ecf20Sopenharmony_ci * @data_sgl - sgl for NAND BAM consumer/producer pipe
2308c2ecf20Sopenharmony_ci * @bam_ce_pos - the index in bam_ce which is available for next sgl
2318c2ecf20Sopenharmony_ci * @bam_ce_start - the index in bam_ce which marks the start position ce
2328c2ecf20Sopenharmony_ci *		   for current sgl. It will be used for size calculation
2338c2ecf20Sopenharmony_ci *		   for current sgl
2348c2ecf20Sopenharmony_ci * @cmd_sgl_pos - current index in command sgl.
2358c2ecf20Sopenharmony_ci * @cmd_sgl_start - start index in command sgl.
2368c2ecf20Sopenharmony_ci * @tx_sgl_pos - current index in data sgl for tx.
2378c2ecf20Sopenharmony_ci * @tx_sgl_start - start index in data sgl for tx.
2388c2ecf20Sopenharmony_ci * @rx_sgl_pos - current index in data sgl for rx.
2398c2ecf20Sopenharmony_ci * @rx_sgl_start - start index in data sgl for rx.
2408c2ecf20Sopenharmony_ci * @wait_second_completion - wait for second DMA desc completion before making
2418c2ecf20Sopenharmony_ci *			     the NAND transfer completion.
2428c2ecf20Sopenharmony_ci * @txn_done - completion for NAND transfer.
2438c2ecf20Sopenharmony_ci * @last_data_desc - last DMA desc in data channel (tx/rx).
2448c2ecf20Sopenharmony_ci * @last_cmd_desc - last DMA desc in command channel.
2458c2ecf20Sopenharmony_ci */
2468c2ecf20Sopenharmony_cistruct bam_transaction {
2478c2ecf20Sopenharmony_ci	struct bam_cmd_element *bam_ce;
2488c2ecf20Sopenharmony_ci	struct scatterlist *cmd_sgl;
2498c2ecf20Sopenharmony_ci	struct scatterlist *data_sgl;
2508c2ecf20Sopenharmony_ci	u32 bam_ce_pos;
2518c2ecf20Sopenharmony_ci	u32 bam_ce_start;
2528c2ecf20Sopenharmony_ci	u32 cmd_sgl_pos;
2538c2ecf20Sopenharmony_ci	u32 cmd_sgl_start;
2548c2ecf20Sopenharmony_ci	u32 tx_sgl_pos;
2558c2ecf20Sopenharmony_ci	u32 tx_sgl_start;
2568c2ecf20Sopenharmony_ci	u32 rx_sgl_pos;
2578c2ecf20Sopenharmony_ci	u32 rx_sgl_start;
2588c2ecf20Sopenharmony_ci	bool wait_second_completion;
2598c2ecf20Sopenharmony_ci	struct completion txn_done;
2608c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *last_data_desc;
2618c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *last_cmd_desc;
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/*
2658c2ecf20Sopenharmony_ci * This data type corresponds to the nand dma descriptor
2668c2ecf20Sopenharmony_ci * @list - list for desc_info
2678c2ecf20Sopenharmony_ci * @dir - DMA transfer direction
2688c2ecf20Sopenharmony_ci * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
2698c2ecf20Sopenharmony_ci *	      ADM
2708c2ecf20Sopenharmony_ci * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
2718c2ecf20Sopenharmony_ci * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
2728c2ecf20Sopenharmony_ci * @dma_desc - low level DMA engine descriptor
2738c2ecf20Sopenharmony_ci */
2748c2ecf20Sopenharmony_cistruct desc_info {
2758c2ecf20Sopenharmony_ci	struct list_head node;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	enum dma_data_direction dir;
2788c2ecf20Sopenharmony_ci	union {
2798c2ecf20Sopenharmony_ci		struct scatterlist adm_sgl;
2808c2ecf20Sopenharmony_ci		struct {
2818c2ecf20Sopenharmony_ci			struct scatterlist *bam_sgl;
2828c2ecf20Sopenharmony_ci			int sgl_cnt;
2838c2ecf20Sopenharmony_ci		};
2848c2ecf20Sopenharmony_ci	};
2858c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *dma_desc;
2868c2ecf20Sopenharmony_ci};
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/*
2898c2ecf20Sopenharmony_ci * holds the current register values that we want to write. acts as a contiguous
2908c2ecf20Sopenharmony_ci * chunk of memory which we use to write the controller registers through DMA.
2918c2ecf20Sopenharmony_ci */
2928c2ecf20Sopenharmony_cistruct nandc_regs {
2938c2ecf20Sopenharmony_ci	__le32 cmd;
2948c2ecf20Sopenharmony_ci	__le32 addr0;
2958c2ecf20Sopenharmony_ci	__le32 addr1;
2968c2ecf20Sopenharmony_ci	__le32 chip_sel;
2978c2ecf20Sopenharmony_ci	__le32 exec;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	__le32 cfg0;
3008c2ecf20Sopenharmony_ci	__le32 cfg1;
3018c2ecf20Sopenharmony_ci	__le32 ecc_bch_cfg;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	__le32 clrflashstatus;
3048c2ecf20Sopenharmony_ci	__le32 clrreadstatus;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	__le32 cmd1;
3078c2ecf20Sopenharmony_ci	__le32 vld;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	__le32 orig_cmd1;
3108c2ecf20Sopenharmony_ci	__le32 orig_vld;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	__le32 ecc_buf_cfg;
3138c2ecf20Sopenharmony_ci	__le32 read_location0;
3148c2ecf20Sopenharmony_ci	__le32 read_location1;
3158c2ecf20Sopenharmony_ci	__le32 read_location2;
3168c2ecf20Sopenharmony_ci	__le32 read_location3;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	__le32 erased_cw_detect_cfg_clr;
3198c2ecf20Sopenharmony_ci	__le32 erased_cw_detect_cfg_set;
3208c2ecf20Sopenharmony_ci};
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci/*
3238c2ecf20Sopenharmony_ci * NAND controller data struct
3248c2ecf20Sopenharmony_ci *
3258c2ecf20Sopenharmony_ci * @controller:			base controller structure
3268c2ecf20Sopenharmony_ci * @host_list:			list containing all the chips attached to the
3278c2ecf20Sopenharmony_ci *				controller
3288c2ecf20Sopenharmony_ci * @dev:			parent device
3298c2ecf20Sopenharmony_ci * @base:			MMIO base
3308c2ecf20Sopenharmony_ci * @base_phys:			physical base address of controller registers
3318c2ecf20Sopenharmony_ci * @base_dma:			dma base address of controller registers
3328c2ecf20Sopenharmony_ci * @core_clk:			controller clock
3338c2ecf20Sopenharmony_ci * @aon_clk:			another controller clock
3348c2ecf20Sopenharmony_ci *
3358c2ecf20Sopenharmony_ci * @chan:			dma channel
3368c2ecf20Sopenharmony_ci * @cmd_crci:			ADM DMA CRCI for command flow control
3378c2ecf20Sopenharmony_ci * @data_crci:			ADM DMA CRCI for data flow control
3388c2ecf20Sopenharmony_ci * @desc_list:			DMA descriptor list (list of desc_infos)
3398c2ecf20Sopenharmony_ci *
3408c2ecf20Sopenharmony_ci * @data_buffer:		our local DMA buffer for page read/writes,
3418c2ecf20Sopenharmony_ci *				used when we can't use the buffer provided
3428c2ecf20Sopenharmony_ci *				by upper layers directly
3438c2ecf20Sopenharmony_ci * @buf_size/count/start:	markers for chip->legacy.read_buf/write_buf
3448c2ecf20Sopenharmony_ci *				functions
3458c2ecf20Sopenharmony_ci * @reg_read_buf:		local buffer for reading back registers via DMA
3468c2ecf20Sopenharmony_ci * @reg_read_dma:		contains dma address for register read buffer
3478c2ecf20Sopenharmony_ci * @reg_read_pos:		marker for data read in reg_read_buf
3488c2ecf20Sopenharmony_ci *
3498c2ecf20Sopenharmony_ci * @regs:			a contiguous chunk of memory for DMA register
3508c2ecf20Sopenharmony_ci *				writes. contains the register values to be
3518c2ecf20Sopenharmony_ci *				written to controller
3528c2ecf20Sopenharmony_ci * @cmd1/vld:			some fixed controller register values
3538c2ecf20Sopenharmony_ci * @props:			properties of current NAND controller,
3548c2ecf20Sopenharmony_ci *				initialized via DT match data
3558c2ecf20Sopenharmony_ci * @max_cwperpage:		maximum QPIC codewords required. calculated
3568c2ecf20Sopenharmony_ci *				from all connected NAND devices pagesize
3578c2ecf20Sopenharmony_ci */
3588c2ecf20Sopenharmony_cistruct qcom_nand_controller {
3598c2ecf20Sopenharmony_ci	struct nand_controller controller;
3608c2ecf20Sopenharmony_ci	struct list_head host_list;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	struct device *dev;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	void __iomem *base;
3658c2ecf20Sopenharmony_ci	phys_addr_t base_phys;
3668c2ecf20Sopenharmony_ci	dma_addr_t base_dma;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	struct clk *core_clk;
3698c2ecf20Sopenharmony_ci	struct clk *aon_clk;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	union {
3728c2ecf20Sopenharmony_ci		/* will be used only by QPIC for BAM DMA */
3738c2ecf20Sopenharmony_ci		struct {
3748c2ecf20Sopenharmony_ci			struct dma_chan *tx_chan;
3758c2ecf20Sopenharmony_ci			struct dma_chan *rx_chan;
3768c2ecf20Sopenharmony_ci			struct dma_chan *cmd_chan;
3778c2ecf20Sopenharmony_ci		};
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		/* will be used only by EBI2 for ADM DMA */
3808c2ecf20Sopenharmony_ci		struct {
3818c2ecf20Sopenharmony_ci			struct dma_chan *chan;
3828c2ecf20Sopenharmony_ci			unsigned int cmd_crci;
3838c2ecf20Sopenharmony_ci			unsigned int data_crci;
3848c2ecf20Sopenharmony_ci		};
3858c2ecf20Sopenharmony_ci	};
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	struct list_head desc_list;
3888c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	u8		*data_buffer;
3918c2ecf20Sopenharmony_ci	int		buf_size;
3928c2ecf20Sopenharmony_ci	int		buf_count;
3938c2ecf20Sopenharmony_ci	int		buf_start;
3948c2ecf20Sopenharmony_ci	unsigned int	max_cwperpage;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	__le32 *reg_read_buf;
3978c2ecf20Sopenharmony_ci	dma_addr_t reg_read_dma;
3988c2ecf20Sopenharmony_ci	int reg_read_pos;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	struct nandc_regs *regs;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	u32 cmd1, vld;
4038c2ecf20Sopenharmony_ci	const struct qcom_nandc_props *props;
4048c2ecf20Sopenharmony_ci};
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/*
4078c2ecf20Sopenharmony_ci * NAND chip structure
4088c2ecf20Sopenharmony_ci *
4098c2ecf20Sopenharmony_ci * @chip:			base NAND chip structure
4108c2ecf20Sopenharmony_ci * @node:			list node to add itself to host_list in
4118c2ecf20Sopenharmony_ci *				qcom_nand_controller
4128c2ecf20Sopenharmony_ci *
4138c2ecf20Sopenharmony_ci * @cs:				chip select value for this chip
4148c2ecf20Sopenharmony_ci * @cw_size:			the number of bytes in a single step/codeword
4158c2ecf20Sopenharmony_ci *				of a page, consisting of all data, ecc, spare
4168c2ecf20Sopenharmony_ci *				and reserved bytes
4178c2ecf20Sopenharmony_ci * @cw_data:			the number of bytes within a codeword protected
4188c2ecf20Sopenharmony_ci *				by ECC
4198c2ecf20Sopenharmony_ci * @use_ecc:			request the controller to use ECC for the
4208c2ecf20Sopenharmony_ci *				upcoming read/write
4218c2ecf20Sopenharmony_ci * @bch_enabled:		flag to tell whether BCH ECC mode is used
4228c2ecf20Sopenharmony_ci * @ecc_bytes_hw:		ECC bytes used by controller hardware for this
4238c2ecf20Sopenharmony_ci *				chip
4248c2ecf20Sopenharmony_ci * @status:			value to be returned if NAND_CMD_STATUS command
4258c2ecf20Sopenharmony_ci *				is executed
4268c2ecf20Sopenharmony_ci * @last_command:		keeps track of last command on this chip. used
4278c2ecf20Sopenharmony_ci *				for reading correct status
4288c2ecf20Sopenharmony_ci *
4298c2ecf20Sopenharmony_ci * @cfg0, cfg1, cfg0_raw..:	NANDc register configurations needed for
4308c2ecf20Sopenharmony_ci *				ecc/non-ecc mode for the current nand flash
4318c2ecf20Sopenharmony_ci *				device
4328c2ecf20Sopenharmony_ci */
4338c2ecf20Sopenharmony_cistruct qcom_nand_host {
4348c2ecf20Sopenharmony_ci	struct nand_chip chip;
4358c2ecf20Sopenharmony_ci	struct list_head node;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	int cs;
4388c2ecf20Sopenharmony_ci	int cw_size;
4398c2ecf20Sopenharmony_ci	int cw_data;
4408c2ecf20Sopenharmony_ci	bool use_ecc;
4418c2ecf20Sopenharmony_ci	bool bch_enabled;
4428c2ecf20Sopenharmony_ci	int ecc_bytes_hw;
4438c2ecf20Sopenharmony_ci	int spare_bytes;
4448c2ecf20Sopenharmony_ci	int bbm_size;
4458c2ecf20Sopenharmony_ci	u8 status;
4468c2ecf20Sopenharmony_ci	int last_command;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	u32 cfg0, cfg1;
4498c2ecf20Sopenharmony_ci	u32 cfg0_raw, cfg1_raw;
4508c2ecf20Sopenharmony_ci	u32 ecc_buf_cfg;
4518c2ecf20Sopenharmony_ci	u32 ecc_bch_cfg;
4528c2ecf20Sopenharmony_ci	u32 clrflashstatus;
4538c2ecf20Sopenharmony_ci	u32 clrreadstatus;
4548c2ecf20Sopenharmony_ci};
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci/*
4578c2ecf20Sopenharmony_ci * This data type corresponds to the NAND controller properties which varies
4588c2ecf20Sopenharmony_ci * among different NAND controllers.
4598c2ecf20Sopenharmony_ci * @ecc_modes - ecc mode for NAND
4608c2ecf20Sopenharmony_ci * @is_bam - whether NAND controller is using BAM
4618c2ecf20Sopenharmony_ci * @is_qpic - whether NAND CTRL is part of qpic IP
4628c2ecf20Sopenharmony_ci * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_cistruct qcom_nandc_props {
4658c2ecf20Sopenharmony_ci	u32 ecc_modes;
4668c2ecf20Sopenharmony_ci	bool is_bam;
4678c2ecf20Sopenharmony_ci	bool is_qpic;
4688c2ecf20Sopenharmony_ci	u32 dev_cmd_reg_start;
4698c2ecf20Sopenharmony_ci};
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/* Frees the BAM transaction memory */
4728c2ecf20Sopenharmony_cistatic void free_bam_transaction(struct qcom_nand_controller *nandc)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn = nandc->bam_txn;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	devm_kfree(nandc->dev, bam_txn);
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci/* Allocates and Initializes the BAM transaction */
4808c2ecf20Sopenharmony_cistatic struct bam_transaction *
4818c2ecf20Sopenharmony_cialloc_bam_transaction(struct qcom_nand_controller *nandc)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn;
4848c2ecf20Sopenharmony_ci	size_t bam_txn_size;
4858c2ecf20Sopenharmony_ci	unsigned int num_cw = nandc->max_cwperpage;
4868c2ecf20Sopenharmony_ci	void *bam_txn_buf;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	bam_txn_size =
4898c2ecf20Sopenharmony_ci		sizeof(*bam_txn) + num_cw *
4908c2ecf20Sopenharmony_ci		((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) +
4918c2ecf20Sopenharmony_ci		(sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
4928c2ecf20Sopenharmony_ci		(sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL);
4958c2ecf20Sopenharmony_ci	if (!bam_txn_buf)
4968c2ecf20Sopenharmony_ci		return NULL;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	bam_txn = bam_txn_buf;
4998c2ecf20Sopenharmony_ci	bam_txn_buf += sizeof(*bam_txn);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	bam_txn->bam_ce = bam_txn_buf;
5028c2ecf20Sopenharmony_ci	bam_txn_buf +=
5038c2ecf20Sopenharmony_ci		sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	bam_txn->cmd_sgl = bam_txn_buf;
5068c2ecf20Sopenharmony_ci	bam_txn_buf +=
5078c2ecf20Sopenharmony_ci		sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	bam_txn->data_sgl = bam_txn_buf;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	init_completion(&bam_txn->txn_done);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return bam_txn;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci/* Clears the BAM transaction indexes */
5178c2ecf20Sopenharmony_cistatic void clear_bam_transaction(struct qcom_nand_controller *nandc)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn = nandc->bam_txn;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!nandc->props->is_bam)
5228c2ecf20Sopenharmony_ci		return;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	bam_txn->bam_ce_pos = 0;
5258c2ecf20Sopenharmony_ci	bam_txn->bam_ce_start = 0;
5268c2ecf20Sopenharmony_ci	bam_txn->cmd_sgl_pos = 0;
5278c2ecf20Sopenharmony_ci	bam_txn->cmd_sgl_start = 0;
5288c2ecf20Sopenharmony_ci	bam_txn->tx_sgl_pos = 0;
5298c2ecf20Sopenharmony_ci	bam_txn->tx_sgl_start = 0;
5308c2ecf20Sopenharmony_ci	bam_txn->rx_sgl_pos = 0;
5318c2ecf20Sopenharmony_ci	bam_txn->rx_sgl_start = 0;
5328c2ecf20Sopenharmony_ci	bam_txn->last_data_desc = NULL;
5338c2ecf20Sopenharmony_ci	bam_txn->wait_second_completion = false;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
5368c2ecf20Sopenharmony_ci		      QPIC_PER_CW_CMD_SGL);
5378c2ecf20Sopenharmony_ci	sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage *
5388c2ecf20Sopenharmony_ci		      QPIC_PER_CW_DATA_SGL);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	reinit_completion(&bam_txn->txn_done);
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci/* Callback for DMA descriptor completion */
5448c2ecf20Sopenharmony_cistatic void qpic_bam_dma_done(void *data)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn = data;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/*
5498c2ecf20Sopenharmony_ci	 * In case of data transfer with NAND, 2 callbacks will be generated.
5508c2ecf20Sopenharmony_ci	 * One for command channel and another one for data channel.
5518c2ecf20Sopenharmony_ci	 * If current transaction has data descriptors
5528c2ecf20Sopenharmony_ci	 * (i.e. wait_second_completion is true), then set this to false
5538c2ecf20Sopenharmony_ci	 * and wait for second DMA descriptor completion.
5548c2ecf20Sopenharmony_ci	 */
5558c2ecf20Sopenharmony_ci	if (bam_txn->wait_second_completion)
5568c2ecf20Sopenharmony_ci		bam_txn->wait_second_completion = false;
5578c2ecf20Sopenharmony_ci	else
5588c2ecf20Sopenharmony_ci		complete(&bam_txn->txn_done);
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	return container_of(chip, struct qcom_nand_host, chip);
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic inline struct qcom_nand_controller *
5678c2ecf20Sopenharmony_ciget_qcom_nand_controller(struct nand_chip *chip)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	return container_of(chip->controller, struct qcom_nand_controller,
5708c2ecf20Sopenharmony_ci			    controller);
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	return ioread32(nandc->base + offset);
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic inline void nandc_write(struct qcom_nand_controller *nandc, int offset,
5798c2ecf20Sopenharmony_ci			       u32 val)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	iowrite32(val, nandc->base + offset);
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc,
5858c2ecf20Sopenharmony_ci					  bool is_cpu)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	if (!nandc->props->is_bam)
5888c2ecf20Sopenharmony_ci		return;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (is_cpu)
5918c2ecf20Sopenharmony_ci		dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma,
5928c2ecf20Sopenharmony_ci					MAX_REG_RD *
5938c2ecf20Sopenharmony_ci					sizeof(*nandc->reg_read_buf),
5948c2ecf20Sopenharmony_ci					DMA_FROM_DEVICE);
5958c2ecf20Sopenharmony_ci	else
5968c2ecf20Sopenharmony_ci		dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma,
5978c2ecf20Sopenharmony_ci					   MAX_REG_RD *
5988c2ecf20Sopenharmony_ci					   sizeof(*nandc->reg_read_buf),
5998c2ecf20Sopenharmony_ci					   DMA_FROM_DEVICE);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	switch (offset) {
6058c2ecf20Sopenharmony_ci	case NAND_FLASH_CMD:
6068c2ecf20Sopenharmony_ci		return &regs->cmd;
6078c2ecf20Sopenharmony_ci	case NAND_ADDR0:
6088c2ecf20Sopenharmony_ci		return &regs->addr0;
6098c2ecf20Sopenharmony_ci	case NAND_ADDR1:
6108c2ecf20Sopenharmony_ci		return &regs->addr1;
6118c2ecf20Sopenharmony_ci	case NAND_FLASH_CHIP_SELECT:
6128c2ecf20Sopenharmony_ci		return &regs->chip_sel;
6138c2ecf20Sopenharmony_ci	case NAND_EXEC_CMD:
6148c2ecf20Sopenharmony_ci		return &regs->exec;
6158c2ecf20Sopenharmony_ci	case NAND_FLASH_STATUS:
6168c2ecf20Sopenharmony_ci		return &regs->clrflashstatus;
6178c2ecf20Sopenharmony_ci	case NAND_DEV0_CFG0:
6188c2ecf20Sopenharmony_ci		return &regs->cfg0;
6198c2ecf20Sopenharmony_ci	case NAND_DEV0_CFG1:
6208c2ecf20Sopenharmony_ci		return &regs->cfg1;
6218c2ecf20Sopenharmony_ci	case NAND_DEV0_ECC_CFG:
6228c2ecf20Sopenharmony_ci		return &regs->ecc_bch_cfg;
6238c2ecf20Sopenharmony_ci	case NAND_READ_STATUS:
6248c2ecf20Sopenharmony_ci		return &regs->clrreadstatus;
6258c2ecf20Sopenharmony_ci	case NAND_DEV_CMD1:
6268c2ecf20Sopenharmony_ci		return &regs->cmd1;
6278c2ecf20Sopenharmony_ci	case NAND_DEV_CMD1_RESTORE:
6288c2ecf20Sopenharmony_ci		return &regs->orig_cmd1;
6298c2ecf20Sopenharmony_ci	case NAND_DEV_CMD_VLD:
6308c2ecf20Sopenharmony_ci		return &regs->vld;
6318c2ecf20Sopenharmony_ci	case NAND_DEV_CMD_VLD_RESTORE:
6328c2ecf20Sopenharmony_ci		return &regs->orig_vld;
6338c2ecf20Sopenharmony_ci	case NAND_EBI2_ECC_BUF_CFG:
6348c2ecf20Sopenharmony_ci		return &regs->ecc_buf_cfg;
6358c2ecf20Sopenharmony_ci	case NAND_READ_LOCATION_0:
6368c2ecf20Sopenharmony_ci		return &regs->read_location0;
6378c2ecf20Sopenharmony_ci	case NAND_READ_LOCATION_1:
6388c2ecf20Sopenharmony_ci		return &regs->read_location1;
6398c2ecf20Sopenharmony_ci	case NAND_READ_LOCATION_2:
6408c2ecf20Sopenharmony_ci		return &regs->read_location2;
6418c2ecf20Sopenharmony_ci	case NAND_READ_LOCATION_3:
6428c2ecf20Sopenharmony_ci		return &regs->read_location3;
6438c2ecf20Sopenharmony_ci	default:
6448c2ecf20Sopenharmony_ci		return NULL;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic void nandc_set_reg(struct qcom_nand_controller *nandc, int offset,
6498c2ecf20Sopenharmony_ci			  u32 val)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct nandc_regs *regs = nandc->regs;
6528c2ecf20Sopenharmony_ci	__le32 *reg;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	reg = offset_to_nandc_reg(regs, offset);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (reg)
6578c2ecf20Sopenharmony_ci		*reg = cpu_to_le32(val);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci/* helper to configure address register values */
6618c2ecf20Sopenharmony_cistatic void set_address(struct qcom_nand_host *host, u16 column, int page)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
6648c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (chip->options & NAND_BUSWIDTH_16)
6678c2ecf20Sopenharmony_ci		column >>= 1;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column);
6708c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff);
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci/*
6748c2ecf20Sopenharmony_ci * update_rw_regs:	set up read/write register values, these will be
6758c2ecf20Sopenharmony_ci *			written to the NAND controller registers via DMA
6768c2ecf20Sopenharmony_ci *
6778c2ecf20Sopenharmony_ci * @num_cw:		number of steps for the read/write operation
6788c2ecf20Sopenharmony_ci * @read:		read or write operation
6798c2ecf20Sopenharmony_ci */
6808c2ecf20Sopenharmony_cistatic void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
6838c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
6848c2ecf20Sopenharmony_ci	u32 cmd, cfg0, cfg1, ecc_bch_cfg;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (read) {
6878c2ecf20Sopenharmony_ci		if (host->use_ecc)
6888c2ecf20Sopenharmony_ci			cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
6898c2ecf20Sopenharmony_ci		else
6908c2ecf20Sopenharmony_ci			cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE;
6918c2ecf20Sopenharmony_ci	} else {
6928c2ecf20Sopenharmony_ci		cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (host->use_ecc) {
6968c2ecf20Sopenharmony_ci		cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) |
6978c2ecf20Sopenharmony_ci				(num_cw - 1) << CW_PER_PAGE;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci		cfg1 = host->cfg1;
7008c2ecf20Sopenharmony_ci		ecc_bch_cfg = host->ecc_bch_cfg;
7018c2ecf20Sopenharmony_ci	} else {
7028c2ecf20Sopenharmony_ci		cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) |
7038c2ecf20Sopenharmony_ci				(num_cw - 1) << CW_PER_PAGE;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci		cfg1 = host->cfg1_raw;
7068c2ecf20Sopenharmony_ci		ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_FLASH_CMD, cmd);
7108c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0);
7118c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1);
7128c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg);
7138c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
7148c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
7158c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
7168c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (read)
7198c2ecf20Sopenharmony_ci		nandc_set_read_loc(nandc, 0, 0, host->use_ecc ?
7208c2ecf20Sopenharmony_ci				   host->cw_data : host->cw_size, 1);
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci/*
7248c2ecf20Sopenharmony_ci * Maps the scatter gather list for DMA transfer and forms the DMA descriptor
7258c2ecf20Sopenharmony_ci * for BAM. This descriptor will be added in the NAND DMA descriptor queue
7268c2ecf20Sopenharmony_ci * which will be submitted to DMA engine.
7278c2ecf20Sopenharmony_ci */
7288c2ecf20Sopenharmony_cistatic int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
7298c2ecf20Sopenharmony_ci				  struct dma_chan *chan,
7308c2ecf20Sopenharmony_ci				  unsigned long flags)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	struct desc_info *desc;
7338c2ecf20Sopenharmony_ci	struct scatterlist *sgl;
7348c2ecf20Sopenharmony_ci	unsigned int sgl_cnt;
7358c2ecf20Sopenharmony_ci	int ret;
7368c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn = nandc->bam_txn;
7378c2ecf20Sopenharmony_ci	enum dma_transfer_direction dir_eng;
7388c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *dma_desc;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
7418c2ecf20Sopenharmony_ci	if (!desc)
7428c2ecf20Sopenharmony_ci		return -ENOMEM;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (chan == nandc->cmd_chan) {
7458c2ecf20Sopenharmony_ci		sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start];
7468c2ecf20Sopenharmony_ci		sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start;
7478c2ecf20Sopenharmony_ci		bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos;
7488c2ecf20Sopenharmony_ci		dir_eng = DMA_MEM_TO_DEV;
7498c2ecf20Sopenharmony_ci		desc->dir = DMA_TO_DEVICE;
7508c2ecf20Sopenharmony_ci	} else if (chan == nandc->tx_chan) {
7518c2ecf20Sopenharmony_ci		sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start];
7528c2ecf20Sopenharmony_ci		sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start;
7538c2ecf20Sopenharmony_ci		bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos;
7548c2ecf20Sopenharmony_ci		dir_eng = DMA_MEM_TO_DEV;
7558c2ecf20Sopenharmony_ci		desc->dir = DMA_TO_DEVICE;
7568c2ecf20Sopenharmony_ci	} else {
7578c2ecf20Sopenharmony_ci		sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start];
7588c2ecf20Sopenharmony_ci		sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start;
7598c2ecf20Sopenharmony_ci		bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos;
7608c2ecf20Sopenharmony_ci		dir_eng = DMA_DEV_TO_MEM;
7618c2ecf20Sopenharmony_ci		desc->dir = DMA_FROM_DEVICE;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	sg_mark_end(sgl + sgl_cnt - 1);
7658c2ecf20Sopenharmony_ci	ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
7668c2ecf20Sopenharmony_ci	if (ret == 0) {
7678c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure in mapping desc\n");
7688c2ecf20Sopenharmony_ci		kfree(desc);
7698c2ecf20Sopenharmony_ci		return -ENOMEM;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	desc->sgl_cnt = sgl_cnt;
7738c2ecf20Sopenharmony_ci	desc->bam_sgl = sgl;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng,
7768c2ecf20Sopenharmony_ci					   flags);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	if (!dma_desc) {
7798c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure in prep desc\n");
7808c2ecf20Sopenharmony_ci		dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
7818c2ecf20Sopenharmony_ci		kfree(desc);
7828c2ecf20Sopenharmony_ci		return -EINVAL;
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	desc->dma_desc = dma_desc;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* update last data/command descriptor */
7888c2ecf20Sopenharmony_ci	if (chan == nandc->cmd_chan)
7898c2ecf20Sopenharmony_ci		bam_txn->last_cmd_desc = dma_desc;
7908c2ecf20Sopenharmony_ci	else
7918c2ecf20Sopenharmony_ci		bam_txn->last_data_desc = dma_desc;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	list_add_tail(&desc->node, &nandc->desc_list);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	return 0;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci/*
7998c2ecf20Sopenharmony_ci * Prepares the command descriptor for BAM DMA which will be used for NAND
8008c2ecf20Sopenharmony_ci * register reads and writes. The command descriptor requires the command
8018c2ecf20Sopenharmony_ci * to be formed in command element type so this function uses the command
8028c2ecf20Sopenharmony_ci * element from bam transaction ce array and fills the same with required
8038c2ecf20Sopenharmony_ci * data. A single SGL can contain multiple command elements so
8048c2ecf20Sopenharmony_ci * NAND_BAM_NEXT_SGL will be used for starting the separate SGL
8058c2ecf20Sopenharmony_ci * after the current command element.
8068c2ecf20Sopenharmony_ci */
8078c2ecf20Sopenharmony_cistatic int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
8088c2ecf20Sopenharmony_ci				 int reg_off, const void *vaddr,
8098c2ecf20Sopenharmony_ci				 int size, unsigned int flags)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	int bam_ce_size;
8128c2ecf20Sopenharmony_ci	int i, ret;
8138c2ecf20Sopenharmony_ci	struct bam_cmd_element *bam_ce_buffer;
8148c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn = nandc->bam_txn;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos];
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* fill the command desc */
8198c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++) {
8208c2ecf20Sopenharmony_ci		if (read)
8218c2ecf20Sopenharmony_ci			bam_prep_ce(&bam_ce_buffer[i],
8228c2ecf20Sopenharmony_ci				    nandc_reg_phys(nandc, reg_off + 4 * i),
8238c2ecf20Sopenharmony_ci				    BAM_READ_COMMAND,
8248c2ecf20Sopenharmony_ci				    reg_buf_dma_addr(nandc,
8258c2ecf20Sopenharmony_ci						     (__le32 *)vaddr + i));
8268c2ecf20Sopenharmony_ci		else
8278c2ecf20Sopenharmony_ci			bam_prep_ce_le32(&bam_ce_buffer[i],
8288c2ecf20Sopenharmony_ci					 nandc_reg_phys(nandc, reg_off + 4 * i),
8298c2ecf20Sopenharmony_ci					 BAM_WRITE_COMMAND,
8308c2ecf20Sopenharmony_ci					 *((__le32 *)vaddr + i));
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	bam_txn->bam_ce_pos += size;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	/* use the separate sgl after this command */
8368c2ecf20Sopenharmony_ci	if (flags & NAND_BAM_NEXT_SGL) {
8378c2ecf20Sopenharmony_ci		bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start];
8388c2ecf20Sopenharmony_ci		bam_ce_size = (bam_txn->bam_ce_pos -
8398c2ecf20Sopenharmony_ci				bam_txn->bam_ce_start) *
8408c2ecf20Sopenharmony_ci				sizeof(struct bam_cmd_element);
8418c2ecf20Sopenharmony_ci		sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos],
8428c2ecf20Sopenharmony_ci			   bam_ce_buffer, bam_ce_size);
8438c2ecf20Sopenharmony_ci		bam_txn->cmd_sgl_pos++;
8448c2ecf20Sopenharmony_ci		bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci		if (flags & NAND_BAM_NWD) {
8478c2ecf20Sopenharmony_ci			ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
8488c2ecf20Sopenharmony_ci						     DMA_PREP_FENCE |
8498c2ecf20Sopenharmony_ci						     DMA_PREP_CMD);
8508c2ecf20Sopenharmony_ci			if (ret)
8518c2ecf20Sopenharmony_ci				return ret;
8528c2ecf20Sopenharmony_ci		}
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	return 0;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci/*
8598c2ecf20Sopenharmony_ci * Prepares the data descriptor for BAM DMA which will be used for NAND
8608c2ecf20Sopenharmony_ci * data reads and writes.
8618c2ecf20Sopenharmony_ci */
8628c2ecf20Sopenharmony_cistatic int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
8638c2ecf20Sopenharmony_ci				  const void *vaddr,
8648c2ecf20Sopenharmony_ci				  int size, unsigned int flags)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	int ret;
8678c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn = nandc->bam_txn;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (read) {
8708c2ecf20Sopenharmony_ci		sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos],
8718c2ecf20Sopenharmony_ci			   vaddr, size);
8728c2ecf20Sopenharmony_ci		bam_txn->rx_sgl_pos++;
8738c2ecf20Sopenharmony_ci	} else {
8748c2ecf20Sopenharmony_ci		sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos],
8758c2ecf20Sopenharmony_ci			   vaddr, size);
8768c2ecf20Sopenharmony_ci		bam_txn->tx_sgl_pos++;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci		/*
8798c2ecf20Sopenharmony_ci		 * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag
8808c2ecf20Sopenharmony_ci		 * is not set, form the DMA descriptor
8818c2ecf20Sopenharmony_ci		 */
8828c2ecf20Sopenharmony_ci		if (!(flags & NAND_BAM_NO_EOT)) {
8838c2ecf20Sopenharmony_ci			ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
8848c2ecf20Sopenharmony_ci						     DMA_PREP_INTERRUPT);
8858c2ecf20Sopenharmony_ci			if (ret)
8868c2ecf20Sopenharmony_ci				return ret;
8878c2ecf20Sopenharmony_ci		}
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	return 0;
8918c2ecf20Sopenharmony_ci}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
8948c2ecf20Sopenharmony_ci			     int reg_off, const void *vaddr, int size,
8958c2ecf20Sopenharmony_ci			     bool flow_control)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	struct desc_info *desc;
8988c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *dma_desc;
8998c2ecf20Sopenharmony_ci	struct scatterlist *sgl;
9008c2ecf20Sopenharmony_ci	struct dma_slave_config slave_conf;
9018c2ecf20Sopenharmony_ci	enum dma_transfer_direction dir_eng;
9028c2ecf20Sopenharmony_ci	int ret;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
9058c2ecf20Sopenharmony_ci	if (!desc)
9068c2ecf20Sopenharmony_ci		return -ENOMEM;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	sgl = &desc->adm_sgl;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	sg_init_one(sgl, vaddr, size);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	if (read) {
9138c2ecf20Sopenharmony_ci		dir_eng = DMA_DEV_TO_MEM;
9148c2ecf20Sopenharmony_ci		desc->dir = DMA_FROM_DEVICE;
9158c2ecf20Sopenharmony_ci	} else {
9168c2ecf20Sopenharmony_ci		dir_eng = DMA_MEM_TO_DEV;
9178c2ecf20Sopenharmony_ci		desc->dir = DMA_TO_DEVICE;
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir);
9218c2ecf20Sopenharmony_ci	if (ret == 0) {
9228c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9238c2ecf20Sopenharmony_ci		goto err;
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	memset(&slave_conf, 0x00, sizeof(slave_conf));
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	slave_conf.device_fc = flow_control;
9298c2ecf20Sopenharmony_ci	if (read) {
9308c2ecf20Sopenharmony_ci		slave_conf.src_maxburst = 16;
9318c2ecf20Sopenharmony_ci		slave_conf.src_addr = nandc->base_dma + reg_off;
9328c2ecf20Sopenharmony_ci		slave_conf.slave_id = nandc->data_crci;
9338c2ecf20Sopenharmony_ci	} else {
9348c2ecf20Sopenharmony_ci		slave_conf.dst_maxburst = 16;
9358c2ecf20Sopenharmony_ci		slave_conf.dst_addr = nandc->base_dma + reg_off;
9368c2ecf20Sopenharmony_ci		slave_conf.slave_id = nandc->cmd_crci;
9378c2ecf20Sopenharmony_ci	}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	ret = dmaengine_slave_config(nandc->chan, &slave_conf);
9408c2ecf20Sopenharmony_ci	if (ret) {
9418c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failed to configure dma channel\n");
9428c2ecf20Sopenharmony_ci		goto err;
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0);
9468c2ecf20Sopenharmony_ci	if (!dma_desc) {
9478c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failed to prepare desc\n");
9488c2ecf20Sopenharmony_ci		ret = -EINVAL;
9498c2ecf20Sopenharmony_ci		goto err;
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	desc->dma_desc = dma_desc;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	list_add_tail(&desc->node, &nandc->desc_list);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	return 0;
9578c2ecf20Sopenharmony_cierr:
9588c2ecf20Sopenharmony_ci	kfree(desc);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	return ret;
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci/*
9648c2ecf20Sopenharmony_ci * read_reg_dma:	prepares a descriptor to read a given number of
9658c2ecf20Sopenharmony_ci *			contiguous registers to the reg_read_buf pointer
9668c2ecf20Sopenharmony_ci *
9678c2ecf20Sopenharmony_ci * @first:		offset of the first register in the contiguous block
9688c2ecf20Sopenharmony_ci * @num_regs:		number of registers to read
9698c2ecf20Sopenharmony_ci * @flags:		flags to control DMA descriptor preparation
9708c2ecf20Sopenharmony_ci */
9718c2ecf20Sopenharmony_cistatic int read_reg_dma(struct qcom_nand_controller *nandc, int first,
9728c2ecf20Sopenharmony_ci			int num_regs, unsigned int flags)
9738c2ecf20Sopenharmony_ci{
9748c2ecf20Sopenharmony_ci	bool flow_control = false;
9758c2ecf20Sopenharmony_ci	void *vaddr;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
9788c2ecf20Sopenharmony_ci	nandc->reg_read_pos += num_regs;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
9818c2ecf20Sopenharmony_ci		first = dev_cmd_reg_addr(nandc, first);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (nandc->props->is_bam)
9848c2ecf20Sopenharmony_ci		return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
9858c2ecf20Sopenharmony_ci					     num_regs, flags);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
9888c2ecf20Sopenharmony_ci		flow_control = true;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	return prep_adm_dma_desc(nandc, true, first, vaddr,
9918c2ecf20Sopenharmony_ci				 num_regs * sizeof(u32), flow_control);
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci/*
9958c2ecf20Sopenharmony_ci * write_reg_dma:	prepares a descriptor to write a given number of
9968c2ecf20Sopenharmony_ci *			contiguous registers
9978c2ecf20Sopenharmony_ci *
9988c2ecf20Sopenharmony_ci * @first:		offset of the first register in the contiguous block
9998c2ecf20Sopenharmony_ci * @num_regs:		number of registers to write
10008c2ecf20Sopenharmony_ci * @flags:		flags to control DMA descriptor preparation
10018c2ecf20Sopenharmony_ci */
10028c2ecf20Sopenharmony_cistatic int write_reg_dma(struct qcom_nand_controller *nandc, int first,
10038c2ecf20Sopenharmony_ci			 int num_regs, unsigned int flags)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	bool flow_control = false;
10068c2ecf20Sopenharmony_ci	struct nandc_regs *regs = nandc->regs;
10078c2ecf20Sopenharmony_ci	void *vaddr;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	vaddr = offset_to_nandc_reg(regs, first);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	if (first == NAND_ERASED_CW_DETECT_CFG) {
10128c2ecf20Sopenharmony_ci		if (flags & NAND_ERASED_CW_SET)
10138c2ecf20Sopenharmony_ci			vaddr = &regs->erased_cw_detect_cfg_set;
10148c2ecf20Sopenharmony_ci		else
10158c2ecf20Sopenharmony_ci			vaddr = &regs->erased_cw_detect_cfg_clr;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	if (first == NAND_EXEC_CMD)
10198c2ecf20Sopenharmony_ci		flags |= NAND_BAM_NWD;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1)
10228c2ecf20Sopenharmony_ci		first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
10258c2ecf20Sopenharmony_ci		first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (nandc->props->is_bam)
10288c2ecf20Sopenharmony_ci		return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
10298c2ecf20Sopenharmony_ci					     num_regs, flags);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	if (first == NAND_FLASH_CMD)
10328c2ecf20Sopenharmony_ci		flow_control = true;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	return prep_adm_dma_desc(nandc, false, first, vaddr,
10358c2ecf20Sopenharmony_ci				 num_regs * sizeof(u32), flow_control);
10368c2ecf20Sopenharmony_ci}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci/*
10398c2ecf20Sopenharmony_ci * read_data_dma:	prepares a DMA descriptor to transfer data from the
10408c2ecf20Sopenharmony_ci *			controller's internal buffer to the buffer 'vaddr'
10418c2ecf20Sopenharmony_ci *
10428c2ecf20Sopenharmony_ci * @reg_off:		offset within the controller's data buffer
10438c2ecf20Sopenharmony_ci * @vaddr:		virtual address of the buffer we want to write to
10448c2ecf20Sopenharmony_ci * @size:		DMA transaction size in bytes
10458c2ecf20Sopenharmony_ci * @flags:		flags to control DMA descriptor preparation
10468c2ecf20Sopenharmony_ci */
10478c2ecf20Sopenharmony_cistatic int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
10488c2ecf20Sopenharmony_ci			 const u8 *vaddr, int size, unsigned int flags)
10498c2ecf20Sopenharmony_ci{
10508c2ecf20Sopenharmony_ci	if (nandc->props->is_bam)
10518c2ecf20Sopenharmony_ci		return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci/*
10578c2ecf20Sopenharmony_ci * write_data_dma:	prepares a DMA descriptor to transfer data from
10588c2ecf20Sopenharmony_ci *			'vaddr' to the controller's internal buffer
10598c2ecf20Sopenharmony_ci *
10608c2ecf20Sopenharmony_ci * @reg_off:		offset within the controller's data buffer
10618c2ecf20Sopenharmony_ci * @vaddr:		virtual address of the buffer we want to read from
10628c2ecf20Sopenharmony_ci * @size:		DMA transaction size in bytes
10638c2ecf20Sopenharmony_ci * @flags:		flags to control DMA descriptor preparation
10648c2ecf20Sopenharmony_ci */
10658c2ecf20Sopenharmony_cistatic int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
10668c2ecf20Sopenharmony_ci			  const u8 *vaddr, int size, unsigned int flags)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	if (nandc->props->is_bam)
10698c2ecf20Sopenharmony_ci		return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci/*
10758c2ecf20Sopenharmony_ci * Helper to prepare DMA descriptors for configuring registers
10768c2ecf20Sopenharmony_ci * before reading a NAND page.
10778c2ecf20Sopenharmony_ci */
10788c2ecf20Sopenharmony_cistatic void config_nand_page_read(struct qcom_nand_controller *nandc)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_ADDR0, 2, 0);
10818c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
10828c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
10838c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0);
10848c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1,
10858c2ecf20Sopenharmony_ci		      NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
10868c2ecf20Sopenharmony_ci}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci/*
10898c2ecf20Sopenharmony_ci * Helper to prepare DMA descriptors for configuring registers
10908c2ecf20Sopenharmony_ci * before reading each codeword in NAND page.
10918c2ecf20Sopenharmony_ci */
10928c2ecf20Sopenharmony_cistatic void
10938c2ecf20Sopenharmony_ciconfig_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	if (nandc->props->is_bam)
10968c2ecf20Sopenharmony_ci		write_reg_dma(nandc, NAND_READ_LOCATION_0, 4,
10978c2ecf20Sopenharmony_ci			      NAND_BAM_NEXT_SGL);
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
11008c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (use_ecc) {
11038c2ecf20Sopenharmony_ci		read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
11048c2ecf20Sopenharmony_ci		read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
11058c2ecf20Sopenharmony_ci			     NAND_BAM_NEXT_SGL);
11068c2ecf20Sopenharmony_ci	} else {
11078c2ecf20Sopenharmony_ci		read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
11088c2ecf20Sopenharmony_ci	}
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci/*
11128c2ecf20Sopenharmony_ci * Helper to prepare dma descriptors to configure registers needed for reading a
11138c2ecf20Sopenharmony_ci * single codeword in page
11148c2ecf20Sopenharmony_ci */
11158c2ecf20Sopenharmony_cistatic void
11168c2ecf20Sopenharmony_ciconfig_nand_single_cw_page_read(struct qcom_nand_controller *nandc,
11178c2ecf20Sopenharmony_ci				bool use_ecc)
11188c2ecf20Sopenharmony_ci{
11198c2ecf20Sopenharmony_ci	config_nand_page_read(nandc);
11208c2ecf20Sopenharmony_ci	config_nand_cw_read(nandc, use_ecc);
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci/*
11248c2ecf20Sopenharmony_ci * Helper to prepare DMA descriptors used to configure registers needed for
11258c2ecf20Sopenharmony_ci * before writing a NAND page.
11268c2ecf20Sopenharmony_ci */
11278c2ecf20Sopenharmony_cistatic void config_nand_page_write(struct qcom_nand_controller *nandc)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_ADDR0, 2, 0);
11308c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
11318c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
11328c2ecf20Sopenharmony_ci		      NAND_BAM_NEXT_SGL);
11338c2ecf20Sopenharmony_ci}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci/*
11368c2ecf20Sopenharmony_ci * Helper to prepare DMA descriptors for configuring registers
11378c2ecf20Sopenharmony_ci * before writing each codeword in NAND page.
11388c2ecf20Sopenharmony_ci */
11398c2ecf20Sopenharmony_cistatic void config_nand_cw_write(struct qcom_nand_controller *nandc)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
11428c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
11478c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci/*
11518c2ecf20Sopenharmony_ci * the following functions are used within chip->legacy.cmdfunc() to
11528c2ecf20Sopenharmony_ci * perform different NAND_CMD_* commands
11538c2ecf20Sopenharmony_ci */
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci/* sets up descriptors for NAND_CMD_PARAM */
11568c2ecf20Sopenharmony_cistatic int nandc_param(struct qcom_nand_host *host)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
11598c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	/*
11628c2ecf20Sopenharmony_ci	 * NAND_CMD_PARAM is called before we know much about the FLASH chip
11638c2ecf20Sopenharmony_ci	 * in use. we configure the controller to perform a raw read of 512
11648c2ecf20Sopenharmony_ci	 * bytes to read onfi params
11658c2ecf20Sopenharmony_ci	 */
11668c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | PAGE_ACC | LAST_PAGE);
11678c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_ADDR0, 0);
11688c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_ADDR1, 0);
11698c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
11708c2ecf20Sopenharmony_ci					| 512 << UD_SIZE_BYTES
11718c2ecf20Sopenharmony_ci					| 5 << NUM_ADDR_CYCLES
11728c2ecf20Sopenharmony_ci					| 0 << SPARE_SIZE_BYTES);
11738c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES
11748c2ecf20Sopenharmony_ci					| 0 << CS_ACTIVE_BSY
11758c2ecf20Sopenharmony_ci					| 17 << BAD_BLOCK_BYTE_NUM
11768c2ecf20Sopenharmony_ci					| 1 << BAD_BLOCK_IN_SPARE_AREA
11778c2ecf20Sopenharmony_ci					| 2 << WR_RD_BSY_GAP
11788c2ecf20Sopenharmony_ci					| 0 << WIDE_FLASH
11798c2ecf20Sopenharmony_ci					| 1 << DEV0_CFG1_ECC_DISABLE);
11808c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	/* configure CMD1 and VLD for ONFI param probing */
11838c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
11848c2ecf20Sopenharmony_ci		      (nandc->vld & ~READ_START_VLD));
11858c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV_CMD1,
11868c2ecf20Sopenharmony_ci		      (nandc->cmd1 & ~(0xFF << READ_ADDR))
11878c2ecf20Sopenharmony_ci		      | NAND_CMD_PARAM << READ_ADDR);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
11928c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
11938c2ecf20Sopenharmony_ci	nandc_set_read_loc(nandc, 0, 0, 512, 1);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
11968c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	nandc->buf_count = 512;
11998c2ecf20Sopenharmony_ci	memset(nandc->data_buffer, 0xff, nandc->buf_count);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	config_nand_single_cw_page_read(nandc, false);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
12048c2ecf20Sopenharmony_ci		      nandc->buf_count, 0);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	/* restore CMD1 and VLD regs */
12078c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
12088c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	return 0;
12118c2ecf20Sopenharmony_ci}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci/* sets up descriptors for NAND_CMD_ERASE1 */
12148c2ecf20Sopenharmony_cistatic int erase_block(struct qcom_nand_host *host, int page_addr)
12158c2ecf20Sopenharmony_ci{
12168c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
12178c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_FLASH_CMD,
12208c2ecf20Sopenharmony_ci		      OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE);
12218c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_ADDR0, page_addr);
12228c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_ADDR1, 0);
12238c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV0_CFG0,
12248c2ecf20Sopenharmony_ci		      host->cfg0_raw & ~(7 << CW_PER_PAGE));
12258c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw);
12268c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
12278c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
12288c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
12318c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
12328c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
12378c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	return 0;
12408c2ecf20Sopenharmony_ci}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci/* sets up descriptors for NAND_CMD_READID */
12438c2ecf20Sopenharmony_cistatic int read_id(struct qcom_nand_host *host, int column)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
12468c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	if (column == -1)
12498c2ecf20Sopenharmony_ci		return 0;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_FLASH_CMD, OP_FETCH_ID);
12528c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_ADDR0, column);
12538c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_ADDR1, 0);
12548c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT,
12558c2ecf20Sopenharmony_ci		      nandc->props->is_bam ? 0 : DM_EN);
12568c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
12598c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	return 0;
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci/* sets up descriptors for NAND_CMD_RESET */
12678c2ecf20Sopenharmony_cistatic int reset(struct qcom_nand_host *host)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
12708c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_FLASH_CMD, OP_RESET_DEVICE);
12738c2ecf20Sopenharmony_ci	nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
12768c2ecf20Sopenharmony_ci	write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	return 0;
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci/* helpers to submit/free our list of dma descriptors */
12848c2ecf20Sopenharmony_cistatic int submit_descs(struct qcom_nand_controller *nandc)
12858c2ecf20Sopenharmony_ci{
12868c2ecf20Sopenharmony_ci	struct desc_info *desc;
12878c2ecf20Sopenharmony_ci	dma_cookie_t cookie = 0;
12888c2ecf20Sopenharmony_ci	struct bam_transaction *bam_txn = nandc->bam_txn;
12898c2ecf20Sopenharmony_ci	int r;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	if (nandc->props->is_bam) {
12928c2ecf20Sopenharmony_ci		if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
12938c2ecf20Sopenharmony_ci			r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
12948c2ecf20Sopenharmony_ci			if (r)
12958c2ecf20Sopenharmony_ci				return r;
12968c2ecf20Sopenharmony_ci		}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci		if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
12998c2ecf20Sopenharmony_ci			r = prepare_bam_async_desc(nandc, nandc->tx_chan,
13008c2ecf20Sopenharmony_ci						   DMA_PREP_INTERRUPT);
13018c2ecf20Sopenharmony_ci			if (r)
13028c2ecf20Sopenharmony_ci				return r;
13038c2ecf20Sopenharmony_ci		}
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci		if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
13068c2ecf20Sopenharmony_ci			r = prepare_bam_async_desc(nandc, nandc->cmd_chan,
13078c2ecf20Sopenharmony_ci						   DMA_PREP_CMD);
13088c2ecf20Sopenharmony_ci			if (r)
13098c2ecf20Sopenharmony_ci				return r;
13108c2ecf20Sopenharmony_ci		}
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	list_for_each_entry(desc, &nandc->desc_list, node)
13148c2ecf20Sopenharmony_ci		cookie = dmaengine_submit(desc->dma_desc);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	if (nandc->props->is_bam) {
13178c2ecf20Sopenharmony_ci		bam_txn->last_cmd_desc->callback = qpic_bam_dma_done;
13188c2ecf20Sopenharmony_ci		bam_txn->last_cmd_desc->callback_param = bam_txn;
13198c2ecf20Sopenharmony_ci		if (bam_txn->last_data_desc) {
13208c2ecf20Sopenharmony_ci			bam_txn->last_data_desc->callback = qpic_bam_dma_done;
13218c2ecf20Sopenharmony_ci			bam_txn->last_data_desc->callback_param = bam_txn;
13228c2ecf20Sopenharmony_ci			bam_txn->wait_second_completion = true;
13238c2ecf20Sopenharmony_ci		}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci		dma_async_issue_pending(nandc->tx_chan);
13268c2ecf20Sopenharmony_ci		dma_async_issue_pending(nandc->rx_chan);
13278c2ecf20Sopenharmony_ci		dma_async_issue_pending(nandc->cmd_chan);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci		if (!wait_for_completion_timeout(&bam_txn->txn_done,
13308c2ecf20Sopenharmony_ci						 QPIC_NAND_COMPLETION_TIMEOUT))
13318c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
13328c2ecf20Sopenharmony_ci	} else {
13338c2ecf20Sopenharmony_ci		if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
13348c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
13358c2ecf20Sopenharmony_ci	}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	return 0;
13388c2ecf20Sopenharmony_ci}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_cistatic void free_descs(struct qcom_nand_controller *nandc)
13418c2ecf20Sopenharmony_ci{
13428c2ecf20Sopenharmony_ci	struct desc_info *desc, *n;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
13458c2ecf20Sopenharmony_ci		list_del(&desc->node);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci		if (nandc->props->is_bam)
13488c2ecf20Sopenharmony_ci			dma_unmap_sg(nandc->dev, desc->bam_sgl,
13498c2ecf20Sopenharmony_ci				     desc->sgl_cnt, desc->dir);
13508c2ecf20Sopenharmony_ci		else
13518c2ecf20Sopenharmony_ci			dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1,
13528c2ecf20Sopenharmony_ci				     desc->dir);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci		kfree(desc);
13558c2ecf20Sopenharmony_ci	}
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci/* reset the register read buffer for next NAND operation */
13598c2ecf20Sopenharmony_cistatic void clear_read_regs(struct qcom_nand_controller *nandc)
13608c2ecf20Sopenharmony_ci{
13618c2ecf20Sopenharmony_ci	nandc->reg_read_pos = 0;
13628c2ecf20Sopenharmony_ci	nandc_read_buffer_sync(nandc, false);
13638c2ecf20Sopenharmony_ci}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_cistatic void pre_command(struct qcom_nand_host *host, int command)
13668c2ecf20Sopenharmony_ci{
13678c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
13688c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	nandc->buf_count = 0;
13718c2ecf20Sopenharmony_ci	nandc->buf_start = 0;
13728c2ecf20Sopenharmony_ci	host->use_ecc = false;
13738c2ecf20Sopenharmony_ci	host->last_command = command;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	clear_read_regs(nandc);
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	if (command == NAND_CMD_RESET || command == NAND_CMD_READID ||
13788c2ecf20Sopenharmony_ci	    command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1)
13798c2ecf20Sopenharmony_ci		clear_bam_transaction(nandc);
13808c2ecf20Sopenharmony_ci}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci/*
13838c2ecf20Sopenharmony_ci * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our
13848c2ecf20Sopenharmony_ci * privately maintained status byte, this status byte can be read after
13858c2ecf20Sopenharmony_ci * NAND_CMD_STATUS is called
13868c2ecf20Sopenharmony_ci */
13878c2ecf20Sopenharmony_cistatic void parse_erase_write_errors(struct qcom_nand_host *host, int command)
13888c2ecf20Sopenharmony_ci{
13898c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
13908c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
13918c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
13928c2ecf20Sopenharmony_ci	int num_cw;
13938c2ecf20Sopenharmony_ci	int i;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
13968c2ecf20Sopenharmony_ci	nandc_read_buffer_sync(nandc, true);
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	for (i = 0; i < num_cw; i++) {
13998c2ecf20Sopenharmony_ci		u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci		if (flash_status & FS_MPU_ERR)
14028c2ecf20Sopenharmony_ci			host->status &= ~NAND_STATUS_WP;
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci		if (flash_status & FS_OP_ERR || (i == (num_cw - 1) &&
14058c2ecf20Sopenharmony_ci						 (flash_status &
14068c2ecf20Sopenharmony_ci						  FS_DEVICE_STS_ERR)))
14078c2ecf20Sopenharmony_ci			host->status |= NAND_STATUS_FAIL;
14088c2ecf20Sopenharmony_ci	}
14098c2ecf20Sopenharmony_ci}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_cistatic void post_command(struct qcom_nand_host *host, int command)
14128c2ecf20Sopenharmony_ci{
14138c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
14148c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	switch (command) {
14178c2ecf20Sopenharmony_ci	case NAND_CMD_READID:
14188c2ecf20Sopenharmony_ci		nandc_read_buffer_sync(nandc, true);
14198c2ecf20Sopenharmony_ci		memcpy(nandc->data_buffer, nandc->reg_read_buf,
14208c2ecf20Sopenharmony_ci		       nandc->buf_count);
14218c2ecf20Sopenharmony_ci		break;
14228c2ecf20Sopenharmony_ci	case NAND_CMD_PAGEPROG:
14238c2ecf20Sopenharmony_ci	case NAND_CMD_ERASE1:
14248c2ecf20Sopenharmony_ci		parse_erase_write_errors(host, command);
14258c2ecf20Sopenharmony_ci		break;
14268c2ecf20Sopenharmony_ci	default:
14278c2ecf20Sopenharmony_ci		break;
14288c2ecf20Sopenharmony_ci	}
14298c2ecf20Sopenharmony_ci}
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci/*
14328c2ecf20Sopenharmony_ci * Implements chip->legacy.cmdfunc. It's  only used for a limited set of
14338c2ecf20Sopenharmony_ci * commands. The rest of the commands wouldn't be called by upper layers.
14348c2ecf20Sopenharmony_ci * For example, NAND_CMD_READOOB would never be called because we have our own
14358c2ecf20Sopenharmony_ci * versions of read_oob ops for nand_ecc_ctrl.
14368c2ecf20Sopenharmony_ci */
14378c2ecf20Sopenharmony_cistatic void qcom_nandc_command(struct nand_chip *chip, unsigned int command,
14388c2ecf20Sopenharmony_ci			       int column, int page_addr)
14398c2ecf20Sopenharmony_ci{
14408c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
14418c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
14428c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
14438c2ecf20Sopenharmony_ci	bool wait = false;
14448c2ecf20Sopenharmony_ci	int ret = 0;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	pre_command(host, command);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	switch (command) {
14498c2ecf20Sopenharmony_ci	case NAND_CMD_RESET:
14508c2ecf20Sopenharmony_ci		ret = reset(host);
14518c2ecf20Sopenharmony_ci		wait = true;
14528c2ecf20Sopenharmony_ci		break;
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	case NAND_CMD_READID:
14558c2ecf20Sopenharmony_ci		nandc->buf_count = 4;
14568c2ecf20Sopenharmony_ci		ret = read_id(host, column);
14578c2ecf20Sopenharmony_ci		wait = true;
14588c2ecf20Sopenharmony_ci		break;
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	case NAND_CMD_PARAM:
14618c2ecf20Sopenharmony_ci		ret = nandc_param(host);
14628c2ecf20Sopenharmony_ci		wait = true;
14638c2ecf20Sopenharmony_ci		break;
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	case NAND_CMD_ERASE1:
14668c2ecf20Sopenharmony_ci		ret = erase_block(host, page_addr);
14678c2ecf20Sopenharmony_ci		wait = true;
14688c2ecf20Sopenharmony_ci		break;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	case NAND_CMD_READ0:
14718c2ecf20Sopenharmony_ci		/* we read the entire page for now */
14728c2ecf20Sopenharmony_ci		WARN_ON(column != 0);
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci		host->use_ecc = true;
14758c2ecf20Sopenharmony_ci		set_address(host, 0, page_addr);
14768c2ecf20Sopenharmony_ci		update_rw_regs(host, ecc->steps, true);
14778c2ecf20Sopenharmony_ci		break;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	case NAND_CMD_SEQIN:
14808c2ecf20Sopenharmony_ci		WARN_ON(column != 0);
14818c2ecf20Sopenharmony_ci		set_address(host, 0, page_addr);
14828c2ecf20Sopenharmony_ci		break;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	case NAND_CMD_PAGEPROG:
14858c2ecf20Sopenharmony_ci	case NAND_CMD_STATUS:
14868c2ecf20Sopenharmony_ci	case NAND_CMD_NONE:
14878c2ecf20Sopenharmony_ci	default:
14888c2ecf20Sopenharmony_ci		break;
14898c2ecf20Sopenharmony_ci	}
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	if (ret) {
14928c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure executing command %d\n",
14938c2ecf20Sopenharmony_ci			command);
14948c2ecf20Sopenharmony_ci		free_descs(nandc);
14958c2ecf20Sopenharmony_ci		return;
14968c2ecf20Sopenharmony_ci	}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	if (wait) {
14998c2ecf20Sopenharmony_ci		ret = submit_descs(nandc);
15008c2ecf20Sopenharmony_ci		if (ret)
15018c2ecf20Sopenharmony_ci			dev_err(nandc->dev,
15028c2ecf20Sopenharmony_ci				"failure submitting descs for command %d\n",
15038c2ecf20Sopenharmony_ci				command);
15048c2ecf20Sopenharmony_ci	}
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	free_descs(nandc);
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	post_command(host, command);
15098c2ecf20Sopenharmony_ci}
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci/*
15128c2ecf20Sopenharmony_ci * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read
15138c2ecf20Sopenharmony_ci * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS.
15148c2ecf20Sopenharmony_ci *
15158c2ecf20Sopenharmony_ci * when using RS ECC, the HW reports the same erros when reading an erased CW,
15168c2ecf20Sopenharmony_ci * but it notifies that it is an erased CW by placing special characters at
15178c2ecf20Sopenharmony_ci * certain offsets in the buffer.
15188c2ecf20Sopenharmony_ci *
15198c2ecf20Sopenharmony_ci * verify if the page is erased or not, and fix up the page for RS ECC by
15208c2ecf20Sopenharmony_ci * replacing the special characters with 0xff.
15218c2ecf20Sopenharmony_ci */
15228c2ecf20Sopenharmony_cistatic bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len)
15238c2ecf20Sopenharmony_ci{
15248c2ecf20Sopenharmony_ci	u8 empty1, empty2;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	/*
15278c2ecf20Sopenharmony_ci	 * an erased page flags an error in NAND_FLASH_STATUS, check if the page
15288c2ecf20Sopenharmony_ci	 * is erased by looking for 0x54s at offsets 3 and 175 from the
15298c2ecf20Sopenharmony_ci	 * beginning of each codeword
15308c2ecf20Sopenharmony_ci	 */
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	empty1 = data_buf[3];
15338c2ecf20Sopenharmony_ci	empty2 = data_buf[175];
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	/*
15368c2ecf20Sopenharmony_ci	 * if the erased codework markers, if they exist override them with
15378c2ecf20Sopenharmony_ci	 * 0xffs
15388c2ecf20Sopenharmony_ci	 */
15398c2ecf20Sopenharmony_ci	if ((empty1 == 0x54 && empty2 == 0xff) ||
15408c2ecf20Sopenharmony_ci	    (empty1 == 0xff && empty2 == 0x54)) {
15418c2ecf20Sopenharmony_ci		data_buf[3] = 0xff;
15428c2ecf20Sopenharmony_ci		data_buf[175] = 0xff;
15438c2ecf20Sopenharmony_ci	}
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	/*
15468c2ecf20Sopenharmony_ci	 * check if the entire chunk contains 0xffs or not. if it doesn't, then
15478c2ecf20Sopenharmony_ci	 * restore the original values at the special offsets
15488c2ecf20Sopenharmony_ci	 */
15498c2ecf20Sopenharmony_ci	if (memchr_inv(data_buf, 0xff, data_len)) {
15508c2ecf20Sopenharmony_ci		data_buf[3] = empty1;
15518c2ecf20Sopenharmony_ci		data_buf[175] = empty2;
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci		return false;
15548c2ecf20Sopenharmony_ci	}
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	return true;
15578c2ecf20Sopenharmony_ci}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_cistruct read_stats {
15608c2ecf20Sopenharmony_ci	__le32 flash;
15618c2ecf20Sopenharmony_ci	__le32 buffer;
15628c2ecf20Sopenharmony_ci	__le32 erased_cw;
15638c2ecf20Sopenharmony_ci};
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci/* reads back FLASH_STATUS register set by the controller */
15668c2ecf20Sopenharmony_cistatic int check_flash_errors(struct qcom_nand_host *host, int cw_cnt)
15678c2ecf20Sopenharmony_ci{
15688c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
15698c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
15708c2ecf20Sopenharmony_ci	int i;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	nandc_read_buffer_sync(nandc, true);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	for (i = 0; i < cw_cnt; i++) {
15758c2ecf20Sopenharmony_ci		u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci		if (flash & (FS_OP_ERR | FS_MPU_ERR))
15788c2ecf20Sopenharmony_ci			return -EIO;
15798c2ecf20Sopenharmony_ci	}
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	return 0;
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci/* performs raw read for one codeword */
15858c2ecf20Sopenharmony_cistatic int
15868c2ecf20Sopenharmony_ciqcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
15878c2ecf20Sopenharmony_ci		       u8 *data_buf, u8 *oob_buf, int page, int cw)
15888c2ecf20Sopenharmony_ci{
15898c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
15908c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
15918c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
15928c2ecf20Sopenharmony_ci	int data_size1, data_size2, oob_size1, oob_size2;
15938c2ecf20Sopenharmony_ci	int ret, reg_off = FLASH_BUF_ACC, read_loc = 0;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	nand_read_page_op(chip, page, 0, NULL, 0);
15968c2ecf20Sopenharmony_ci	host->use_ecc = false;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	clear_bam_transaction(nandc);
15998c2ecf20Sopenharmony_ci	set_address(host, host->cw_size * cw, page);
16008c2ecf20Sopenharmony_ci	update_rw_regs(host, 1, true);
16018c2ecf20Sopenharmony_ci	config_nand_page_read(nandc);
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
16048c2ecf20Sopenharmony_ci	oob_size1 = host->bbm_size;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	if (cw == (ecc->steps - 1)) {
16078c2ecf20Sopenharmony_ci		data_size2 = ecc->size - data_size1 -
16088c2ecf20Sopenharmony_ci			     ((ecc->steps - 1) * 4);
16098c2ecf20Sopenharmony_ci		oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw +
16108c2ecf20Sopenharmony_ci			    host->spare_bytes;
16118c2ecf20Sopenharmony_ci	} else {
16128c2ecf20Sopenharmony_ci		data_size2 = host->cw_data - data_size1;
16138c2ecf20Sopenharmony_ci		oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
16148c2ecf20Sopenharmony_ci	}
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	if (nandc->props->is_bam) {
16178c2ecf20Sopenharmony_ci		nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0);
16188c2ecf20Sopenharmony_ci		read_loc += data_size1;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci		nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0);
16218c2ecf20Sopenharmony_ci		read_loc += oob_size1;
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci		nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0);
16248c2ecf20Sopenharmony_ci		read_loc += data_size2;
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci		nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1);
16278c2ecf20Sopenharmony_ci	}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	config_nand_cw_read(nandc, false);
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
16328c2ecf20Sopenharmony_ci	reg_off += data_size1;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
16358c2ecf20Sopenharmony_ci	reg_off += oob_size1;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0);
16388c2ecf20Sopenharmony_ci	reg_off += data_size2;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0);
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	ret = submit_descs(nandc);
16438c2ecf20Sopenharmony_ci	free_descs(nandc);
16448c2ecf20Sopenharmony_ci	if (ret) {
16458c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure to read raw cw %d\n", cw);
16468c2ecf20Sopenharmony_ci		return ret;
16478c2ecf20Sopenharmony_ci	}
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	return check_flash_errors(host, 1);
16508c2ecf20Sopenharmony_ci}
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci/*
16538c2ecf20Sopenharmony_ci * Bitflips can happen in erased codewords also so this function counts the
16548c2ecf20Sopenharmony_ci * number of 0 in each CW for which ECC engine returns the uncorrectable
16558c2ecf20Sopenharmony_ci * error. The page will be assumed as erased if this count is less than or
16568c2ecf20Sopenharmony_ci * equal to the ecc->strength for each CW.
16578c2ecf20Sopenharmony_ci *
16588c2ecf20Sopenharmony_ci * 1. Both DATA and OOB need to be checked for number of 0. The
16598c2ecf20Sopenharmony_ci *    top-level API can be called with only data buf or OOB buf so use
16608c2ecf20Sopenharmony_ci *    chip->data_buf if data buf is null and chip->oob_poi if oob buf
16618c2ecf20Sopenharmony_ci *    is null for copying the raw bytes.
16628c2ecf20Sopenharmony_ci * 2. Perform raw read for all the CW which has uncorrectable errors.
16638c2ecf20Sopenharmony_ci * 3. For each CW, check the number of 0 in cw_data and usable OOB bytes.
16648c2ecf20Sopenharmony_ci *    The BBM and spare bytes bit flip won’t affect the ECC so don’t check
16658c2ecf20Sopenharmony_ci *    the number of bitflips in this area.
16668c2ecf20Sopenharmony_ci */
16678c2ecf20Sopenharmony_cistatic int
16688c2ecf20Sopenharmony_cicheck_for_erased_page(struct qcom_nand_host *host, u8 *data_buf,
16698c2ecf20Sopenharmony_ci		      u8 *oob_buf, unsigned long uncorrectable_cws,
16708c2ecf20Sopenharmony_ci		      int page, unsigned int max_bitflips)
16718c2ecf20Sopenharmony_ci{
16728c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
16738c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
16748c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
16758c2ecf20Sopenharmony_ci	u8 *cw_data_buf, *cw_oob_buf;
16768c2ecf20Sopenharmony_ci	int cw, data_size, oob_size, ret = 0;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	if (!data_buf)
16798c2ecf20Sopenharmony_ci		data_buf = nand_get_data_buf(chip);
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	if (!oob_buf) {
16828c2ecf20Sopenharmony_ci		nand_get_data_buf(chip);
16838c2ecf20Sopenharmony_ci		oob_buf = chip->oob_poi;
16848c2ecf20Sopenharmony_ci	}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
16878c2ecf20Sopenharmony_ci		if (cw == (ecc->steps - 1)) {
16888c2ecf20Sopenharmony_ci			data_size = ecc->size - ((ecc->steps - 1) * 4);
16898c2ecf20Sopenharmony_ci			oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
16908c2ecf20Sopenharmony_ci		} else {
16918c2ecf20Sopenharmony_ci			data_size = host->cw_data;
16928c2ecf20Sopenharmony_ci			oob_size = host->ecc_bytes_hw;
16938c2ecf20Sopenharmony_ci		}
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci		/* determine starting buffer address for current CW */
16968c2ecf20Sopenharmony_ci		cw_data_buf = data_buf + (cw * host->cw_data);
16978c2ecf20Sopenharmony_ci		cw_oob_buf = oob_buf + (cw * ecc->bytes);
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci		ret = qcom_nandc_read_cw_raw(mtd, chip, cw_data_buf,
17008c2ecf20Sopenharmony_ci					     cw_oob_buf, page, cw);
17018c2ecf20Sopenharmony_ci		if (ret)
17028c2ecf20Sopenharmony_ci			return ret;
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci		/*
17058c2ecf20Sopenharmony_ci		 * make sure it isn't an erased page reported
17068c2ecf20Sopenharmony_ci		 * as not-erased by HW because of a few bitflips
17078c2ecf20Sopenharmony_ci		 */
17088c2ecf20Sopenharmony_ci		ret = nand_check_erased_ecc_chunk(cw_data_buf, data_size,
17098c2ecf20Sopenharmony_ci						  cw_oob_buf + host->bbm_size,
17108c2ecf20Sopenharmony_ci						  oob_size, NULL,
17118c2ecf20Sopenharmony_ci						  0, ecc->strength);
17128c2ecf20Sopenharmony_ci		if (ret < 0) {
17138c2ecf20Sopenharmony_ci			mtd->ecc_stats.failed++;
17148c2ecf20Sopenharmony_ci		} else {
17158c2ecf20Sopenharmony_ci			mtd->ecc_stats.corrected += ret;
17168c2ecf20Sopenharmony_ci			max_bitflips = max_t(unsigned int, max_bitflips, ret);
17178c2ecf20Sopenharmony_ci		}
17188c2ecf20Sopenharmony_ci	}
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	return max_bitflips;
17218c2ecf20Sopenharmony_ci}
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci/*
17248c2ecf20Sopenharmony_ci * reads back status registers set by the controller to notify page read
17258c2ecf20Sopenharmony_ci * errors. this is equivalent to what 'ecc->correct()' would do.
17268c2ecf20Sopenharmony_ci */
17278c2ecf20Sopenharmony_cistatic int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
17288c2ecf20Sopenharmony_ci			     u8 *oob_buf, int page)
17298c2ecf20Sopenharmony_ci{
17308c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
17318c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
17328c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
17338c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
17348c2ecf20Sopenharmony_ci	unsigned int max_bitflips = 0, uncorrectable_cws = 0;
17358c2ecf20Sopenharmony_ci	struct read_stats *buf;
17368c2ecf20Sopenharmony_ci	bool flash_op_err = false, erased;
17378c2ecf20Sopenharmony_ci	int i;
17388c2ecf20Sopenharmony_ci	u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	buf = (struct read_stats *)nandc->reg_read_buf;
17418c2ecf20Sopenharmony_ci	nandc_read_buffer_sync(nandc, true);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	for (i = 0; i < ecc->steps; i++, buf++) {
17448c2ecf20Sopenharmony_ci		u32 flash, buffer, erased_cw;
17458c2ecf20Sopenharmony_ci		int data_len, oob_len;
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci		if (i == (ecc->steps - 1)) {
17488c2ecf20Sopenharmony_ci			data_len = ecc->size - ((ecc->steps - 1) << 2);
17498c2ecf20Sopenharmony_ci			oob_len = ecc->steps << 2;
17508c2ecf20Sopenharmony_ci		} else {
17518c2ecf20Sopenharmony_ci			data_len = host->cw_data;
17528c2ecf20Sopenharmony_ci			oob_len = 0;
17538c2ecf20Sopenharmony_ci		}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci		flash = le32_to_cpu(buf->flash);
17568c2ecf20Sopenharmony_ci		buffer = le32_to_cpu(buf->buffer);
17578c2ecf20Sopenharmony_ci		erased_cw = le32_to_cpu(buf->erased_cw);
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci		/*
17608c2ecf20Sopenharmony_ci		 * Check ECC failure for each codeword. ECC failure can
17618c2ecf20Sopenharmony_ci		 * happen in either of the following conditions
17628c2ecf20Sopenharmony_ci		 * 1. If number of bitflips are greater than ECC engine
17638c2ecf20Sopenharmony_ci		 *    capability.
17648c2ecf20Sopenharmony_ci		 * 2. If this codeword contains all 0xff for which erased
17658c2ecf20Sopenharmony_ci		 *    codeword detection check will be done.
17668c2ecf20Sopenharmony_ci		 */
17678c2ecf20Sopenharmony_ci		if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) {
17688c2ecf20Sopenharmony_ci			/*
17698c2ecf20Sopenharmony_ci			 * For BCH ECC, ignore erased codeword errors, if
17708c2ecf20Sopenharmony_ci			 * ERASED_CW bits are set.
17718c2ecf20Sopenharmony_ci			 */
17728c2ecf20Sopenharmony_ci			if (host->bch_enabled) {
17738c2ecf20Sopenharmony_ci				erased = (erased_cw & ERASED_CW) == ERASED_CW ?
17748c2ecf20Sopenharmony_ci					 true : false;
17758c2ecf20Sopenharmony_ci			/*
17768c2ecf20Sopenharmony_ci			 * For RS ECC, HW reports the erased CW by placing
17778c2ecf20Sopenharmony_ci			 * special characters at certain offsets in the buffer.
17788c2ecf20Sopenharmony_ci			 * These special characters will be valid only if
17798c2ecf20Sopenharmony_ci			 * complete page is read i.e. data_buf is not NULL.
17808c2ecf20Sopenharmony_ci			 */
17818c2ecf20Sopenharmony_ci			} else if (data_buf) {
17828c2ecf20Sopenharmony_ci				erased = erased_chunk_check_and_fixup(data_buf,
17838c2ecf20Sopenharmony_ci								      data_len);
17848c2ecf20Sopenharmony_ci			} else {
17858c2ecf20Sopenharmony_ci				erased = false;
17868c2ecf20Sopenharmony_ci			}
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci			if (!erased)
17898c2ecf20Sopenharmony_ci				uncorrectable_cws |= BIT(i);
17908c2ecf20Sopenharmony_ci		/*
17918c2ecf20Sopenharmony_ci		 * Check if MPU or any other operational error (timeout,
17928c2ecf20Sopenharmony_ci		 * device failure, etc.) happened for this codeword and
17938c2ecf20Sopenharmony_ci		 * make flash_op_err true. If flash_op_err is set, then
17948c2ecf20Sopenharmony_ci		 * EIO will be returned for page read.
17958c2ecf20Sopenharmony_ci		 */
17968c2ecf20Sopenharmony_ci		} else if (flash & (FS_OP_ERR | FS_MPU_ERR)) {
17978c2ecf20Sopenharmony_ci			flash_op_err = true;
17988c2ecf20Sopenharmony_ci		/*
17998c2ecf20Sopenharmony_ci		 * No ECC or operational errors happened. Check the number of
18008c2ecf20Sopenharmony_ci		 * bits corrected and update the ecc_stats.corrected.
18018c2ecf20Sopenharmony_ci		 */
18028c2ecf20Sopenharmony_ci		} else {
18038c2ecf20Sopenharmony_ci			unsigned int stat;
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci			stat = buffer & BS_CORRECTABLE_ERR_MSK;
18068c2ecf20Sopenharmony_ci			mtd->ecc_stats.corrected += stat;
18078c2ecf20Sopenharmony_ci			max_bitflips = max(max_bitflips, stat);
18088c2ecf20Sopenharmony_ci		}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci		if (data_buf)
18118c2ecf20Sopenharmony_ci			data_buf += data_len;
18128c2ecf20Sopenharmony_ci		if (oob_buf)
18138c2ecf20Sopenharmony_ci			oob_buf += oob_len + ecc->bytes;
18148c2ecf20Sopenharmony_ci	}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	if (flash_op_err)
18178c2ecf20Sopenharmony_ci		return -EIO;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	if (!uncorrectable_cws)
18208c2ecf20Sopenharmony_ci		return max_bitflips;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	return check_for_erased_page(host, data_buf_start, oob_buf_start,
18238c2ecf20Sopenharmony_ci				     uncorrectable_cws, page,
18248c2ecf20Sopenharmony_ci				     max_bitflips);
18258c2ecf20Sopenharmony_ci}
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci/*
18288c2ecf20Sopenharmony_ci * helper to perform the actual page read operation, used by ecc->read_page(),
18298c2ecf20Sopenharmony_ci * ecc->read_oob()
18308c2ecf20Sopenharmony_ci */
18318c2ecf20Sopenharmony_cistatic int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
18328c2ecf20Sopenharmony_ci			 u8 *oob_buf, int page)
18338c2ecf20Sopenharmony_ci{
18348c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
18358c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
18368c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
18378c2ecf20Sopenharmony_ci	u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
18388c2ecf20Sopenharmony_ci	int i, ret;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	config_nand_page_read(nandc);
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	/* queue cmd descs for each codeword */
18438c2ecf20Sopenharmony_ci	for (i = 0; i < ecc->steps; i++) {
18448c2ecf20Sopenharmony_ci		int data_size, oob_size;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci		if (i == (ecc->steps - 1)) {
18478c2ecf20Sopenharmony_ci			data_size = ecc->size - ((ecc->steps - 1) << 2);
18488c2ecf20Sopenharmony_ci			oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
18498c2ecf20Sopenharmony_ci				   host->spare_bytes;
18508c2ecf20Sopenharmony_ci		} else {
18518c2ecf20Sopenharmony_ci			data_size = host->cw_data;
18528c2ecf20Sopenharmony_ci			oob_size = host->ecc_bytes_hw + host->spare_bytes;
18538c2ecf20Sopenharmony_ci		}
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci		if (nandc->props->is_bam) {
18568c2ecf20Sopenharmony_ci			if (data_buf && oob_buf) {
18578c2ecf20Sopenharmony_ci				nandc_set_read_loc(nandc, 0, 0, data_size, 0);
18588c2ecf20Sopenharmony_ci				nandc_set_read_loc(nandc, 1, data_size,
18598c2ecf20Sopenharmony_ci						   oob_size, 1);
18608c2ecf20Sopenharmony_ci			} else if (data_buf) {
18618c2ecf20Sopenharmony_ci				nandc_set_read_loc(nandc, 0, 0, data_size, 1);
18628c2ecf20Sopenharmony_ci			} else {
18638c2ecf20Sopenharmony_ci				nandc_set_read_loc(nandc, 0, data_size,
18648c2ecf20Sopenharmony_ci						   oob_size, 1);
18658c2ecf20Sopenharmony_ci			}
18668c2ecf20Sopenharmony_ci		}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci		config_nand_cw_read(nandc, true);
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci		if (data_buf)
18718c2ecf20Sopenharmony_ci			read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
18728c2ecf20Sopenharmony_ci				      data_size, 0);
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci		/*
18758c2ecf20Sopenharmony_ci		 * when ecc is enabled, the controller doesn't read the real
18768c2ecf20Sopenharmony_ci		 * or dummy bad block markers in each chunk. To maintain a
18778c2ecf20Sopenharmony_ci		 * consistent layout across RAW and ECC reads, we just
18788c2ecf20Sopenharmony_ci		 * leave the real/dummy BBM offsets empty (i.e, filled with
18798c2ecf20Sopenharmony_ci		 * 0xffs)
18808c2ecf20Sopenharmony_ci		 */
18818c2ecf20Sopenharmony_ci		if (oob_buf) {
18828c2ecf20Sopenharmony_ci			int j;
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci			for (j = 0; j < host->bbm_size; j++)
18858c2ecf20Sopenharmony_ci				*oob_buf++ = 0xff;
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci			read_data_dma(nandc, FLASH_BUF_ACC + data_size,
18888c2ecf20Sopenharmony_ci				      oob_buf, oob_size, 0);
18898c2ecf20Sopenharmony_ci		}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci		if (data_buf)
18928c2ecf20Sopenharmony_ci			data_buf += data_size;
18938c2ecf20Sopenharmony_ci		if (oob_buf)
18948c2ecf20Sopenharmony_ci			oob_buf += oob_size;
18958c2ecf20Sopenharmony_ci	}
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci	ret = submit_descs(nandc);
18988c2ecf20Sopenharmony_ci	free_descs(nandc);
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	if (ret) {
19018c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure to read page/oob\n");
19028c2ecf20Sopenharmony_ci		return ret;
19038c2ecf20Sopenharmony_ci	}
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	return parse_read_errors(host, data_buf_start, oob_buf_start, page);
19068c2ecf20Sopenharmony_ci}
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci/*
19098c2ecf20Sopenharmony_ci * a helper that copies the last step/codeword of a page (containing free oob)
19108c2ecf20Sopenharmony_ci * into our local buffer
19118c2ecf20Sopenharmony_ci */
19128c2ecf20Sopenharmony_cistatic int copy_last_cw(struct qcom_nand_host *host, int page)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
19158c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
19168c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
19178c2ecf20Sopenharmony_ci	int size;
19188c2ecf20Sopenharmony_ci	int ret;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	clear_read_regs(nandc);
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	size = host->use_ecc ? host->cw_data : host->cw_size;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	/* prepare a clean read buffer */
19258c2ecf20Sopenharmony_ci	memset(nandc->data_buffer, 0xff, size);
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	set_address(host, host->cw_size * (ecc->steps - 1), page);
19288c2ecf20Sopenharmony_ci	update_rw_regs(host, 1, true);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	config_nand_single_cw_page_read(nandc, host->use_ecc);
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci	read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	ret = submit_descs(nandc);
19358c2ecf20Sopenharmony_ci	if (ret)
19368c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failed to copy last codeword\n");
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	free_descs(nandc);
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	return ret;
19418c2ecf20Sopenharmony_ci}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci/* implements ecc->read_page() */
19448c2ecf20Sopenharmony_cistatic int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
19458c2ecf20Sopenharmony_ci				int oob_required, int page)
19468c2ecf20Sopenharmony_ci{
19478c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
19488c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
19498c2ecf20Sopenharmony_ci	u8 *data_buf, *oob_buf = NULL;
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	nand_read_page_op(chip, page, 0, NULL, 0);
19528c2ecf20Sopenharmony_ci	data_buf = buf;
19538c2ecf20Sopenharmony_ci	oob_buf = oob_required ? chip->oob_poi : NULL;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	clear_bam_transaction(nandc);
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	return read_page_ecc(host, data_buf, oob_buf, page);
19588c2ecf20Sopenharmony_ci}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci/* implements ecc->read_page_raw() */
19618c2ecf20Sopenharmony_cistatic int qcom_nandc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
19628c2ecf20Sopenharmony_ci				    int oob_required, int page)
19638c2ecf20Sopenharmony_ci{
19648c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
19658c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
19668c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
19678c2ecf20Sopenharmony_ci	int cw, ret;
19688c2ecf20Sopenharmony_ci	u8 *data_buf = buf, *oob_buf = chip->oob_poi;
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	for (cw = 0; cw < ecc->steps; cw++) {
19718c2ecf20Sopenharmony_ci		ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf,
19728c2ecf20Sopenharmony_ci					     page, cw);
19738c2ecf20Sopenharmony_ci		if (ret)
19748c2ecf20Sopenharmony_ci			return ret;
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci		data_buf += host->cw_data;
19778c2ecf20Sopenharmony_ci		oob_buf += ecc->bytes;
19788c2ecf20Sopenharmony_ci	}
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	return 0;
19818c2ecf20Sopenharmony_ci}
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci/* implements ecc->read_oob() */
19848c2ecf20Sopenharmony_cistatic int qcom_nandc_read_oob(struct nand_chip *chip, int page)
19858c2ecf20Sopenharmony_ci{
19868c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
19878c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
19888c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	clear_read_regs(nandc);
19918c2ecf20Sopenharmony_ci	clear_bam_transaction(nandc);
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	host->use_ecc = true;
19948c2ecf20Sopenharmony_ci	set_address(host, 0, page);
19958c2ecf20Sopenharmony_ci	update_rw_regs(host, ecc->steps, true);
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci	return read_page_ecc(host, NULL, chip->oob_poi, page);
19988c2ecf20Sopenharmony_ci}
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci/* implements ecc->write_page() */
20018c2ecf20Sopenharmony_cistatic int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
20028c2ecf20Sopenharmony_ci				 int oob_required, int page)
20038c2ecf20Sopenharmony_ci{
20048c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
20058c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
20068c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
20078c2ecf20Sopenharmony_ci	u8 *data_buf, *oob_buf;
20088c2ecf20Sopenharmony_ci	int i, ret;
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci	clear_read_regs(nandc);
20138c2ecf20Sopenharmony_ci	clear_bam_transaction(nandc);
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	data_buf = (u8 *)buf;
20168c2ecf20Sopenharmony_ci	oob_buf = chip->oob_poi;
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	host->use_ecc = true;
20198c2ecf20Sopenharmony_ci	update_rw_regs(host, ecc->steps, false);
20208c2ecf20Sopenharmony_ci	config_nand_page_write(nandc);
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	for (i = 0; i < ecc->steps; i++) {
20238c2ecf20Sopenharmony_ci		int data_size, oob_size;
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_ci		if (i == (ecc->steps - 1)) {
20268c2ecf20Sopenharmony_ci			data_size = ecc->size - ((ecc->steps - 1) << 2);
20278c2ecf20Sopenharmony_ci			oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
20288c2ecf20Sopenharmony_ci				   host->spare_bytes;
20298c2ecf20Sopenharmony_ci		} else {
20308c2ecf20Sopenharmony_ci			data_size = host->cw_data;
20318c2ecf20Sopenharmony_ci			oob_size = ecc->bytes;
20328c2ecf20Sopenharmony_ci		}
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci		write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
20368c2ecf20Sopenharmony_ci			       i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci		/*
20398c2ecf20Sopenharmony_ci		 * when ECC is enabled, we don't really need to write anything
20408c2ecf20Sopenharmony_ci		 * to oob for the first n - 1 codewords since these oob regions
20418c2ecf20Sopenharmony_ci		 * just contain ECC bytes that's written by the controller
20428c2ecf20Sopenharmony_ci		 * itself. For the last codeword, we skip the bbm positions and
20438c2ecf20Sopenharmony_ci		 * write to the free oob area.
20448c2ecf20Sopenharmony_ci		 */
20458c2ecf20Sopenharmony_ci		if (i == (ecc->steps - 1)) {
20468c2ecf20Sopenharmony_ci			oob_buf += host->bbm_size;
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci			write_data_dma(nandc, FLASH_BUF_ACC + data_size,
20498c2ecf20Sopenharmony_ci				       oob_buf, oob_size, 0);
20508c2ecf20Sopenharmony_ci		}
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci		config_nand_cw_write(nandc);
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci		data_buf += data_size;
20558c2ecf20Sopenharmony_ci		oob_buf += oob_size;
20568c2ecf20Sopenharmony_ci	}
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci	ret = submit_descs(nandc);
20598c2ecf20Sopenharmony_ci	if (ret)
20608c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure to write page\n");
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	free_descs(nandc);
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	if (!ret)
20658c2ecf20Sopenharmony_ci		ret = nand_prog_page_end_op(chip);
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	return ret;
20688c2ecf20Sopenharmony_ci}
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci/* implements ecc->write_page_raw() */
20718c2ecf20Sopenharmony_cistatic int qcom_nandc_write_page_raw(struct nand_chip *chip,
20728c2ecf20Sopenharmony_ci				     const uint8_t *buf, int oob_required,
20738c2ecf20Sopenharmony_ci				     int page)
20748c2ecf20Sopenharmony_ci{
20758c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
20768c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
20778c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
20788c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
20798c2ecf20Sopenharmony_ci	u8 *data_buf, *oob_buf;
20808c2ecf20Sopenharmony_ci	int i, ret;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
20838c2ecf20Sopenharmony_ci	clear_read_regs(nandc);
20848c2ecf20Sopenharmony_ci	clear_bam_transaction(nandc);
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	data_buf = (u8 *)buf;
20878c2ecf20Sopenharmony_ci	oob_buf = chip->oob_poi;
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci	host->use_ecc = false;
20908c2ecf20Sopenharmony_ci	update_rw_regs(host, ecc->steps, false);
20918c2ecf20Sopenharmony_ci	config_nand_page_write(nandc);
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci	for (i = 0; i < ecc->steps; i++) {
20948c2ecf20Sopenharmony_ci		int data_size1, data_size2, oob_size1, oob_size2;
20958c2ecf20Sopenharmony_ci		int reg_off = FLASH_BUF_ACC;
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci		data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
20988c2ecf20Sopenharmony_ci		oob_size1 = host->bbm_size;
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_ci		if (i == (ecc->steps - 1)) {
21018c2ecf20Sopenharmony_ci			data_size2 = ecc->size - data_size1 -
21028c2ecf20Sopenharmony_ci				     ((ecc->steps - 1) << 2);
21038c2ecf20Sopenharmony_ci			oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
21048c2ecf20Sopenharmony_ci				    host->spare_bytes;
21058c2ecf20Sopenharmony_ci		} else {
21068c2ecf20Sopenharmony_ci			data_size2 = host->cw_data - data_size1;
21078c2ecf20Sopenharmony_ci			oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
21088c2ecf20Sopenharmony_ci		}
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci		write_data_dma(nandc, reg_off, data_buf, data_size1,
21118c2ecf20Sopenharmony_ci			       NAND_BAM_NO_EOT);
21128c2ecf20Sopenharmony_ci		reg_off += data_size1;
21138c2ecf20Sopenharmony_ci		data_buf += data_size1;
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci		write_data_dma(nandc, reg_off, oob_buf, oob_size1,
21168c2ecf20Sopenharmony_ci			       NAND_BAM_NO_EOT);
21178c2ecf20Sopenharmony_ci		reg_off += oob_size1;
21188c2ecf20Sopenharmony_ci		oob_buf += oob_size1;
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ci		write_data_dma(nandc, reg_off, data_buf, data_size2,
21218c2ecf20Sopenharmony_ci			       NAND_BAM_NO_EOT);
21228c2ecf20Sopenharmony_ci		reg_off += data_size2;
21238c2ecf20Sopenharmony_ci		data_buf += data_size2;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci		write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
21268c2ecf20Sopenharmony_ci		oob_buf += oob_size2;
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci		config_nand_cw_write(nandc);
21298c2ecf20Sopenharmony_ci	}
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	ret = submit_descs(nandc);
21328c2ecf20Sopenharmony_ci	if (ret)
21338c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure to write raw page\n");
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	free_descs(nandc);
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	if (!ret)
21388c2ecf20Sopenharmony_ci		ret = nand_prog_page_end_op(chip);
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci	return ret;
21418c2ecf20Sopenharmony_ci}
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci/*
21448c2ecf20Sopenharmony_ci * implements ecc->write_oob()
21458c2ecf20Sopenharmony_ci *
21468c2ecf20Sopenharmony_ci * the NAND controller cannot write only data or only OOB within a codeword
21478c2ecf20Sopenharmony_ci * since ECC is calculated for the combined codeword. So update the OOB from
21488c2ecf20Sopenharmony_ci * chip->oob_poi, and pad the data area with OxFF before writing.
21498c2ecf20Sopenharmony_ci */
21508c2ecf20Sopenharmony_cistatic int qcom_nandc_write_oob(struct nand_chip *chip, int page)
21518c2ecf20Sopenharmony_ci{
21528c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
21538c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
21548c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
21558c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
21568c2ecf20Sopenharmony_ci	u8 *oob = chip->oob_poi;
21578c2ecf20Sopenharmony_ci	int data_size, oob_size;
21588c2ecf20Sopenharmony_ci	int ret;
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	host->use_ecc = true;
21618c2ecf20Sopenharmony_ci	clear_bam_transaction(nandc);
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	/* calculate the data and oob size for the last codeword/step */
21648c2ecf20Sopenharmony_ci	data_size = ecc->size - ((ecc->steps - 1) << 2);
21658c2ecf20Sopenharmony_ci	oob_size = mtd->oobavail;
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	memset(nandc->data_buffer, 0xff, host->cw_data);
21688c2ecf20Sopenharmony_ci	/* override new oob content to last codeword */
21698c2ecf20Sopenharmony_ci	mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob,
21708c2ecf20Sopenharmony_ci				    0, mtd->oobavail);
21718c2ecf20Sopenharmony_ci
21728c2ecf20Sopenharmony_ci	set_address(host, host->cw_size * (ecc->steps - 1), page);
21738c2ecf20Sopenharmony_ci	update_rw_regs(host, 1, false);
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	config_nand_page_write(nandc);
21768c2ecf20Sopenharmony_ci	write_data_dma(nandc, FLASH_BUF_ACC,
21778c2ecf20Sopenharmony_ci		       nandc->data_buffer, data_size + oob_size, 0);
21788c2ecf20Sopenharmony_ci	config_nand_cw_write(nandc);
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_ci	ret = submit_descs(nandc);
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	free_descs(nandc);
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	if (ret) {
21858c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure to write oob\n");
21868c2ecf20Sopenharmony_ci		return -EIO;
21878c2ecf20Sopenharmony_ci	}
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	return nand_prog_page_end_op(chip);
21908c2ecf20Sopenharmony_ci}
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_cistatic int qcom_nandc_block_bad(struct nand_chip *chip, loff_t ofs)
21938c2ecf20Sopenharmony_ci{
21948c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
21958c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
21968c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
21978c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
21988c2ecf20Sopenharmony_ci	int page, ret, bbpos, bad = 0;
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	page = (int)(ofs >> chip->page_shift) & chip->pagemask;
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	/*
22038c2ecf20Sopenharmony_ci	 * configure registers for a raw sub page read, the address is set to
22048c2ecf20Sopenharmony_ci	 * the beginning of the last codeword, we don't care about reading ecc
22058c2ecf20Sopenharmony_ci	 * portion of oob. we just want the first few bytes from this codeword
22068c2ecf20Sopenharmony_ci	 * that contains the BBM
22078c2ecf20Sopenharmony_ci	 */
22088c2ecf20Sopenharmony_ci	host->use_ecc = false;
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	clear_bam_transaction(nandc);
22118c2ecf20Sopenharmony_ci	ret = copy_last_cw(host, page);
22128c2ecf20Sopenharmony_ci	if (ret)
22138c2ecf20Sopenharmony_ci		goto err;
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	if (check_flash_errors(host, 1)) {
22168c2ecf20Sopenharmony_ci		dev_warn(nandc->dev, "error when trying to read BBM\n");
22178c2ecf20Sopenharmony_ci		goto err;
22188c2ecf20Sopenharmony_ci	}
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1);
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci	bad = nandc->data_buffer[bbpos] != 0xff;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	if (chip->options & NAND_BUSWIDTH_16)
22258c2ecf20Sopenharmony_ci		bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff);
22268c2ecf20Sopenharmony_cierr:
22278c2ecf20Sopenharmony_ci	return bad;
22288c2ecf20Sopenharmony_ci}
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_cistatic int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs)
22318c2ecf20Sopenharmony_ci{
22328c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
22338c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
22348c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
22358c2ecf20Sopenharmony_ci	int page, ret;
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	clear_read_regs(nandc);
22388c2ecf20Sopenharmony_ci	clear_bam_transaction(nandc);
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	/*
22418c2ecf20Sopenharmony_ci	 * to mark the BBM as bad, we flash the entire last codeword with 0s.
22428c2ecf20Sopenharmony_ci	 * we don't care about the rest of the content in the codeword since
22438c2ecf20Sopenharmony_ci	 * we aren't going to use this block again
22448c2ecf20Sopenharmony_ci	 */
22458c2ecf20Sopenharmony_ci	memset(nandc->data_buffer, 0x00, host->cw_size);
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci	page = (int)(ofs >> chip->page_shift) & chip->pagemask;
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	/* prepare write */
22508c2ecf20Sopenharmony_ci	host->use_ecc = false;
22518c2ecf20Sopenharmony_ci	set_address(host, host->cw_size * (ecc->steps - 1), page);
22528c2ecf20Sopenharmony_ci	update_rw_regs(host, 1, false);
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	config_nand_page_write(nandc);
22558c2ecf20Sopenharmony_ci	write_data_dma(nandc, FLASH_BUF_ACC,
22568c2ecf20Sopenharmony_ci		       nandc->data_buffer, host->cw_size, 0);
22578c2ecf20Sopenharmony_ci	config_nand_cw_write(nandc);
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci	ret = submit_descs(nandc);
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	free_descs(nandc);
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	if (ret) {
22648c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failure to update BBM\n");
22658c2ecf20Sopenharmony_ci		return -EIO;
22668c2ecf20Sopenharmony_ci	}
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	return nand_prog_page_end_op(chip);
22698c2ecf20Sopenharmony_ci}
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci/*
22728c2ecf20Sopenharmony_ci * the three functions below implement chip->legacy.read_byte(),
22738c2ecf20Sopenharmony_ci * chip->legacy.read_buf() and chip->legacy.write_buf() respectively. these
22748c2ecf20Sopenharmony_ci * aren't used for reading/writing page data, they are used for smaller data
22758c2ecf20Sopenharmony_ci * like reading	id, status etc
22768c2ecf20Sopenharmony_ci */
22778c2ecf20Sopenharmony_cistatic uint8_t qcom_nandc_read_byte(struct nand_chip *chip)
22788c2ecf20Sopenharmony_ci{
22798c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
22808c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
22818c2ecf20Sopenharmony_ci	u8 *buf = nandc->data_buffer;
22828c2ecf20Sopenharmony_ci	u8 ret = 0x0;
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_ci	if (host->last_command == NAND_CMD_STATUS) {
22858c2ecf20Sopenharmony_ci		ret = host->status;
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci		host->status = NAND_STATUS_READY | NAND_STATUS_WP;
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci		return ret;
22908c2ecf20Sopenharmony_ci	}
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	if (nandc->buf_start < nandc->buf_count)
22938c2ecf20Sopenharmony_ci		ret = buf[nandc->buf_start++];
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	return ret;
22968c2ecf20Sopenharmony_ci}
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_cistatic void qcom_nandc_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
22998c2ecf20Sopenharmony_ci{
23008c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
23018c2ecf20Sopenharmony_ci	int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start);
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	memcpy(buf, nandc->data_buffer + nandc->buf_start, real_len);
23048c2ecf20Sopenharmony_ci	nandc->buf_start += real_len;
23058c2ecf20Sopenharmony_ci}
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_cistatic void qcom_nandc_write_buf(struct nand_chip *chip, const uint8_t *buf,
23088c2ecf20Sopenharmony_ci				 int len)
23098c2ecf20Sopenharmony_ci{
23108c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
23118c2ecf20Sopenharmony_ci	int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start);
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	memcpy(nandc->data_buffer + nandc->buf_start, buf, real_len);
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci	nandc->buf_start += real_len;
23168c2ecf20Sopenharmony_ci}
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci/* we support only one external chip for now */
23198c2ecf20Sopenharmony_cistatic void qcom_nandc_select_chip(struct nand_chip *chip, int chipnr)
23208c2ecf20Sopenharmony_ci{
23218c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	if (chipnr <= 0)
23248c2ecf20Sopenharmony_ci		return;
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci	dev_warn(nandc->dev, "invalid chip select\n");
23278c2ecf20Sopenharmony_ci}
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci/*
23308c2ecf20Sopenharmony_ci * NAND controller page layout info
23318c2ecf20Sopenharmony_ci *
23328c2ecf20Sopenharmony_ci * Layout with ECC enabled:
23338c2ecf20Sopenharmony_ci *
23348c2ecf20Sopenharmony_ci * |----------------------|  |---------------------------------|
23358c2ecf20Sopenharmony_ci * |           xx.......yy|  |             *********xx.......yy|
23368c2ecf20Sopenharmony_ci * |    DATA   xx..ECC..yy|  |    DATA     **SPARE**xx..ECC..yy|
23378c2ecf20Sopenharmony_ci * |   (516)   xx.......yy|  |  (516-n*4)  **(n*4)**xx.......yy|
23388c2ecf20Sopenharmony_ci * |           xx.......yy|  |             *********xx.......yy|
23398c2ecf20Sopenharmony_ci * |----------------------|  |---------------------------------|
23408c2ecf20Sopenharmony_ci *     codeword 1,2..n-1                  codeword n
23418c2ecf20Sopenharmony_ci *  <---(528/532 Bytes)-->    <-------(528/532 Bytes)--------->
23428c2ecf20Sopenharmony_ci *
23438c2ecf20Sopenharmony_ci * n = Number of codewords in the page
23448c2ecf20Sopenharmony_ci * . = ECC bytes
23458c2ecf20Sopenharmony_ci * * = Spare/free bytes
23468c2ecf20Sopenharmony_ci * x = Unused byte(s)
23478c2ecf20Sopenharmony_ci * y = Reserved byte(s)
23488c2ecf20Sopenharmony_ci *
23498c2ecf20Sopenharmony_ci * 2K page: n = 4, spare = 16 bytes
23508c2ecf20Sopenharmony_ci * 4K page: n = 8, spare = 32 bytes
23518c2ecf20Sopenharmony_ci * 8K page: n = 16, spare = 64 bytes
23528c2ecf20Sopenharmony_ci *
23538c2ecf20Sopenharmony_ci * the qcom nand controller operates at a sub page/codeword level. each
23548c2ecf20Sopenharmony_ci * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively.
23558c2ecf20Sopenharmony_ci * the number of ECC bytes vary based on the ECC strength and the bus width.
23568c2ecf20Sopenharmony_ci *
23578c2ecf20Sopenharmony_ci * the first n - 1 codewords contains 516 bytes of user data, the remaining
23588c2ecf20Sopenharmony_ci * 12/16 bytes consist of ECC and reserved data. The nth codeword contains
23598c2ecf20Sopenharmony_ci * both user data and spare(oobavail) bytes that sum up to 516 bytes.
23608c2ecf20Sopenharmony_ci *
23618c2ecf20Sopenharmony_ci * When we access a page with ECC enabled, the reserved bytes(s) are not
23628c2ecf20Sopenharmony_ci * accessible at all. When reading, we fill up these unreadable positions
23638c2ecf20Sopenharmony_ci * with 0xffs. When writing, the controller skips writing the inaccessible
23648c2ecf20Sopenharmony_ci * bytes.
23658c2ecf20Sopenharmony_ci *
23668c2ecf20Sopenharmony_ci * Layout with ECC disabled:
23678c2ecf20Sopenharmony_ci *
23688c2ecf20Sopenharmony_ci * |------------------------------|  |---------------------------------------|
23698c2ecf20Sopenharmony_ci * |         yy          xx.......|  |         bb          *********xx.......|
23708c2ecf20Sopenharmony_ci * |  DATA1  yy  DATA2   xx..ECC..|  |  DATA1  bb  DATA2   **SPARE**xx..ECC..|
23718c2ecf20Sopenharmony_ci * | (size1) yy (size2)  xx.......|  | (size1) bb (size2)  **(n*4)**xx.......|
23728c2ecf20Sopenharmony_ci * |         yy          xx.......|  |         bb          *********xx.......|
23738c2ecf20Sopenharmony_ci * |------------------------------|  |---------------------------------------|
23748c2ecf20Sopenharmony_ci *         codeword 1,2..n-1                        codeword n
23758c2ecf20Sopenharmony_ci *  <-------(528/532 Bytes)------>    <-----------(528/532 Bytes)----------->
23768c2ecf20Sopenharmony_ci *
23778c2ecf20Sopenharmony_ci * n = Number of codewords in the page
23788c2ecf20Sopenharmony_ci * . = ECC bytes
23798c2ecf20Sopenharmony_ci * * = Spare/free bytes
23808c2ecf20Sopenharmony_ci * x = Unused byte(s)
23818c2ecf20Sopenharmony_ci * y = Dummy Bad Bock byte(s)
23828c2ecf20Sopenharmony_ci * b = Real Bad Block byte(s)
23838c2ecf20Sopenharmony_ci * size1/size2 = function of codeword size and 'n'
23848c2ecf20Sopenharmony_ci *
23858c2ecf20Sopenharmony_ci * when the ECC block is disabled, one reserved byte (or two for 16 bit bus
23868c2ecf20Sopenharmony_ci * width) is now accessible. For the first n - 1 codewords, these are dummy Bad
23878c2ecf20Sopenharmony_ci * Block Markers. In the last codeword, this position contains the real BBM
23888c2ecf20Sopenharmony_ci *
23898c2ecf20Sopenharmony_ci * In order to have a consistent layout between RAW and ECC modes, we assume
23908c2ecf20Sopenharmony_ci * the following OOB layout arrangement:
23918c2ecf20Sopenharmony_ci *
23928c2ecf20Sopenharmony_ci * |-----------|  |--------------------|
23938c2ecf20Sopenharmony_ci * |yyxx.......|  |bb*********xx.......|
23948c2ecf20Sopenharmony_ci * |yyxx..ECC..|  |bb*FREEOOB*xx..ECC..|
23958c2ecf20Sopenharmony_ci * |yyxx.......|  |bb*********xx.......|
23968c2ecf20Sopenharmony_ci * |yyxx.......|  |bb*********xx.......|
23978c2ecf20Sopenharmony_ci * |-----------|  |--------------------|
23988c2ecf20Sopenharmony_ci *  first n - 1       nth OOB region
23998c2ecf20Sopenharmony_ci *  OOB regions
24008c2ecf20Sopenharmony_ci *
24018c2ecf20Sopenharmony_ci * n = Number of codewords in the page
24028c2ecf20Sopenharmony_ci * . = ECC bytes
24038c2ecf20Sopenharmony_ci * * = FREE OOB bytes
24048c2ecf20Sopenharmony_ci * y = Dummy bad block byte(s) (inaccessible when ECC enabled)
24058c2ecf20Sopenharmony_ci * x = Unused byte(s)
24068c2ecf20Sopenharmony_ci * b = Real bad block byte(s) (inaccessible when ECC enabled)
24078c2ecf20Sopenharmony_ci *
24088c2ecf20Sopenharmony_ci * This layout is read as is when ECC is disabled. When ECC is enabled, the
24098c2ecf20Sopenharmony_ci * inaccessible Bad Block byte(s) are ignored when we write to a page/oob,
24108c2ecf20Sopenharmony_ci * and assumed as 0xffs when we read a page/oob. The ECC, unused and
24118c2ecf20Sopenharmony_ci * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is
24128c2ecf20Sopenharmony_ci * the sum of the three).
24138c2ecf20Sopenharmony_ci */
24148c2ecf20Sopenharmony_cistatic int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
24158c2ecf20Sopenharmony_ci				   struct mtd_oob_region *oobregion)
24168c2ecf20Sopenharmony_ci{
24178c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
24188c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
24198c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	if (section > 1)
24228c2ecf20Sopenharmony_ci		return -ERANGE;
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	if (!section) {
24258c2ecf20Sopenharmony_ci		oobregion->length = (ecc->bytes * (ecc->steps - 1)) +
24268c2ecf20Sopenharmony_ci				    host->bbm_size;
24278c2ecf20Sopenharmony_ci		oobregion->offset = 0;
24288c2ecf20Sopenharmony_ci	} else {
24298c2ecf20Sopenharmony_ci		oobregion->length = host->ecc_bytes_hw + host->spare_bytes;
24308c2ecf20Sopenharmony_ci		oobregion->offset = mtd->oobsize - oobregion->length;
24318c2ecf20Sopenharmony_ci	}
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci	return 0;
24348c2ecf20Sopenharmony_ci}
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_cistatic int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section,
24378c2ecf20Sopenharmony_ci				     struct mtd_oob_region *oobregion)
24388c2ecf20Sopenharmony_ci{
24398c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
24408c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
24418c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	if (section)
24448c2ecf20Sopenharmony_ci		return -ERANGE;
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ci	oobregion->length = ecc->steps * 4;
24478c2ecf20Sopenharmony_ci	oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size;
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	return 0;
24508c2ecf20Sopenharmony_ci}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = {
24538c2ecf20Sopenharmony_ci	.ecc = qcom_nand_ooblayout_ecc,
24548c2ecf20Sopenharmony_ci	.free = qcom_nand_ooblayout_free,
24558c2ecf20Sopenharmony_ci};
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_cistatic int
24588c2ecf20Sopenharmony_ciqcom_nandc_calc_ecc_bytes(int step_size, int strength)
24598c2ecf20Sopenharmony_ci{
24608c2ecf20Sopenharmony_ci	return strength == 4 ? 12 : 16;
24618c2ecf20Sopenharmony_ci}
24628c2ecf20Sopenharmony_ciNAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes,
24638c2ecf20Sopenharmony_ci		     NANDC_STEP_SIZE, 4, 8);
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_cistatic int qcom_nand_attach_chip(struct nand_chip *chip)
24668c2ecf20Sopenharmony_ci{
24678c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
24688c2ecf20Sopenharmony_ci	struct qcom_nand_host *host = to_qcom_nand_host(chip);
24698c2ecf20Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
24708c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
24718c2ecf20Sopenharmony_ci	int cwperpage, bad_block_byte, ret;
24728c2ecf20Sopenharmony_ci	bool wide_bus;
24738c2ecf20Sopenharmony_ci	int ecc_mode = 1;
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci	/* controller only supports 512 bytes data steps */
24768c2ecf20Sopenharmony_ci	ecc->size = NANDC_STEP_SIZE;
24778c2ecf20Sopenharmony_ci	wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
24788c2ecf20Sopenharmony_ci	cwperpage = mtd->writesize / NANDC_STEP_SIZE;
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_ci	/*
24818c2ecf20Sopenharmony_ci	 * Each CW has 4 available OOB bytes which will be protected with ECC
24828c2ecf20Sopenharmony_ci	 * so remaining bytes can be used for ECC.
24838c2ecf20Sopenharmony_ci	 */
24848c2ecf20Sopenharmony_ci	ret = nand_ecc_choose_conf(chip, &qcom_nandc_ecc_caps,
24858c2ecf20Sopenharmony_ci				   mtd->oobsize - (cwperpage * 4));
24868c2ecf20Sopenharmony_ci	if (ret) {
24878c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "No valid ECC settings possible\n");
24888c2ecf20Sopenharmony_ci		return ret;
24898c2ecf20Sopenharmony_ci	}
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci	if (ecc->strength >= 8) {
24928c2ecf20Sopenharmony_ci		/* 8 bit ECC defaults to BCH ECC on all platforms */
24938c2ecf20Sopenharmony_ci		host->bch_enabled = true;
24948c2ecf20Sopenharmony_ci		ecc_mode = 1;
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci		if (wide_bus) {
24978c2ecf20Sopenharmony_ci			host->ecc_bytes_hw = 14;
24988c2ecf20Sopenharmony_ci			host->spare_bytes = 0;
24998c2ecf20Sopenharmony_ci			host->bbm_size = 2;
25008c2ecf20Sopenharmony_ci		} else {
25018c2ecf20Sopenharmony_ci			host->ecc_bytes_hw = 13;
25028c2ecf20Sopenharmony_ci			host->spare_bytes = 2;
25038c2ecf20Sopenharmony_ci			host->bbm_size = 1;
25048c2ecf20Sopenharmony_ci		}
25058c2ecf20Sopenharmony_ci	} else {
25068c2ecf20Sopenharmony_ci		/*
25078c2ecf20Sopenharmony_ci		 * if the controller supports BCH for 4 bit ECC, the controller
25088c2ecf20Sopenharmony_ci		 * uses lesser bytes for ECC. If RS is used, the ECC bytes is
25098c2ecf20Sopenharmony_ci		 * always 10 bytes
25108c2ecf20Sopenharmony_ci		 */
25118c2ecf20Sopenharmony_ci		if (nandc->props->ecc_modes & ECC_BCH_4BIT) {
25128c2ecf20Sopenharmony_ci			/* BCH */
25138c2ecf20Sopenharmony_ci			host->bch_enabled = true;
25148c2ecf20Sopenharmony_ci			ecc_mode = 0;
25158c2ecf20Sopenharmony_ci
25168c2ecf20Sopenharmony_ci			if (wide_bus) {
25178c2ecf20Sopenharmony_ci				host->ecc_bytes_hw = 8;
25188c2ecf20Sopenharmony_ci				host->spare_bytes = 2;
25198c2ecf20Sopenharmony_ci				host->bbm_size = 2;
25208c2ecf20Sopenharmony_ci			} else {
25218c2ecf20Sopenharmony_ci				host->ecc_bytes_hw = 7;
25228c2ecf20Sopenharmony_ci				host->spare_bytes = 4;
25238c2ecf20Sopenharmony_ci				host->bbm_size = 1;
25248c2ecf20Sopenharmony_ci			}
25258c2ecf20Sopenharmony_ci		} else {
25268c2ecf20Sopenharmony_ci			/* RS */
25278c2ecf20Sopenharmony_ci			host->ecc_bytes_hw = 10;
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci			if (wide_bus) {
25308c2ecf20Sopenharmony_ci				host->spare_bytes = 0;
25318c2ecf20Sopenharmony_ci				host->bbm_size = 2;
25328c2ecf20Sopenharmony_ci			} else {
25338c2ecf20Sopenharmony_ci				host->spare_bytes = 1;
25348c2ecf20Sopenharmony_ci				host->bbm_size = 1;
25358c2ecf20Sopenharmony_ci			}
25368c2ecf20Sopenharmony_ci		}
25378c2ecf20Sopenharmony_ci	}
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	/*
25408c2ecf20Sopenharmony_ci	 * we consider ecc->bytes as the sum of all the non-data content in a
25418c2ecf20Sopenharmony_ci	 * step. It gives us a clean representation of the oob area (even if
25428c2ecf20Sopenharmony_ci	 * all the bytes aren't used for ECC).It is always 16 bytes for 8 bit
25438c2ecf20Sopenharmony_ci	 * ECC and 12 bytes for 4 bit ECC
25448c2ecf20Sopenharmony_ci	 */
25458c2ecf20Sopenharmony_ci	ecc->bytes = host->ecc_bytes_hw + host->spare_bytes + host->bbm_size;
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci	ecc->read_page		= qcom_nandc_read_page;
25488c2ecf20Sopenharmony_ci	ecc->read_page_raw	= qcom_nandc_read_page_raw;
25498c2ecf20Sopenharmony_ci	ecc->read_oob		= qcom_nandc_read_oob;
25508c2ecf20Sopenharmony_ci	ecc->write_page		= qcom_nandc_write_page;
25518c2ecf20Sopenharmony_ci	ecc->write_page_raw	= qcom_nandc_write_page_raw;
25528c2ecf20Sopenharmony_ci	ecc->write_oob		= qcom_nandc_write_oob;
25538c2ecf20Sopenharmony_ci
25548c2ecf20Sopenharmony_ci	ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci	nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
25598c2ecf20Sopenharmony_ci				     cwperpage);
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	/*
25628c2ecf20Sopenharmony_ci	 * DATA_UD_BYTES varies based on whether the read/write command protects
25638c2ecf20Sopenharmony_ci	 * spare data with ECC too. We protect spare data by default, so we set
25648c2ecf20Sopenharmony_ci	 * it to main + spare data, which are 512 and 4 bytes respectively.
25658c2ecf20Sopenharmony_ci	 */
25668c2ecf20Sopenharmony_ci	host->cw_data = 516;
25678c2ecf20Sopenharmony_ci
25688c2ecf20Sopenharmony_ci	/*
25698c2ecf20Sopenharmony_ci	 * total bytes in a step, either 528 bytes for 4 bit ECC, or 532 bytes
25708c2ecf20Sopenharmony_ci	 * for 8 bit ECC
25718c2ecf20Sopenharmony_ci	 */
25728c2ecf20Sopenharmony_ci	host->cw_size = host->cw_data + ecc->bytes;
25738c2ecf20Sopenharmony_ci	bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1;
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ci	host->cfg0 = (cwperpage - 1) << CW_PER_PAGE
25768c2ecf20Sopenharmony_ci				| host->cw_data << UD_SIZE_BYTES
25778c2ecf20Sopenharmony_ci				| 0 << DISABLE_STATUS_AFTER_WRITE
25788c2ecf20Sopenharmony_ci				| 5 << NUM_ADDR_CYCLES
25798c2ecf20Sopenharmony_ci				| host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS
25808c2ecf20Sopenharmony_ci				| 0 << STATUS_BFR_READ
25818c2ecf20Sopenharmony_ci				| 1 << SET_RD_MODE_AFTER_STATUS
25828c2ecf20Sopenharmony_ci				| host->spare_bytes << SPARE_SIZE_BYTES;
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_ci	host->cfg1 = 7 << NAND_RECOVERY_CYCLES
25858c2ecf20Sopenharmony_ci				| 0 <<  CS_ACTIVE_BSY
25868c2ecf20Sopenharmony_ci				| bad_block_byte << BAD_BLOCK_BYTE_NUM
25878c2ecf20Sopenharmony_ci				| 0 << BAD_BLOCK_IN_SPARE_AREA
25888c2ecf20Sopenharmony_ci				| 2 << WR_RD_BSY_GAP
25898c2ecf20Sopenharmony_ci				| wide_bus << WIDE_FLASH
25908c2ecf20Sopenharmony_ci				| host->bch_enabled << ENABLE_BCH_ECC;
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_ci	host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
25938c2ecf20Sopenharmony_ci				| host->cw_size << UD_SIZE_BYTES
25948c2ecf20Sopenharmony_ci				| 5 << NUM_ADDR_CYCLES
25958c2ecf20Sopenharmony_ci				| 0 << SPARE_SIZE_BYTES;
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
25988c2ecf20Sopenharmony_ci				| 0 << CS_ACTIVE_BSY
25998c2ecf20Sopenharmony_ci				| 17 << BAD_BLOCK_BYTE_NUM
26008c2ecf20Sopenharmony_ci				| 1 << BAD_BLOCK_IN_SPARE_AREA
26018c2ecf20Sopenharmony_ci				| 2 << WR_RD_BSY_GAP
26028c2ecf20Sopenharmony_ci				| wide_bus << WIDE_FLASH
26038c2ecf20Sopenharmony_ci				| 1 << DEV0_CFG1_ECC_DISABLE;
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
26068c2ecf20Sopenharmony_ci				| 0 << ECC_SW_RESET
26078c2ecf20Sopenharmony_ci				| host->cw_data << ECC_NUM_DATA_BYTES
26088c2ecf20Sopenharmony_ci				| 1 << ECC_FORCE_CLK_OPEN
26098c2ecf20Sopenharmony_ci				| ecc_mode << ECC_MODE
26108c2ecf20Sopenharmony_ci				| host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH;
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	host->ecc_buf_cfg = 0x203 << NUM_STEPS;
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci	host->clrflashstatus = FS_READY_BSY_N;
26158c2ecf20Sopenharmony_ci	host->clrreadstatus = 0xc0;
26168c2ecf20Sopenharmony_ci	nandc->regs->erased_cw_detect_cfg_clr =
26178c2ecf20Sopenharmony_ci		cpu_to_le32(CLR_ERASED_PAGE_DET);
26188c2ecf20Sopenharmony_ci	nandc->regs->erased_cw_detect_cfg_set =
26198c2ecf20Sopenharmony_ci		cpu_to_le32(SET_ERASED_PAGE_DET);
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	dev_dbg(nandc->dev,
26228c2ecf20Sopenharmony_ci		"cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
26238c2ecf20Sopenharmony_ci		host->cfg0, host->cfg1, host->ecc_buf_cfg, host->ecc_bch_cfg,
26248c2ecf20Sopenharmony_ci		host->cw_size, host->cw_data, ecc->strength, ecc->bytes,
26258c2ecf20Sopenharmony_ci		cwperpage);
26268c2ecf20Sopenharmony_ci
26278c2ecf20Sopenharmony_ci	return 0;
26288c2ecf20Sopenharmony_ci}
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_cistatic const struct nand_controller_ops qcom_nandc_ops = {
26318c2ecf20Sopenharmony_ci	.attach_chip = qcom_nand_attach_chip,
26328c2ecf20Sopenharmony_ci};
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_cistatic void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
26358c2ecf20Sopenharmony_ci{
26368c2ecf20Sopenharmony_ci	if (nandc->props->is_bam) {
26378c2ecf20Sopenharmony_ci		if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
26388c2ecf20Sopenharmony_ci			dma_unmap_single(nandc->dev, nandc->reg_read_dma,
26398c2ecf20Sopenharmony_ci					 MAX_REG_RD *
26408c2ecf20Sopenharmony_ci					 sizeof(*nandc->reg_read_buf),
26418c2ecf20Sopenharmony_ci					 DMA_FROM_DEVICE);
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_ci		if (nandc->tx_chan)
26448c2ecf20Sopenharmony_ci			dma_release_channel(nandc->tx_chan);
26458c2ecf20Sopenharmony_ci
26468c2ecf20Sopenharmony_ci		if (nandc->rx_chan)
26478c2ecf20Sopenharmony_ci			dma_release_channel(nandc->rx_chan);
26488c2ecf20Sopenharmony_ci
26498c2ecf20Sopenharmony_ci		if (nandc->cmd_chan)
26508c2ecf20Sopenharmony_ci			dma_release_channel(nandc->cmd_chan);
26518c2ecf20Sopenharmony_ci	} else {
26528c2ecf20Sopenharmony_ci		if (nandc->chan)
26538c2ecf20Sopenharmony_ci			dma_release_channel(nandc->chan);
26548c2ecf20Sopenharmony_ci	}
26558c2ecf20Sopenharmony_ci}
26568c2ecf20Sopenharmony_ci
26578c2ecf20Sopenharmony_cistatic int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
26588c2ecf20Sopenharmony_ci{
26598c2ecf20Sopenharmony_ci	int ret;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32));
26628c2ecf20Sopenharmony_ci	if (ret) {
26638c2ecf20Sopenharmony_ci		dev_err(nandc->dev, "failed to set DMA mask\n");
26648c2ecf20Sopenharmony_ci		return ret;
26658c2ecf20Sopenharmony_ci	}
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci	/*
26688c2ecf20Sopenharmony_ci	 * we use the internal buffer for reading ONFI params, reading small
26698c2ecf20Sopenharmony_ci	 * data like ID and status, and preforming read-copy-write operations
26708c2ecf20Sopenharmony_ci	 * when writing to a codeword partially. 532 is the maximum possible
26718c2ecf20Sopenharmony_ci	 * size of a codeword for our nand controller
26728c2ecf20Sopenharmony_ci	 */
26738c2ecf20Sopenharmony_ci	nandc->buf_size = 532;
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci	nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size,
26768c2ecf20Sopenharmony_ci					GFP_KERNEL);
26778c2ecf20Sopenharmony_ci	if (!nandc->data_buffer)
26788c2ecf20Sopenharmony_ci		return -ENOMEM;
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci	nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs),
26818c2ecf20Sopenharmony_ci					GFP_KERNEL);
26828c2ecf20Sopenharmony_ci	if (!nandc->regs)
26838c2ecf20Sopenharmony_ci		return -ENOMEM;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	nandc->reg_read_buf = devm_kcalloc(nandc->dev,
26868c2ecf20Sopenharmony_ci				MAX_REG_RD, sizeof(*nandc->reg_read_buf),
26878c2ecf20Sopenharmony_ci				GFP_KERNEL);
26888c2ecf20Sopenharmony_ci	if (!nandc->reg_read_buf)
26898c2ecf20Sopenharmony_ci		return -ENOMEM;
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	if (nandc->props->is_bam) {
26928c2ecf20Sopenharmony_ci		nandc->reg_read_dma =
26938c2ecf20Sopenharmony_ci			dma_map_single(nandc->dev, nandc->reg_read_buf,
26948c2ecf20Sopenharmony_ci				       MAX_REG_RD *
26958c2ecf20Sopenharmony_ci				       sizeof(*nandc->reg_read_buf),
26968c2ecf20Sopenharmony_ci				       DMA_FROM_DEVICE);
26978c2ecf20Sopenharmony_ci		if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) {
26988c2ecf20Sopenharmony_ci			dev_err(nandc->dev, "failed to DMA MAP reg buffer\n");
26998c2ecf20Sopenharmony_ci			return -EIO;
27008c2ecf20Sopenharmony_ci		}
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci		nandc->tx_chan = dma_request_chan(nandc->dev, "tx");
27038c2ecf20Sopenharmony_ci		if (IS_ERR(nandc->tx_chan)) {
27048c2ecf20Sopenharmony_ci			ret = PTR_ERR(nandc->tx_chan);
27058c2ecf20Sopenharmony_ci			nandc->tx_chan = NULL;
27068c2ecf20Sopenharmony_ci			dev_err_probe(nandc->dev, ret,
27078c2ecf20Sopenharmony_ci				      "tx DMA channel request failed\n");
27088c2ecf20Sopenharmony_ci			goto unalloc;
27098c2ecf20Sopenharmony_ci		}
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ci		nandc->rx_chan = dma_request_chan(nandc->dev, "rx");
27128c2ecf20Sopenharmony_ci		if (IS_ERR(nandc->rx_chan)) {
27138c2ecf20Sopenharmony_ci			ret = PTR_ERR(nandc->rx_chan);
27148c2ecf20Sopenharmony_ci			nandc->rx_chan = NULL;
27158c2ecf20Sopenharmony_ci			dev_err_probe(nandc->dev, ret,
27168c2ecf20Sopenharmony_ci				      "rx DMA channel request failed\n");
27178c2ecf20Sopenharmony_ci			goto unalloc;
27188c2ecf20Sopenharmony_ci		}
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ci		nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd");
27218c2ecf20Sopenharmony_ci		if (IS_ERR(nandc->cmd_chan)) {
27228c2ecf20Sopenharmony_ci			ret = PTR_ERR(nandc->cmd_chan);
27238c2ecf20Sopenharmony_ci			nandc->cmd_chan = NULL;
27248c2ecf20Sopenharmony_ci			dev_err_probe(nandc->dev, ret,
27258c2ecf20Sopenharmony_ci				      "cmd DMA channel request failed\n");
27268c2ecf20Sopenharmony_ci			goto unalloc;
27278c2ecf20Sopenharmony_ci		}
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci		/*
27308c2ecf20Sopenharmony_ci		 * Initially allocate BAM transaction to read ONFI param page.
27318c2ecf20Sopenharmony_ci		 * After detecting all the devices, this BAM transaction will
27328c2ecf20Sopenharmony_ci		 * be freed and the next BAM tranasction will be allocated with
27338c2ecf20Sopenharmony_ci		 * maximum codeword size
27348c2ecf20Sopenharmony_ci		 */
27358c2ecf20Sopenharmony_ci		nandc->max_cwperpage = 1;
27368c2ecf20Sopenharmony_ci		nandc->bam_txn = alloc_bam_transaction(nandc);
27378c2ecf20Sopenharmony_ci		if (!nandc->bam_txn) {
27388c2ecf20Sopenharmony_ci			dev_err(nandc->dev,
27398c2ecf20Sopenharmony_ci				"failed to allocate bam transaction\n");
27408c2ecf20Sopenharmony_ci			ret = -ENOMEM;
27418c2ecf20Sopenharmony_ci			goto unalloc;
27428c2ecf20Sopenharmony_ci		}
27438c2ecf20Sopenharmony_ci	} else {
27448c2ecf20Sopenharmony_ci		nandc->chan = dma_request_chan(nandc->dev, "rxtx");
27458c2ecf20Sopenharmony_ci		if (IS_ERR(nandc->chan)) {
27468c2ecf20Sopenharmony_ci			ret = PTR_ERR(nandc->chan);
27478c2ecf20Sopenharmony_ci			nandc->chan = NULL;
27488c2ecf20Sopenharmony_ci			dev_err_probe(nandc->dev, ret,
27498c2ecf20Sopenharmony_ci				      "rxtx DMA channel request failed\n");
27508c2ecf20Sopenharmony_ci			return ret;
27518c2ecf20Sopenharmony_ci		}
27528c2ecf20Sopenharmony_ci	}
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&nandc->desc_list);
27558c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&nandc->host_list);
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	nand_controller_init(&nandc->controller);
27588c2ecf20Sopenharmony_ci	nandc->controller.ops = &qcom_nandc_ops;
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	return 0;
27618c2ecf20Sopenharmony_ciunalloc:
27628c2ecf20Sopenharmony_ci	qcom_nandc_unalloc(nandc);
27638c2ecf20Sopenharmony_ci	return ret;
27648c2ecf20Sopenharmony_ci}
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci/* one time setup of a few nand controller registers */
27678c2ecf20Sopenharmony_cistatic int qcom_nandc_setup(struct qcom_nand_controller *nandc)
27688c2ecf20Sopenharmony_ci{
27698c2ecf20Sopenharmony_ci	u32 nand_ctrl;
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_ci	/* kill onenand */
27728c2ecf20Sopenharmony_ci	if (!nandc->props->is_qpic)
27738c2ecf20Sopenharmony_ci		nandc_write(nandc, SFLASHC_BURST_CFG, 0);
27748c2ecf20Sopenharmony_ci	nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
27758c2ecf20Sopenharmony_ci		    NAND_DEV_CMD_VLD_VAL);
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_ci	/* enable ADM or BAM DMA */
27788c2ecf20Sopenharmony_ci	if (nandc->props->is_bam) {
27798c2ecf20Sopenharmony_ci		nand_ctrl = nandc_read(nandc, NAND_CTRL);
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci		/*
27828c2ecf20Sopenharmony_ci		 *NAND_CTRL is an operational registers, and CPU
27838c2ecf20Sopenharmony_ci		 * access to operational registers are read only
27848c2ecf20Sopenharmony_ci		 * in BAM mode. So update the NAND_CTRL register
27858c2ecf20Sopenharmony_ci		 * only if it is not in BAM mode. In most cases BAM
27868c2ecf20Sopenharmony_ci		 * mode will be enabled in bootloader
27878c2ecf20Sopenharmony_ci		 */
27888c2ecf20Sopenharmony_ci		if (!(nand_ctrl & BAM_MODE_EN))
27898c2ecf20Sopenharmony_ci			nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
27908c2ecf20Sopenharmony_ci	} else {
27918c2ecf20Sopenharmony_ci		nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
27928c2ecf20Sopenharmony_ci	}
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	/* save the original values of these registers */
27958c2ecf20Sopenharmony_ci	nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
27968c2ecf20Sopenharmony_ci	nandc->vld = NAND_DEV_CMD_VLD_VAL;
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci	return 0;
27998c2ecf20Sopenharmony_ci}
28008c2ecf20Sopenharmony_ci
28018c2ecf20Sopenharmony_cistatic int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
28028c2ecf20Sopenharmony_ci					    struct qcom_nand_host *host,
28038c2ecf20Sopenharmony_ci					    struct device_node *dn)
28048c2ecf20Sopenharmony_ci{
28058c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
28068c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
28078c2ecf20Sopenharmony_ci	struct device *dev = nandc->dev;
28088c2ecf20Sopenharmony_ci	int ret;
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_ci	ret = of_property_read_u32(dn, "reg", &host->cs);
28118c2ecf20Sopenharmony_ci	if (ret) {
28128c2ecf20Sopenharmony_ci		dev_err(dev, "can't get chip-select\n");
28138c2ecf20Sopenharmony_ci		return -ENXIO;
28148c2ecf20Sopenharmony_ci	}
28158c2ecf20Sopenharmony_ci
28168c2ecf20Sopenharmony_ci	nand_set_flash_node(chip, dn);
28178c2ecf20Sopenharmony_ci	mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs);
28188c2ecf20Sopenharmony_ci	if (!mtd->name)
28198c2ecf20Sopenharmony_ci		return -ENOMEM;
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	mtd->owner = THIS_MODULE;
28228c2ecf20Sopenharmony_ci	mtd->dev.parent = dev;
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	chip->legacy.cmdfunc	= qcom_nandc_command;
28258c2ecf20Sopenharmony_ci	chip->legacy.select_chip	= qcom_nandc_select_chip;
28268c2ecf20Sopenharmony_ci	chip->legacy.read_byte	= qcom_nandc_read_byte;
28278c2ecf20Sopenharmony_ci	chip->legacy.read_buf	= qcom_nandc_read_buf;
28288c2ecf20Sopenharmony_ci	chip->legacy.write_buf	= qcom_nandc_write_buf;
28298c2ecf20Sopenharmony_ci	chip->legacy.set_features	= nand_get_set_features_notsupp;
28308c2ecf20Sopenharmony_ci	chip->legacy.get_features	= nand_get_set_features_notsupp;
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci	/*
28338c2ecf20Sopenharmony_ci	 * the bad block marker is readable only when we read the last codeword
28348c2ecf20Sopenharmony_ci	 * of a page with ECC disabled. currently, the nand_base and nand_bbt
28358c2ecf20Sopenharmony_ci	 * helpers don't allow us to read BB from a nand chip with ECC
28368c2ecf20Sopenharmony_ci	 * disabled (MTD_OPS_PLACE_OOB is set by default). use the block_bad
28378c2ecf20Sopenharmony_ci	 * and block_markbad helpers until we permanently switch to using
28388c2ecf20Sopenharmony_ci	 * MTD_OPS_RAW for all drivers (with the help of badblockbits)
28398c2ecf20Sopenharmony_ci	 */
28408c2ecf20Sopenharmony_ci	chip->legacy.block_bad		= qcom_nandc_block_bad;
28418c2ecf20Sopenharmony_ci	chip->legacy.block_markbad	= qcom_nandc_block_markbad;
28428c2ecf20Sopenharmony_ci
28438c2ecf20Sopenharmony_ci	chip->controller = &nandc->controller;
28448c2ecf20Sopenharmony_ci	chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA |
28458c2ecf20Sopenharmony_ci			 NAND_SKIP_BBTSCAN;
28468c2ecf20Sopenharmony_ci
28478c2ecf20Sopenharmony_ci	/* set up initial status value */
28488c2ecf20Sopenharmony_ci	host->status = NAND_STATUS_READY | NAND_STATUS_WP;
28498c2ecf20Sopenharmony_ci
28508c2ecf20Sopenharmony_ci	ret = nand_scan(chip, 1);
28518c2ecf20Sopenharmony_ci	if (ret)
28528c2ecf20Sopenharmony_ci		return ret;
28538c2ecf20Sopenharmony_ci
28548c2ecf20Sopenharmony_ci	if (nandc->props->is_bam) {
28558c2ecf20Sopenharmony_ci		free_bam_transaction(nandc);
28568c2ecf20Sopenharmony_ci		nandc->bam_txn = alloc_bam_transaction(nandc);
28578c2ecf20Sopenharmony_ci		if (!nandc->bam_txn) {
28588c2ecf20Sopenharmony_ci			dev_err(nandc->dev,
28598c2ecf20Sopenharmony_ci				"failed to allocate bam transaction\n");
28608c2ecf20Sopenharmony_ci			return -ENOMEM;
28618c2ecf20Sopenharmony_ci		}
28628c2ecf20Sopenharmony_ci	}
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ci	ret = mtd_device_register(mtd, NULL, 0);
28658c2ecf20Sopenharmony_ci	if (ret)
28668c2ecf20Sopenharmony_ci		nand_cleanup(chip);
28678c2ecf20Sopenharmony_ci
28688c2ecf20Sopenharmony_ci	return ret;
28698c2ecf20Sopenharmony_ci}
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_cistatic int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
28728c2ecf20Sopenharmony_ci{
28738c2ecf20Sopenharmony_ci	struct device *dev = nandc->dev;
28748c2ecf20Sopenharmony_ci	struct device_node *dn = dev->of_node, *child;
28758c2ecf20Sopenharmony_ci	struct qcom_nand_host *host;
28768c2ecf20Sopenharmony_ci	int ret = -ENODEV;
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci	for_each_available_child_of_node(dn, child) {
28798c2ecf20Sopenharmony_ci		host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
28808c2ecf20Sopenharmony_ci		if (!host) {
28818c2ecf20Sopenharmony_ci			of_node_put(child);
28828c2ecf20Sopenharmony_ci			return -ENOMEM;
28838c2ecf20Sopenharmony_ci		}
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci		ret = qcom_nand_host_init_and_register(nandc, host, child);
28868c2ecf20Sopenharmony_ci		if (ret) {
28878c2ecf20Sopenharmony_ci			devm_kfree(dev, host);
28888c2ecf20Sopenharmony_ci			continue;
28898c2ecf20Sopenharmony_ci		}
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_ci		list_add_tail(&host->node, &nandc->host_list);
28928c2ecf20Sopenharmony_ci	}
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci	return ret;
28958c2ecf20Sopenharmony_ci}
28968c2ecf20Sopenharmony_ci
28978c2ecf20Sopenharmony_ci/* parse custom DT properties here */
28988c2ecf20Sopenharmony_cistatic int qcom_nandc_parse_dt(struct platform_device *pdev)
28998c2ecf20Sopenharmony_ci{
29008c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = platform_get_drvdata(pdev);
29018c2ecf20Sopenharmony_ci	struct device_node *np = nandc->dev->of_node;
29028c2ecf20Sopenharmony_ci	int ret;
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci	if (!nandc->props->is_bam) {
29058c2ecf20Sopenharmony_ci		ret = of_property_read_u32(np, "qcom,cmd-crci",
29068c2ecf20Sopenharmony_ci					   &nandc->cmd_crci);
29078c2ecf20Sopenharmony_ci		if (ret) {
29088c2ecf20Sopenharmony_ci			dev_err(nandc->dev, "command CRCI unspecified\n");
29098c2ecf20Sopenharmony_ci			return ret;
29108c2ecf20Sopenharmony_ci		}
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ci		ret = of_property_read_u32(np, "qcom,data-crci",
29138c2ecf20Sopenharmony_ci					   &nandc->data_crci);
29148c2ecf20Sopenharmony_ci		if (ret) {
29158c2ecf20Sopenharmony_ci			dev_err(nandc->dev, "data CRCI unspecified\n");
29168c2ecf20Sopenharmony_ci			return ret;
29178c2ecf20Sopenharmony_ci		}
29188c2ecf20Sopenharmony_ci	}
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci	return 0;
29218c2ecf20Sopenharmony_ci}
29228c2ecf20Sopenharmony_ci
29238c2ecf20Sopenharmony_cistatic int qcom_nandc_probe(struct platform_device *pdev)
29248c2ecf20Sopenharmony_ci{
29258c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc;
29268c2ecf20Sopenharmony_ci	const void *dev_data;
29278c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
29288c2ecf20Sopenharmony_ci	struct resource *res;
29298c2ecf20Sopenharmony_ci	int ret;
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_ci	nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL);
29328c2ecf20Sopenharmony_ci	if (!nandc)
29338c2ecf20Sopenharmony_ci		return -ENOMEM;
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, nandc);
29368c2ecf20Sopenharmony_ci	nandc->dev = dev;
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_ci	dev_data = of_device_get_match_data(dev);
29398c2ecf20Sopenharmony_ci	if (!dev_data) {
29408c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get device data\n");
29418c2ecf20Sopenharmony_ci		return -ENODEV;
29428c2ecf20Sopenharmony_ci	}
29438c2ecf20Sopenharmony_ci
29448c2ecf20Sopenharmony_ci	nandc->props = dev_data;
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_ci	nandc->core_clk = devm_clk_get(dev, "core");
29478c2ecf20Sopenharmony_ci	if (IS_ERR(nandc->core_clk))
29488c2ecf20Sopenharmony_ci		return PTR_ERR(nandc->core_clk);
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_ci	nandc->aon_clk = devm_clk_get(dev, "aon");
29518c2ecf20Sopenharmony_ci	if (IS_ERR(nandc->aon_clk))
29528c2ecf20Sopenharmony_ci		return PTR_ERR(nandc->aon_clk);
29538c2ecf20Sopenharmony_ci
29548c2ecf20Sopenharmony_ci	ret = qcom_nandc_parse_dt(pdev);
29558c2ecf20Sopenharmony_ci	if (ret)
29568c2ecf20Sopenharmony_ci		return ret;
29578c2ecf20Sopenharmony_ci
29588c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
29598c2ecf20Sopenharmony_ci	nandc->base = devm_ioremap_resource(dev, res);
29608c2ecf20Sopenharmony_ci	if (IS_ERR(nandc->base))
29618c2ecf20Sopenharmony_ci		return PTR_ERR(nandc->base);
29628c2ecf20Sopenharmony_ci
29638c2ecf20Sopenharmony_ci	nandc->base_phys = res->start;
29648c2ecf20Sopenharmony_ci	nandc->base_dma = dma_map_resource(dev, res->start,
29658c2ecf20Sopenharmony_ci					   resource_size(res),
29668c2ecf20Sopenharmony_ci					   DMA_BIDIRECTIONAL, 0);
29678c2ecf20Sopenharmony_ci	if (!nandc->base_dma)
29688c2ecf20Sopenharmony_ci		return -ENXIO;
29698c2ecf20Sopenharmony_ci
29708c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(nandc->core_clk);
29718c2ecf20Sopenharmony_ci	if (ret)
29728c2ecf20Sopenharmony_ci		goto err_core_clk;
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(nandc->aon_clk);
29758c2ecf20Sopenharmony_ci	if (ret)
29768c2ecf20Sopenharmony_ci		goto err_aon_clk;
29778c2ecf20Sopenharmony_ci
29788c2ecf20Sopenharmony_ci	ret = qcom_nandc_alloc(nandc);
29798c2ecf20Sopenharmony_ci	if (ret)
29808c2ecf20Sopenharmony_ci		goto err_nandc_alloc;
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ci	ret = qcom_nandc_setup(nandc);
29838c2ecf20Sopenharmony_ci	if (ret)
29848c2ecf20Sopenharmony_ci		goto err_setup;
29858c2ecf20Sopenharmony_ci
29868c2ecf20Sopenharmony_ci	ret = qcom_probe_nand_devices(nandc);
29878c2ecf20Sopenharmony_ci	if (ret)
29888c2ecf20Sopenharmony_ci		goto err_setup;
29898c2ecf20Sopenharmony_ci
29908c2ecf20Sopenharmony_ci	return 0;
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_cierr_setup:
29938c2ecf20Sopenharmony_ci	qcom_nandc_unalloc(nandc);
29948c2ecf20Sopenharmony_cierr_nandc_alloc:
29958c2ecf20Sopenharmony_ci	clk_disable_unprepare(nandc->aon_clk);
29968c2ecf20Sopenharmony_cierr_aon_clk:
29978c2ecf20Sopenharmony_ci	clk_disable_unprepare(nandc->core_clk);
29988c2ecf20Sopenharmony_cierr_core_clk:
29998c2ecf20Sopenharmony_ci	dma_unmap_resource(dev, nandc->base_dma, resource_size(res),
30008c2ecf20Sopenharmony_ci			   DMA_BIDIRECTIONAL, 0);
30018c2ecf20Sopenharmony_ci	return ret;
30028c2ecf20Sopenharmony_ci}
30038c2ecf20Sopenharmony_ci
30048c2ecf20Sopenharmony_cistatic int qcom_nandc_remove(struct platform_device *pdev)
30058c2ecf20Sopenharmony_ci{
30068c2ecf20Sopenharmony_ci	struct qcom_nand_controller *nandc = platform_get_drvdata(pdev);
30078c2ecf20Sopenharmony_ci	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
30088c2ecf20Sopenharmony_ci	struct qcom_nand_host *host;
30098c2ecf20Sopenharmony_ci	struct nand_chip *chip;
30108c2ecf20Sopenharmony_ci	int ret;
30118c2ecf20Sopenharmony_ci
30128c2ecf20Sopenharmony_ci	list_for_each_entry(host, &nandc->host_list, node) {
30138c2ecf20Sopenharmony_ci		chip = &host->chip;
30148c2ecf20Sopenharmony_ci		ret = mtd_device_unregister(nand_to_mtd(chip));
30158c2ecf20Sopenharmony_ci		WARN_ON(ret);
30168c2ecf20Sopenharmony_ci		nand_cleanup(chip);
30178c2ecf20Sopenharmony_ci	}
30188c2ecf20Sopenharmony_ci
30198c2ecf20Sopenharmony_ci	qcom_nandc_unalloc(nandc);
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_ci	clk_disable_unprepare(nandc->aon_clk);
30228c2ecf20Sopenharmony_ci	clk_disable_unprepare(nandc->core_clk);
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ci	dma_unmap_resource(&pdev->dev, nandc->base_dma, resource_size(res),
30258c2ecf20Sopenharmony_ci			   DMA_BIDIRECTIONAL, 0);
30268c2ecf20Sopenharmony_ci
30278c2ecf20Sopenharmony_ci	return 0;
30288c2ecf20Sopenharmony_ci}
30298c2ecf20Sopenharmony_ci
30308c2ecf20Sopenharmony_cistatic const struct qcom_nandc_props ipq806x_nandc_props = {
30318c2ecf20Sopenharmony_ci	.ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
30328c2ecf20Sopenharmony_ci	.is_bam = false,
30338c2ecf20Sopenharmony_ci	.dev_cmd_reg_start = 0x0,
30348c2ecf20Sopenharmony_ci};
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_cistatic const struct qcom_nandc_props ipq4019_nandc_props = {
30378c2ecf20Sopenharmony_ci	.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
30388c2ecf20Sopenharmony_ci	.is_bam = true,
30398c2ecf20Sopenharmony_ci	.is_qpic = true,
30408c2ecf20Sopenharmony_ci	.dev_cmd_reg_start = 0x0,
30418c2ecf20Sopenharmony_ci};
30428c2ecf20Sopenharmony_ci
30438c2ecf20Sopenharmony_cistatic const struct qcom_nandc_props ipq8074_nandc_props = {
30448c2ecf20Sopenharmony_ci	.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
30458c2ecf20Sopenharmony_ci	.is_bam = true,
30468c2ecf20Sopenharmony_ci	.is_qpic = true,
30478c2ecf20Sopenharmony_ci	.dev_cmd_reg_start = 0x7000,
30488c2ecf20Sopenharmony_ci};
30498c2ecf20Sopenharmony_ci
30508c2ecf20Sopenharmony_ci/*
30518c2ecf20Sopenharmony_ci * data will hold a struct pointer containing more differences once we support
30528c2ecf20Sopenharmony_ci * more controller variants
30538c2ecf20Sopenharmony_ci */
30548c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_nandc_of_match[] = {
30558c2ecf20Sopenharmony_ci	{
30568c2ecf20Sopenharmony_ci		.compatible = "qcom,ipq806x-nand",
30578c2ecf20Sopenharmony_ci		.data = &ipq806x_nandc_props,
30588c2ecf20Sopenharmony_ci	},
30598c2ecf20Sopenharmony_ci	{
30608c2ecf20Sopenharmony_ci		.compatible = "qcom,ipq4019-nand",
30618c2ecf20Sopenharmony_ci		.data = &ipq4019_nandc_props,
30628c2ecf20Sopenharmony_ci	},
30638c2ecf20Sopenharmony_ci	{
30648c2ecf20Sopenharmony_ci		.compatible = "qcom,ipq8074-nand",
30658c2ecf20Sopenharmony_ci		.data = &ipq8074_nandc_props,
30668c2ecf20Sopenharmony_ci	},
30678c2ecf20Sopenharmony_ci	{}
30688c2ecf20Sopenharmony_ci};
30698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
30708c2ecf20Sopenharmony_ci
30718c2ecf20Sopenharmony_cistatic struct platform_driver qcom_nandc_driver = {
30728c2ecf20Sopenharmony_ci	.driver = {
30738c2ecf20Sopenharmony_ci		.name = "qcom-nandc",
30748c2ecf20Sopenharmony_ci		.of_match_table = qcom_nandc_of_match,
30758c2ecf20Sopenharmony_ci	},
30768c2ecf20Sopenharmony_ci	.probe   = qcom_nandc_probe,
30778c2ecf20Sopenharmony_ci	.remove  = qcom_nandc_remove,
30788c2ecf20Sopenharmony_ci};
30798c2ecf20Sopenharmony_cimodule_platform_driver(qcom_nandc_driver);
30808c2ecf20Sopenharmony_ci
30818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Archit Taneja <architt@codeaurora.org>");
30828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm NAND Controller driver");
30838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3084