xref: /rk3399_ARM-atf/drivers/cadence/nand/cdns_nand.c (revision a773f4121b3064fba24631e980c6226f23378e06)
1ddaf02d1SJit Loon Lim /*
2ddaf02d1SJit Loon Lim  * Copyright (c) 2022-2023, Intel Corporation. All rights reserved.
3ddaf02d1SJit Loon Lim  *
4ddaf02d1SJit Loon Lim  * SPDX-License-Identifier: BSD-3-Clause
5ddaf02d1SJit Loon Lim  */
6ddaf02d1SJit Loon Lim 
7ddaf02d1SJit Loon Lim #include <assert.h>
8ddaf02d1SJit Loon Lim #include <errno.h>
9ddaf02d1SJit Loon Lim #include <stdbool.h>
10ddaf02d1SJit Loon Lim #include <string.h>
11ddaf02d1SJit Loon Lim 
12ddaf02d1SJit Loon Lim #include <arch_helpers.h>
13ddaf02d1SJit Loon Lim #include <common/debug.h>
14ddaf02d1SJit Loon Lim #include <drivers/cadence/cdns_nand.h>
15ddaf02d1SJit Loon Lim #include <drivers/delay_timer.h>
16ddaf02d1SJit Loon Lim #include <lib/mmio.h>
17ddaf02d1SJit Loon Lim #include <lib/utils.h>
18ddaf02d1SJit Loon Lim #include <platform_def.h>
19ddaf02d1SJit Loon Lim 
20ddaf02d1SJit Loon Lim /* NAND flash device information struct */
21ddaf02d1SJit Loon Lim static cnf_dev_info_t dev_info;
22ddaf02d1SJit Loon Lim 
23*a773f412SGirisha Dengi /*
24*a773f412SGirisha Dengi  * Scratch buffers for read and write operations
25*a773f412SGirisha Dengi  * DMA transfer of Cadence NAND expects data 8 bytes aligned
26*a773f412SGirisha Dengi  * to be written to register
27*a773f412SGirisha Dengi  */
28*a773f412SGirisha Dengi static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE] __aligned(8);
29ddaf02d1SJit Loon Lim 
30ddaf02d1SJit Loon Lim /* Wait for controller to be in idle state */
31ddaf02d1SJit Loon Lim static inline void cdns_nand_wait_idle(void)
32ddaf02d1SJit Loon Lim {
33ddaf02d1SJit Loon Lim 	uint32_t reg = 0U;
34ddaf02d1SJit Loon Lim 
35ddaf02d1SJit Loon Lim 	do {
36ddaf02d1SJit Loon Lim 		udelay(CNF_DEF_DELAY_US);
37ddaf02d1SJit Loon Lim 		reg = mmio_read_32(CNF_CMDREG(CTRL_STATUS));
38ddaf02d1SJit Loon Lim 	} while (CNF_GET_CTRL_BUSY(reg) != 0U);
39ddaf02d1SJit Loon Lim }
40ddaf02d1SJit Loon Lim 
41ddaf02d1SJit Loon Lim /* Wait for given thread to be in ready state */
42ddaf02d1SJit Loon Lim static inline void cdns_nand_wait_thread_ready(uint8_t thread_id)
43ddaf02d1SJit Loon Lim {
44ddaf02d1SJit Loon Lim 	uint32_t reg = 0U;
45ddaf02d1SJit Loon Lim 
46ddaf02d1SJit Loon Lim 	do {
47ddaf02d1SJit Loon Lim 		udelay(CNF_DEF_DELAY_US);
48ddaf02d1SJit Loon Lim 		reg = mmio_read_32(CNF_CMDREG(TRD_STATUS));
49ddaf02d1SJit Loon Lim 		reg &= (1U << (uint32_t)thread_id);
50ddaf02d1SJit Loon Lim 	} while (reg != 0U);
51ddaf02d1SJit Loon Lim }
52ddaf02d1SJit Loon Lim 
53ddaf02d1SJit Loon Lim /* Check if the last operation/command in selected thread is completed */
54ddaf02d1SJit Loon Lim static int cdns_nand_last_opr_status(uint8_t thread_id)
55ddaf02d1SJit Loon Lim {
56ddaf02d1SJit Loon Lim 	uint8_t nthreads = 0U;
57ddaf02d1SJit Loon Lim 	uint32_t reg = 0U;
58ddaf02d1SJit Loon Lim 
59ddaf02d1SJit Loon Lim 	/* Get number of threads */
60ddaf02d1SJit Loon Lim 	reg = mmio_read_32(CNF_CTRLPARAM(FEATURE));
61ddaf02d1SJit Loon Lim 	nthreads = CNF_GET_NTHREADS(reg);
62ddaf02d1SJit Loon Lim 
63ddaf02d1SJit Loon Lim 	if (thread_id > nthreads) {
64ddaf02d1SJit Loon Lim 		ERROR("%s: Invalid thread ID\n", __func__);
65ddaf02d1SJit Loon Lim 		return -EINVAL;
66ddaf02d1SJit Loon Lim 	}
67ddaf02d1SJit Loon Lim 
68ddaf02d1SJit Loon Lim 	/* Select thread */
69ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_STAT_PTR), (uint32_t)thread_id);
70ddaf02d1SJit Loon Lim 
71ddaf02d1SJit Loon Lim 	uint32_t err_mask = CNF_ECMD | CNF_EECC | CNF_EDEV | CNF_EDQS | CNF_EFAIL |
72ddaf02d1SJit Loon Lim 				CNF_EBUS | CNF_EDI | CNF_EPAR | CNF_ECTX | CNF_EPRO;
73ddaf02d1SJit Loon Lim 
74ddaf02d1SJit Loon Lim 	do {
75ddaf02d1SJit Loon Lim 		udelay(CNF_DEF_DELAY_US * 2);
76ddaf02d1SJit Loon Lim 		reg = mmio_read_32(CNF_CMDREG(CMD_STAT));
77ddaf02d1SJit Loon Lim 	} while ((reg & CNF_CMPLT) == 0U);
78ddaf02d1SJit Loon Lim 
79ddaf02d1SJit Loon Lim 	/* last operation is completed, make sure no other error bits are set */
80ddaf02d1SJit Loon Lim 	if ((reg & err_mask) == 1U) {
81ddaf02d1SJit Loon Lim 		ERROR("%s, CMD_STATUS:0x%x\n", __func__, reg);
82ddaf02d1SJit Loon Lim 		return -EIO;
83ddaf02d1SJit Loon Lim 	}
84ddaf02d1SJit Loon Lim 
85ddaf02d1SJit Loon Lim 	return 0;
86ddaf02d1SJit Loon Lim }
87ddaf02d1SJit Loon Lim 
88ddaf02d1SJit Loon Lim /* Set feature command */
89ddaf02d1SJit Loon Lim int cdns_nand_set_feature(uint8_t feat_addr, uint8_t feat_val, uint8_t thread_id)
90ddaf02d1SJit Loon Lim {
91ddaf02d1SJit Loon Lim 	/* Wait for thread to be ready */
92ddaf02d1SJit Loon Lim 	cdns_nand_wait_thread_ready(thread_id);
93ddaf02d1SJit Loon Lim 
94ddaf02d1SJit Loon Lim 	/* Set feature address */
95ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG1), (uint32_t)feat_addr);
96ddaf02d1SJit Loon Lim 	/* Set feature volume */
97ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG2), (uint32_t)feat_val);
98ddaf02d1SJit Loon Lim 
99ddaf02d1SJit Loon Lim 	/* Set feature command */
100ddaf02d1SJit Loon Lim 	uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT);
101ddaf02d1SJit Loon Lim 
102ddaf02d1SJit Loon Lim 	reg |= (thread_id << CNF_CMDREG0_TRD);
103ddaf02d1SJit Loon Lim 	reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL);
104ddaf02d1SJit Loon Lim 	reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR);
105ddaf02d1SJit Loon Lim 	reg |= (CNF_CT_SET_FEATURE << CNF_CMDREG0_CMD);
106ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG0), reg);
107ddaf02d1SJit Loon Lim 
108ddaf02d1SJit Loon Lim 	return cdns_nand_last_opr_status(thread_id);
109ddaf02d1SJit Loon Lim }
110ddaf02d1SJit Loon Lim 
111ddaf02d1SJit Loon Lim /* Reset command to the selected device */
112ddaf02d1SJit Loon Lim int cdns_nand_reset(uint8_t thread_id)
113ddaf02d1SJit Loon Lim {
114ddaf02d1SJit Loon Lim 	/* Operation is executed in selected thread */
115ddaf02d1SJit Loon Lim 	cdns_nand_wait_thread_ready(thread_id);
116ddaf02d1SJit Loon Lim 
117ddaf02d1SJit Loon Lim 	/* Select memory */
118*a773f412SGirisha Dengi 	mmio_write_32(CNF_CMDREG(CMD_REG4),
119*a773f412SGirisha Dengi 			(CNF_DEF_DEVICE << CNF_CMDREG4_MEM));
120ddaf02d1SJit Loon Lim 
121ddaf02d1SJit Loon Lim 	/* Issue reset command */
122ddaf02d1SJit Loon Lim 	uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT);
123ddaf02d1SJit Loon Lim 
124ddaf02d1SJit Loon Lim 	reg |= (thread_id << CNF_CMDREG0_TRD);
125ddaf02d1SJit Loon Lim 	reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL);
126ddaf02d1SJit Loon Lim 	reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR);
127ddaf02d1SJit Loon Lim 	reg |= (CNF_CT_RESET_ASYNC << CNF_CMDREG0_CMD);
128ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG0), reg);
129ddaf02d1SJit Loon Lim 
130ddaf02d1SJit Loon Lim 	return cdns_nand_last_opr_status(thread_id);
131ddaf02d1SJit Loon Lim }
132ddaf02d1SJit Loon Lim 
133ddaf02d1SJit Loon Lim /* Set operation work mode */
134ddaf02d1SJit Loon Lim static void cdns_nand_set_opr_mode(uint8_t opr_mode)
135ddaf02d1SJit Loon Lim {
136ddaf02d1SJit Loon Lim 	/* Wait for controller to be in idle state */
137ddaf02d1SJit Loon Lim 	cdns_nand_wait_idle();
138ddaf02d1SJit Loon Lim 
139ddaf02d1SJit Loon Lim 	/* Reset DLL PHY */
140ddaf02d1SJit Loon Lim 	uint32_t reg = mmio_read_32(CNF_MINICTRL(DLL_PHY_CTRL));
141ddaf02d1SJit Loon Lim 
142ddaf02d1SJit Loon Lim 	reg &= ~(1 << CNF_DLL_PHY_RST_N);
143ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_MINICTRL(DLL_PHY_CTRL), reg);
144ddaf02d1SJit Loon Lim 
145ddaf02d1SJit Loon Lim 	if (opr_mode == CNF_OPR_WORK_MODE_SDR) {
146ddaf02d1SJit Loon Lim 		/* Combo PHY Control Timing Block register settings */
147ddaf02d1SJit Loon Lim 		mmio_write_32(CP_CTB(CTRL_REG), CP_CTRL_REG_SDR);
148ddaf02d1SJit Loon Lim 		mmio_write_32(CP_CTB(TSEL_REG), CP_TSEL_REG_SDR);
149ddaf02d1SJit Loon Lim 
150ddaf02d1SJit Loon Lim 		/* Combo PHY DLL register settings */
151ddaf02d1SJit Loon Lim 		mmio_write_32(CP_DLL(DQ_TIMING_REG), CP_DQ_TIMING_REG_SDR);
152ddaf02d1SJit Loon Lim 		mmio_write_32(CP_DLL(DQS_TIMING_REG), CP_DQS_TIMING_REG_SDR);
153ddaf02d1SJit Loon Lim 		mmio_write_32(CP_DLL(GATE_LPBK_CTRL_REG), CP_GATE_LPBK_CTRL_REG_SDR);
154ddaf02d1SJit Loon Lim 		mmio_write_32(CP_DLL(MASTER_CTRL_REG), CP_DLL_MASTER_CTRL_REG_SDR);
155ddaf02d1SJit Loon Lim 
156ddaf02d1SJit Loon Lim 		/* Async mode timing settings */
157ddaf02d1SJit Loon Lim 		mmio_write_32(CNF_MINICTRL(ASYNC_TOGGLE_TIMINGS),
158ddaf02d1SJit Loon Lim 				(2 << CNF_ASYNC_TIMINGS_TRH) |
159ddaf02d1SJit Loon Lim 				(4 << CNF_ASYNC_TIMINGS_TRP) |
160ddaf02d1SJit Loon Lim 				(2 << CNF_ASYNC_TIMINGS_TWH) |
161ddaf02d1SJit Loon Lim 				(4 << CNF_ASYNC_TIMINGS_TWP));
162ddaf02d1SJit Loon Lim 
163ddaf02d1SJit Loon Lim 		/* Set extended read and write mode */
164ddaf02d1SJit Loon Lim 		reg |= (1 << CNF_DLL_PHY_EXT_RD_MODE);
165ddaf02d1SJit Loon Lim 		reg |= (1 << CNF_DLL_PHY_EXT_WR_MODE);
166ddaf02d1SJit Loon Lim 
167ddaf02d1SJit Loon Lim 		/* Set operation work mode in common settings */
168*a773f412SGirisha Dengi 		mmio_clrsetbits_32(CNF_MINICTRL(CMN_SETTINGS),
169*a773f412SGirisha Dengi 				CNF_CMN_SETTINGS_OPR_MASK,
170*a773f412SGirisha Dengi 				CNF_OPR_WORK_MODE_SDR);
171ddaf02d1SJit Loon Lim 	} else if (opr_mode == CNF_OPR_WORK_MODE_NVDDR) {
172ddaf02d1SJit Loon Lim 		; /* ToDo: add DDR mode settings also once available on SIMICS */
173ddaf02d1SJit Loon Lim 	} else {
174ddaf02d1SJit Loon Lim 		;
175ddaf02d1SJit Loon Lim 	}
176ddaf02d1SJit Loon Lim 
177ddaf02d1SJit Loon Lim 	reg |= (1 << CNF_DLL_PHY_RST_N);
178ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_MINICTRL(DLL_PHY_CTRL), reg);
179ddaf02d1SJit Loon Lim }
180ddaf02d1SJit Loon Lim 
181ddaf02d1SJit Loon Lim /* Data transfer configuration */
182ddaf02d1SJit Loon Lim static void cdns_nand_transfer_config(void)
183ddaf02d1SJit Loon Lim {
184ddaf02d1SJit Loon Lim 	/* Wait for controller to be in idle state */
185ddaf02d1SJit Loon Lim 	cdns_nand_wait_idle();
186ddaf02d1SJit Loon Lim 
187ddaf02d1SJit Loon Lim 	/* Configure data transfer parameters */
188ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CTRLCFG(TRANS_CFG0), 1);
189ddaf02d1SJit Loon Lim 
190ddaf02d1SJit Loon Lim 	/* ECC is disabled */
191ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CTRLCFG(ECC_CFG0), 0);
192ddaf02d1SJit Loon Lim 
193ddaf02d1SJit Loon Lim 	/* DMA burst select */
194ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CTRLCFG(DMA_SETTINGS),
195ddaf02d1SJit Loon Lim 			(CNF_DMA_BURST_SIZE_MAX << CNF_DMA_SETTINGS_BURST) |
196ddaf02d1SJit Loon Lim 			(1 << CNF_DMA_SETTINGS_OTE));
197ddaf02d1SJit Loon Lim 
198ddaf02d1SJit Loon Lim 	/* Enable pre-fetching for 1K */
199ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CTRLCFG(FIFO_TLEVEL),
200ddaf02d1SJit Loon Lim 			(CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_POS) |
201ddaf02d1SJit Loon Lim 			(CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_DMA_SIZE));
202ddaf02d1SJit Loon Lim 
203ddaf02d1SJit Loon Lim 	/* Select access type */
204ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CTRLCFG(MULTIPLANE_CFG), 0);
205ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CTRLCFG(CACHE_CFG), 0);
206ddaf02d1SJit Loon Lim }
207ddaf02d1SJit Loon Lim 
208ddaf02d1SJit Loon Lim /* Update the nand flash device info */
209ddaf02d1SJit Loon Lim static int cdns_nand_update_dev_info(void)
210ddaf02d1SJit Loon Lim {
211ddaf02d1SJit Loon Lim 	uint32_t reg = 0U;
212ddaf02d1SJit Loon Lim 
213ddaf02d1SJit Loon Lim 	/* Read the device type and number of LUNs */
214ddaf02d1SJit Loon Lim 	reg = mmio_read_32(CNF_CTRLPARAM(DEV_PARAMS0));
215ddaf02d1SJit Loon Lim 	dev_info.type = CNF_GET_DEV_TYPE(reg);
216ddaf02d1SJit Loon Lim 	if (dev_info.type == CNF_DT_UNKNOWN) {
217ddaf02d1SJit Loon Lim 		ERROR("%s: device type unknown\n", __func__);
218ddaf02d1SJit Loon Lim 		return -ENXIO;
219ddaf02d1SJit Loon Lim 	}
220ddaf02d1SJit Loon Lim 	dev_info.nluns = CNF_GET_NLUNS(reg);
221ddaf02d1SJit Loon Lim 
222ddaf02d1SJit Loon Lim 	/* Pages per block */
223ddaf02d1SJit Loon Lim 	reg = mmio_read_32(CNF_CTRLCFG(DEV_LAYOUT));
224ddaf02d1SJit Loon Lim 	dev_info.npages_per_block = CNF_GET_NPAGES_PER_BLOCK(reg);
225ddaf02d1SJit Loon Lim 
226ddaf02d1SJit Loon Lim 	/* Sector size and last sector size */
227ddaf02d1SJit Loon Lim 	reg = mmio_read_32(CNF_CTRLCFG(TRANS_CFG1));
228ddaf02d1SJit Loon Lim 	dev_info.sector_size = CNF_GET_SCTR_SIZE(reg);
229ddaf02d1SJit Loon Lim 	dev_info.last_sector_size = CNF_GET_LAST_SCTR_SIZE(reg);
230ddaf02d1SJit Loon Lim 
231ddaf02d1SJit Loon Lim 	/* Page size and spare size */
232ddaf02d1SJit Loon Lim 	reg = mmio_read_32(CNF_CTRLPARAM(DEV_AREA));
233ddaf02d1SJit Loon Lim 	dev_info.page_size = CNF_GET_PAGE_SIZE(reg);
234ddaf02d1SJit Loon Lim 	dev_info.spare_size = CNF_GET_SPARE_SIZE(reg);
235ddaf02d1SJit Loon Lim 
236ddaf02d1SJit Loon Lim 	/* Device blocks per LUN */
237ddaf02d1SJit Loon Lim 	dev_info.nblocks_per_lun = mmio_read_32(CNF_CTRLPARAM(DEV_BLOCKS_PLUN));
238ddaf02d1SJit Loon Lim 
239ddaf02d1SJit Loon Lim 	/* Calculate block size and total device size */
240ddaf02d1SJit Loon Lim 	dev_info.block_size = (dev_info.npages_per_block * dev_info.page_size);
241*a773f412SGirisha Dengi 	dev_info.total_size = ((unsigned long long)dev_info.block_size *
242*a773f412SGirisha Dengi 				(unsigned long long)dev_info.nblocks_per_lun *
243ddaf02d1SJit Loon Lim 				dev_info.nluns);
244ddaf02d1SJit Loon Lim 
245*a773f412SGirisha Dengi 	VERBOSE("CNF params: page_size %d, spare_size %d, block_size %u, total_size %llu\n",
246ddaf02d1SJit Loon Lim 		dev_info.page_size, dev_info.spare_size,
247ddaf02d1SJit Loon Lim 		dev_info.block_size, dev_info.total_size);
248ddaf02d1SJit Loon Lim 
249ddaf02d1SJit Loon Lim 	return 0;
250ddaf02d1SJit Loon Lim }
251ddaf02d1SJit Loon Lim 
252ddaf02d1SJit Loon Lim /* NAND Flash Controller/Host initialization */
253ddaf02d1SJit Loon Lim int cdns_nand_host_init(void)
254ddaf02d1SJit Loon Lim {
255ddaf02d1SJit Loon Lim 	uint32_t reg = 0U;
256ddaf02d1SJit Loon Lim 	int ret = 0;
257ddaf02d1SJit Loon Lim 
258ddaf02d1SJit Loon Lim 	do {
259ddaf02d1SJit Loon Lim 		/* Read controller status register for init complete */
260ddaf02d1SJit Loon Lim 		reg = mmio_read_32(CNF_CMDREG(CTRL_STATUS));
261ddaf02d1SJit Loon Lim 	} while (CNF_GET_INIT_COMP(reg) == 0);
262ddaf02d1SJit Loon Lim 
263ddaf02d1SJit Loon Lim 	ret = cdns_nand_update_dev_info();
264ddaf02d1SJit Loon Lim 	if (ret != 0) {
265ddaf02d1SJit Loon Lim 		return ret;
266ddaf02d1SJit Loon Lim 	}
267ddaf02d1SJit Loon Lim 
268ddaf02d1SJit Loon Lim 	INFO("CNF: device discovery process completed and device type %d\n",
269ddaf02d1SJit Loon Lim 			dev_info.type);
270ddaf02d1SJit Loon Lim 
271ddaf02d1SJit Loon Lim 	/* Enable data integrity, enable CRC and parity */
272ddaf02d1SJit Loon Lim 	reg = mmio_read_32(CNF_DI(CONTROL));
273ddaf02d1SJit Loon Lim 	reg |= (1 << CNF_DI_PAR_EN);
274ddaf02d1SJit Loon Lim 	reg |= (1 << CNF_DI_CRC_EN);
275ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_DI(CONTROL), reg);
276ddaf02d1SJit Loon Lim 
277ddaf02d1SJit Loon Lim 	/* Status polling mode, device control and status register */
278ddaf02d1SJit Loon Lim 	cdns_nand_wait_idle();
279ddaf02d1SJit Loon Lim 	reg = mmio_read_32(CNF_CTRLCFG(DEV_STAT));
280ddaf02d1SJit Loon Lim 	reg = reg & ~1;
281ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CTRLCFG(DEV_STAT), reg);
282ddaf02d1SJit Loon Lim 
283ddaf02d1SJit Loon Lim 	/* Set operation work mode */
284ddaf02d1SJit Loon Lim 	cdns_nand_set_opr_mode(CNF_OPR_WORK_MODE_SDR);
285ddaf02d1SJit Loon Lim 
286ddaf02d1SJit Loon Lim 	/* Set data transfer configuration parameters */
287ddaf02d1SJit Loon Lim 	cdns_nand_transfer_config();
288ddaf02d1SJit Loon Lim 
289ddaf02d1SJit Loon Lim 	return 0;
290ddaf02d1SJit Loon Lim }
291ddaf02d1SJit Loon Lim 
292ddaf02d1SJit Loon Lim /* erase: Block erase command */
293ddaf02d1SJit Loon Lim int cdns_nand_erase(uint32_t offset, uint32_t size)
294ddaf02d1SJit Loon Lim {
295ddaf02d1SJit Loon Lim 	/* Determine the starting block offset i.e row address */
296ddaf02d1SJit Loon Lim 	uint32_t row_address = dev_info.npages_per_block * offset;
297ddaf02d1SJit Loon Lim 
298ddaf02d1SJit Loon Lim 	/* Wait for thread to be in ready state */
299ddaf02d1SJit Loon Lim 	cdns_nand_wait_thread_ready(CNF_DEF_TRD);
300ddaf02d1SJit Loon Lim 
301ddaf02d1SJit Loon Lim 	/*Set row address */
302ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG1), row_address);
303ddaf02d1SJit Loon Lim 
304ddaf02d1SJit Loon Lim 	/* Operation bank number */
305ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG4), (CNF_DEF_DEVICE << CNF_CMDREG4_MEM));
306ddaf02d1SJit Loon Lim 
307ddaf02d1SJit Loon Lim 	/* Block erase command */
308ddaf02d1SJit Loon Lim 	uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT);
309ddaf02d1SJit Loon Lim 
310ddaf02d1SJit Loon Lim 	reg |= (CNF_DEF_TRD << CNF_CMDREG0_TRD);
311ddaf02d1SJit Loon Lim 	reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL);
312ddaf02d1SJit Loon Lim 	reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR);
313ddaf02d1SJit Loon Lim 	reg |= (CNF_CT_ERASE << CNF_CMDREG0_CMD);
314ddaf02d1SJit Loon Lim 	reg |= (((size-1) & 0xFF) << CNF_CMDREG0_CMD);
315ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG0), reg);
316ddaf02d1SJit Loon Lim 
317ddaf02d1SJit Loon Lim 	/* Wait for erase operation to complete */
318ddaf02d1SJit Loon Lim 	return cdns_nand_last_opr_status(CNF_DEF_TRD);
319ddaf02d1SJit Loon Lim }
320ddaf02d1SJit Loon Lim 
321ddaf02d1SJit Loon Lim /* io mtd functions */
322ddaf02d1SJit Loon Lim int cdns_nand_init_mtd(unsigned long long *size, unsigned int *erase_size)
323ddaf02d1SJit Loon Lim {
324ddaf02d1SJit Loon Lim 	*size = dev_info.total_size;
325ddaf02d1SJit Loon Lim 	*erase_size = dev_info.block_size;
326ddaf02d1SJit Loon Lim 
327ddaf02d1SJit Loon Lim 	return 0;
328ddaf02d1SJit Loon Lim }
329ddaf02d1SJit Loon Lim 
330*a773f412SGirisha Dengi static uint32_t cdns_nand_get_row_address(uint32_t page, uint32_t block)
331*a773f412SGirisha Dengi {
332*a773f412SGirisha Dengi 	uint32_t row_address = 0U;
333*a773f412SGirisha Dengi 	uint32_t req_bits = 0U;
334*a773f412SGirisha Dengi 
335*a773f412SGirisha Dengi 	/* The device info is not populated yet. */
336*a773f412SGirisha Dengi 	if (dev_info.npages_per_block == 0U)
337*a773f412SGirisha Dengi 		return 0;
338*a773f412SGirisha Dengi 
339*a773f412SGirisha Dengi 	for (uint32_t i = 0U; i < sizeof(uint32_t) * 8; i++) {
340*a773f412SGirisha Dengi 		if ((1U << i) & dev_info.npages_per_block)
341*a773f412SGirisha Dengi 			req_bits = i;
342*a773f412SGirisha Dengi 	}
343*a773f412SGirisha Dengi 
344*a773f412SGirisha Dengi 	row_address = ((page & GENMASK_32((req_bits - 1), 0)) |
345*a773f412SGirisha Dengi 			(block << req_bits));
346*a773f412SGirisha Dengi 
347*a773f412SGirisha Dengi 	return row_address;
348*a773f412SGirisha Dengi }
349*a773f412SGirisha Dengi 
350ddaf02d1SJit Loon Lim /* NAND Flash page read */
351ddaf02d1SJit Loon Lim static int cdns_nand_read_page(uint32_t block, uint32_t page, uintptr_t buffer)
352ddaf02d1SJit Loon Lim {
353*a773f412SGirisha Dengi 
354ddaf02d1SJit Loon Lim 	/* Wait for thread to be ready */
355ddaf02d1SJit Loon Lim 	cdns_nand_wait_thread_ready(CNF_DEF_TRD);
356ddaf02d1SJit Loon Lim 
357ddaf02d1SJit Loon Lim 	/* Select device */
358ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG4),
359ddaf02d1SJit Loon Lim 			(CNF_DEF_DEVICE << CNF_CMDREG4_MEM));
360ddaf02d1SJit Loon Lim 
361ddaf02d1SJit Loon Lim 	/* Set host memory address for DMA transfers */
362*a773f412SGirisha Dengi 	mmio_write_32(CNF_CMDREG(CMD_REG2), (buffer & UINT32_MAX));
363*a773f412SGirisha Dengi 	mmio_write_32(CNF_CMDREG(CMD_REG3), ((buffer >> 32) & UINT32_MAX));
364ddaf02d1SJit Loon Lim 
365ddaf02d1SJit Loon Lim 	/* Set row address */
366*a773f412SGirisha Dengi 	mmio_write_32(CNF_CMDREG(CMD_REG1),
367*a773f412SGirisha Dengi 			cdns_nand_get_row_address(page, block));
368ddaf02d1SJit Loon Lim 
369ddaf02d1SJit Loon Lim 	/* Page read command */
370ddaf02d1SJit Loon Lim 	uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT);
371ddaf02d1SJit Loon Lim 
372ddaf02d1SJit Loon Lim 	reg |= (CNF_DEF_TRD << CNF_CMDREG0_TRD);
373ddaf02d1SJit Loon Lim 	reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL);
374ddaf02d1SJit Loon Lim 	reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR);
375ddaf02d1SJit Loon Lim 	reg |= (CNF_DMA_MASTER_SEL << CNF_CMDREG0_DMA);
376ddaf02d1SJit Loon Lim 	reg |= (CNF_CT_PAGE_READ << CNF_CMDREG0_CMD);
377ddaf02d1SJit Loon Lim 	reg |= (((CNF_READ_SINGLE_PAGE-1) & 0xFF) << CNF_CMDREG0_CMD);
378ddaf02d1SJit Loon Lim 	mmio_write_32(CNF_CMDREG(CMD_REG0), reg);
379ddaf02d1SJit Loon Lim 
380ddaf02d1SJit Loon Lim 	/* Wait for read operation to complete */
381ddaf02d1SJit Loon Lim 	if (cdns_nand_last_opr_status(CNF_DEF_TRD)) {
382ddaf02d1SJit Loon Lim 		ERROR("%s: Page read failed\n", __func__);
383ddaf02d1SJit Loon Lim 		return -EIO;
384ddaf02d1SJit Loon Lim 	}
385ddaf02d1SJit Loon Lim 
386ddaf02d1SJit Loon Lim 	return 0;
387ddaf02d1SJit Loon Lim }
388ddaf02d1SJit Loon Lim 
389ddaf02d1SJit Loon Lim int cdns_nand_read(unsigned int offset, uintptr_t buffer, size_t length,
390ddaf02d1SJit Loon Lim 					size_t *out_length)
391ddaf02d1SJit Loon Lim {
392ddaf02d1SJit Loon Lim 	uint32_t block = offset / dev_info.block_size;
393ddaf02d1SJit Loon Lim 	uint32_t end_block = (offset + length - 1U) / dev_info.block_size;
394ddaf02d1SJit Loon Lim 	uint32_t page_start = (offset % dev_info.block_size) / dev_info.page_size;
395ddaf02d1SJit Loon Lim 	uint32_t start_offset = offset % dev_info.page_size;
396ddaf02d1SJit Loon Lim 	uint32_t nb_pages = dev_info.block_size / dev_info.page_size;
397ddaf02d1SJit Loon Lim 	uint32_t bytes_read = 0U;
398ddaf02d1SJit Loon Lim 	uint32_t page = 0U;
399ddaf02d1SJit Loon Lim 	int result = 0;
400ddaf02d1SJit Loon Lim 
401*a773f412SGirisha Dengi 	INFO("CNF: %s: block %u-%u, page_start %u, len %zu, offset %u\n",
402*a773f412SGirisha Dengi 		__func__, block, end_block, page_start, length, offset);
403ddaf02d1SJit Loon Lim 
404ddaf02d1SJit Loon Lim 	if ((offset >= dev_info.total_size) ||
405ddaf02d1SJit Loon Lim 		(offset + length-1 >= dev_info.total_size) ||
406ddaf02d1SJit Loon Lim 		(length == 0U)) {
407ddaf02d1SJit Loon Lim 		ERROR("CNF: Invalid read parameters\n");
408ddaf02d1SJit Loon Lim 		return -EINVAL;
409ddaf02d1SJit Loon Lim 	}
410ddaf02d1SJit Loon Lim 
411ddaf02d1SJit Loon Lim 	*out_length = 0UL;
412ddaf02d1SJit Loon Lim 
413ddaf02d1SJit Loon Lim 	while (block <= end_block) {
414ddaf02d1SJit Loon Lim 		for (page = page_start; page < nb_pages; page++) {
415ddaf02d1SJit Loon Lim 			if ((start_offset != 0U) || (length < dev_info.page_size)) {
416ddaf02d1SJit Loon Lim 				/* Partial page read */
417ddaf02d1SJit Loon Lim 				result = cdns_nand_read_page(block, page,
418ddaf02d1SJit Loon Lim 							(uintptr_t)scratch_buff);
419ddaf02d1SJit Loon Lim 				if (result != 0) {
420ddaf02d1SJit Loon Lim 					return result;
421ddaf02d1SJit Loon Lim 				}
422ddaf02d1SJit Loon Lim 
423ddaf02d1SJit Loon Lim 				bytes_read = MIN((size_t)(dev_info.page_size - start_offset),
424ddaf02d1SJit Loon Lim 								length);
425ddaf02d1SJit Loon Lim 
426ddaf02d1SJit Loon Lim 				memcpy((uint8_t *)buffer, scratch_buff + start_offset,
427ddaf02d1SJit Loon Lim 						bytes_read);
428ddaf02d1SJit Loon Lim 				start_offset = 0U;
429ddaf02d1SJit Loon Lim 			} else {
430ddaf02d1SJit Loon Lim 				/* Full page read */
431ddaf02d1SJit Loon Lim 				result = cdns_nand_read_page(block, page,
432ddaf02d1SJit Loon Lim 				(uintptr_t)scratch_buff);
433ddaf02d1SJit Loon Lim 				if (result != 0) {
434ddaf02d1SJit Loon Lim 					return result;
435ddaf02d1SJit Loon Lim 				}
436ddaf02d1SJit Loon Lim 
437ddaf02d1SJit Loon Lim 				bytes_read = dev_info.page_size;
438ddaf02d1SJit Loon Lim 				memcpy((uint8_t *)buffer, scratch_buff, bytes_read);
439ddaf02d1SJit Loon Lim 			}
440ddaf02d1SJit Loon Lim 
441ddaf02d1SJit Loon Lim 			length -= bytes_read;
442ddaf02d1SJit Loon Lim 			buffer += bytes_read;
443ddaf02d1SJit Loon Lim 			*out_length += bytes_read;
444ddaf02d1SJit Loon Lim 
445ddaf02d1SJit Loon Lim 			/* All the bytes have read */
446ddaf02d1SJit Loon Lim 			if (length == 0U) {
447ddaf02d1SJit Loon Lim 				break;
448ddaf02d1SJit Loon Lim 			}
449ddaf02d1SJit Loon Lim 
450ddaf02d1SJit Loon Lim 			udelay(CNF_READ_INT_DELAY_US);
451ddaf02d1SJit Loon Lim 		} /* for */
452ddaf02d1SJit Loon Lim 
453ddaf02d1SJit Loon Lim 		page_start = 0U;
454ddaf02d1SJit Loon Lim 		block++;
455ddaf02d1SJit Loon Lim 	} /* while */
456ddaf02d1SJit Loon Lim 
457ddaf02d1SJit Loon Lim 	return 0;
458ddaf02d1SJit Loon Lim }
459