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