xref: /arm-trusted-firmware/drivers/nxp/ifc/nand/ifc_nand.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright 2022 NXP
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <string.h>
8*91f16700Schasinglulu 
9*91f16700Schasinglulu #include <common/debug.h>
10*91f16700Schasinglulu #include <drivers/io/io_block.h>
11*91f16700Schasinglulu #include "ifc.h"
12*91f16700Schasinglulu #include <lib/xlat_tables/xlat_tables_v2.h>
13*91f16700Schasinglulu #include <nxp_timer.h>
14*91f16700Schasinglulu 
15*91f16700Schasinglulu /* Private structure for NAND driver data */
16*91f16700Schasinglulu static struct nand_info nand_drv_data;
17*91f16700Schasinglulu 
18*91f16700Schasinglulu static int update_bbt(uint32_t idx, uint32_t blk, uint32_t *updated,
19*91f16700Schasinglulu 		struct nand_info *nand);
20*91f16700Schasinglulu 
21*91f16700Schasinglulu static int nand_wait(struct nand_info *nand)
22*91f16700Schasinglulu {
23*91f16700Schasinglulu 	int timeout = 1;
24*91f16700Schasinglulu 	uint32_t  neesr;
25*91f16700Schasinglulu 	unsigned long start_time;
26*91f16700Schasinglulu 
27*91f16700Schasinglulu 	start_time = get_timer_val(0);
28*91f16700Schasinglulu 
29*91f16700Schasinglulu 	while (get_timer_val(start_time) < NAND_TIMEOUT_MS) {
30*91f16700Schasinglulu 		/* clear the OPC event */
31*91f16700Schasinglulu 		neesr = read_reg(nand, NAND_EVTER_STAT);
32*91f16700Schasinglulu 		if (neesr & NAND_EVTER_STAT_OPC_DN) {
33*91f16700Schasinglulu 			write_reg(nand, NAND_EVTER_STAT, neesr);
34*91f16700Schasinglulu 			timeout = 0;
35*91f16700Schasinglulu 
36*91f16700Schasinglulu 			/* check for other errors */
37*91f16700Schasinglulu 			if (neesr & NAND_EVTER_STAT_FTOER) {
38*91f16700Schasinglulu 				ERROR("%s NAND_EVTER_STAT_FTOER occurs\n",
39*91f16700Schasinglulu 						__func__);
40*91f16700Schasinglulu 				return -1;
41*91f16700Schasinglulu 			} else if (neesr & NAND_EVTER_STAT_ECCER) {
42*91f16700Schasinglulu 				ERROR("%s NAND_EVTER_STAT_ECCER occurs\n",
43*91f16700Schasinglulu 						__func__);
44*91f16700Schasinglulu 				return -1;
45*91f16700Schasinglulu 			} else if (neesr & NAND_EVTER_STAT_DQSER) {
46*91f16700Schasinglulu 				ERROR("%s NAND_EVTER_STAT_DQSER occurs\n",
47*91f16700Schasinglulu 						__func__);
48*91f16700Schasinglulu 				return -1;
49*91f16700Schasinglulu 			}
50*91f16700Schasinglulu 
51*91f16700Schasinglulu 			break;
52*91f16700Schasinglulu 		}
53*91f16700Schasinglulu 	}
54*91f16700Schasinglulu 
55*91f16700Schasinglulu 	if (timeout) {
56*91f16700Schasinglulu 		ERROR("%s ERROR_NAND_TIMEOUT occurs\n", __func__);
57*91f16700Schasinglulu 		return -1;
58*91f16700Schasinglulu 	}
59*91f16700Schasinglulu 
60*91f16700Schasinglulu 	return 0;
61*91f16700Schasinglulu }
62*91f16700Schasinglulu 
63*91f16700Schasinglulu static uint32_t nand_get_port_size(struct nand_info *nand)
64*91f16700Schasinglulu {
65*91f16700Schasinglulu 	uint32_t port_size = U(0);
66*91f16700Schasinglulu 	uint32_t cs_reg;
67*91f16700Schasinglulu 	uint32_t cur_cs;
68*91f16700Schasinglulu 
69*91f16700Schasinglulu 	cur_cs = U(0);
70*91f16700Schasinglulu 	cs_reg = CSPR(cur_cs);
71*91f16700Schasinglulu 	port_size = (read_reg(nand, cs_reg) & CSPR_PS) >> CSPR_PS_SHIFT;
72*91f16700Schasinglulu 	switch (port_size) {
73*91f16700Schasinglulu 	case CSPR_PS_8:
74*91f16700Schasinglulu 		port_size = U(8);
75*91f16700Schasinglulu 		break;
76*91f16700Schasinglulu 	case CSPR_PS_16:
77*91f16700Schasinglulu 		port_size = U(16);
78*91f16700Schasinglulu 		break;
79*91f16700Schasinglulu 	case CSPR_PS_32:
80*91f16700Schasinglulu 		port_size = U(32);
81*91f16700Schasinglulu 		break;
82*91f16700Schasinglulu 	default:
83*91f16700Schasinglulu 		port_size = U(8);
84*91f16700Schasinglulu 	}
85*91f16700Schasinglulu 
86*91f16700Schasinglulu 	return port_size;
87*91f16700Schasinglulu }
88*91f16700Schasinglulu 
89*91f16700Schasinglulu static uint32_t nand_get_page_size(struct nand_info *nand)
90*91f16700Schasinglulu {
91*91f16700Schasinglulu 	uint32_t pg_size;
92*91f16700Schasinglulu 	uint32_t cs_reg;
93*91f16700Schasinglulu 	uint32_t cur_cs;
94*91f16700Schasinglulu 
95*91f16700Schasinglulu 	cur_cs = 0;
96*91f16700Schasinglulu 	cs_reg = CSOR(cur_cs);
97*91f16700Schasinglulu 	pg_size = read_reg(nand, cs_reg) & CSOR_NAND_PGS;
98*91f16700Schasinglulu 	switch (pg_size) {
99*91f16700Schasinglulu 	case CSOR_NAND_PGS_2K:
100*91f16700Schasinglulu 		pg_size = U(2048);
101*91f16700Schasinglulu 		break;
102*91f16700Schasinglulu 	case CSOR_NAND_PGS_4K:
103*91f16700Schasinglulu 		pg_size = U(4096);
104*91f16700Schasinglulu 		break;
105*91f16700Schasinglulu 	case CSOR_NAND_PGS_8K:
106*91f16700Schasinglulu 		pg_size = U(8192);
107*91f16700Schasinglulu 		break;
108*91f16700Schasinglulu 	case CSOR_NAND_PGS_16K:
109*91f16700Schasinglulu 		pg_size = U(16384);
110*91f16700Schasinglulu 		break;
111*91f16700Schasinglulu 	default:
112*91f16700Schasinglulu 		pg_size = U(512);
113*91f16700Schasinglulu 	}
114*91f16700Schasinglulu 
115*91f16700Schasinglulu 	return pg_size;
116*91f16700Schasinglulu }
117*91f16700Schasinglulu 
118*91f16700Schasinglulu static uint32_t nand_get_pages_per_blk(struct nand_info *nand)
119*91f16700Schasinglulu {
120*91f16700Schasinglulu 	uint32_t pages_per_blk;
121*91f16700Schasinglulu 	uint32_t cs_reg;
122*91f16700Schasinglulu 	uint32_t cur_cs;
123*91f16700Schasinglulu 
124*91f16700Schasinglulu 	cur_cs = 0;
125*91f16700Schasinglulu 	cs_reg = CSOR(cur_cs);
126*91f16700Schasinglulu 	pages_per_blk = (read_reg(nand, cs_reg) & CSOR_NAND_PB);
127*91f16700Schasinglulu 	switch (pages_per_blk) {
128*91f16700Schasinglulu 	case CSOR_NAND_PB_32:
129*91f16700Schasinglulu 		pages_per_blk = U(32);
130*91f16700Schasinglulu 		break;
131*91f16700Schasinglulu 	case CSOR_NAND_PB_64:
132*91f16700Schasinglulu 		pages_per_blk = U(64);
133*91f16700Schasinglulu 		break;
134*91f16700Schasinglulu 	case CSOR_NAND_PB_128:
135*91f16700Schasinglulu 		pages_per_blk = U(128);
136*91f16700Schasinglulu 		break;
137*91f16700Schasinglulu 	case CSOR_NAND_PB_256:
138*91f16700Schasinglulu 		pages_per_blk = U(256);
139*91f16700Schasinglulu 		break;
140*91f16700Schasinglulu 	case CSOR_NAND_PB_512:
141*91f16700Schasinglulu 		pages_per_blk = U(512);
142*91f16700Schasinglulu 		break;
143*91f16700Schasinglulu 	case CSOR_NAND_PB_1024:
144*91f16700Schasinglulu 		pages_per_blk = U(1024);
145*91f16700Schasinglulu 		break;
146*91f16700Schasinglulu 	case CSOR_NAND_PB_2048:
147*91f16700Schasinglulu 		pages_per_blk = U(2048);
148*91f16700Schasinglulu 		break;
149*91f16700Schasinglulu 	default:
150*91f16700Schasinglulu 		pages_per_blk = U(0);
151*91f16700Schasinglulu 	}
152*91f16700Schasinglulu 
153*91f16700Schasinglulu 	return pages_per_blk;
154*91f16700Schasinglulu }
155*91f16700Schasinglulu 
156*91f16700Schasinglulu static uint32_t get_page_index_width(uint32_t ppb)
157*91f16700Schasinglulu {
158*91f16700Schasinglulu 	switch (ppb) {
159*91f16700Schasinglulu 	case CSOR_NAND_PPB_32:
160*91f16700Schasinglulu 		return U(5);
161*91f16700Schasinglulu 	case CSOR_NAND_PPB_64:
162*91f16700Schasinglulu 		return U(6);
163*91f16700Schasinglulu 	case CSOR_NAND_PPB_128:
164*91f16700Schasinglulu 		return U(7);
165*91f16700Schasinglulu 	case CSOR_NAND_PPB_256:
166*91f16700Schasinglulu 		return U(8);
167*91f16700Schasinglulu 	case CSOR_NAND_PPB_512:
168*91f16700Schasinglulu 		return U(9);
169*91f16700Schasinglulu 	case CSOR_NAND_PPB_1024:
170*91f16700Schasinglulu 		return U(10);
171*91f16700Schasinglulu 	case CSOR_NAND_PPB_2048:
172*91f16700Schasinglulu 		return U(11);
173*91f16700Schasinglulu 	default:
174*91f16700Schasinglulu 		return U(5);
175*91f16700Schasinglulu 	}
176*91f16700Schasinglulu }
177*91f16700Schasinglulu 
178*91f16700Schasinglulu static void nand_get_params(struct nand_info *nand)
179*91f16700Schasinglulu {
180*91f16700Schasinglulu 	nand->port_size = nand_get_port_size(nand);
181*91f16700Schasinglulu 
182*91f16700Schasinglulu 	nand->page_size = nand_get_page_size(nand);
183*91f16700Schasinglulu 
184*91f16700Schasinglulu 	/*
185*91f16700Schasinglulu 	 * Set Bad marker Location for LP / SP
186*91f16700Schasinglulu 	 * Small Page : 8 Bit	 : 0x5
187*91f16700Schasinglulu 	 * Small Page : 16 Bit	: 0xa
188*91f16700Schasinglulu 	 * Large Page : 8 /16 Bit : 0x0
189*91f16700Schasinglulu 	 */
190*91f16700Schasinglulu 	nand->bad_marker_loc = (nand->page_size == 512) ?
191*91f16700Schasinglulu 				((nand->port_size == 8) ? 0x5 : 0xa) : 0;
192*91f16700Schasinglulu 
193*91f16700Schasinglulu 	/* check for the device is ONFI compliant or not */
194*91f16700Schasinglulu 	nand->onfi_dev_flag =
195*91f16700Schasinglulu 	   (read_reg(nand, NAND_EVTER_STAT) & NAND_EVTER_STAT_BBI_SRCH_SEL)
196*91f16700Schasinglulu 	   ? 1 : 0;
197*91f16700Schasinglulu 
198*91f16700Schasinglulu 	/* NAND Blk serached count for incremental Bad block search cnt */
199*91f16700Schasinglulu 	nand->bbs = 0;
200*91f16700Schasinglulu 
201*91f16700Schasinglulu 	/* pages per Block */
202*91f16700Schasinglulu 	nand->ppb = nand_get_pages_per_blk(nand);
203*91f16700Schasinglulu 
204*91f16700Schasinglulu 	/* Blk size */
205*91f16700Schasinglulu 	nand->blk_size = nand->page_size * nand->ppb;
206*91f16700Schasinglulu 
207*91f16700Schasinglulu 	/* get_page_index_width */
208*91f16700Schasinglulu 	nand->pi_width = get_page_index_width(nand->ppb);
209*91f16700Schasinglulu 
210*91f16700Schasinglulu 	/* bad block table init */
211*91f16700Schasinglulu 	nand->lgb = 0;
212*91f16700Schasinglulu 	nand->bbt_max = 0;
213*91f16700Schasinglulu 	nand->bzero_good = 0;
214*91f16700Schasinglulu 	memset(nand->bbt, EMPTY_VAL, BBT_SIZE * sizeof(nand->bbt[0]));
215*91f16700Schasinglulu }
216*91f16700Schasinglulu 
217*91f16700Schasinglulu static int nand_init(struct nand_info *nand)
218*91f16700Schasinglulu {
219*91f16700Schasinglulu 	uint32_t ncfgr = 0;
220*91f16700Schasinglulu 
221*91f16700Schasinglulu 	/* Get nand Parameters from IFC */
222*91f16700Schasinglulu 	nand_get_params(nand);
223*91f16700Schasinglulu 
224*91f16700Schasinglulu 	/* Clear all errors */
225*91f16700Schasinglulu 	write_reg(nand, NAND_EVTER_STAT, U(0xffffffff));
226*91f16700Schasinglulu 
227*91f16700Schasinglulu 	/*
228*91f16700Schasinglulu 	 * Disable autoboot in NCFGR. Mapping will change from
229*91f16700Schasinglulu 	 * physical to logical for SRAM buffer
230*91f16700Schasinglulu 	 */
231*91f16700Schasinglulu 	ncfgr = read_reg(nand, NCFGR);
232*91f16700Schasinglulu 	write_reg(nand, NCFGR, (ncfgr & ~NCFGR_BOOT));
233*91f16700Schasinglulu 
234*91f16700Schasinglulu 	return 0;
235*91f16700Schasinglulu }
236*91f16700Schasinglulu 
237*91f16700Schasinglulu static int nand_read_data(
238*91f16700Schasinglulu 		uintptr_t ifc_region_addr,
239*91f16700Schasinglulu 		uint32_t row_add,
240*91f16700Schasinglulu 		uint32_t col_add,
241*91f16700Schasinglulu 		uint32_t byte_cnt,
242*91f16700Schasinglulu 		uint8_t *data,
243*91f16700Schasinglulu 		uint32_t main_spare,
244*91f16700Schasinglulu 		struct nand_info *nand)
245*91f16700Schasinglulu {
246*91f16700Schasinglulu 	uint32_t page_size_add_bits = U(0);
247*91f16700Schasinglulu 	uint32_t page_add_in_actual, page_add;
248*91f16700Schasinglulu 	uintptr_t sram_addr_calc;
249*91f16700Schasinglulu 	int ret;
250*91f16700Schasinglulu 	uint32_t col_val;
251*91f16700Schasinglulu 
252*91f16700Schasinglulu 	/* Programming MS bit to read from spare area.*/
253*91f16700Schasinglulu 	col_val = (main_spare << NAND_COL_MS_SHIFT) | col_add;
254*91f16700Schasinglulu 
255*91f16700Schasinglulu 	write_reg(nand, NAND_BC, byte_cnt);
256*91f16700Schasinglulu 
257*91f16700Schasinglulu 	write_reg(nand, ROW0, row_add);
258*91f16700Schasinglulu 	write_reg(nand, COL0, col_val);
259*91f16700Schasinglulu 
260*91f16700Schasinglulu 	/* Program FCR for small Page */
261*91f16700Schasinglulu 	if (nand->page_size == U(512)) {
262*91f16700Schasinglulu 		if (byte_cnt == 0 ||
263*91f16700Schasinglulu 			(byte_cnt != 0  && main_spare == 0 && col_add <= 255)) {
264*91f16700Schasinglulu 			write_reg(nand, NAND_FCR0,
265*91f16700Schasinglulu 				  (NAND_CMD_READ0 << FCR_CMD0_SHIFT));
266*91f16700Schasinglulu 		} else if (main_spare == 0) {
267*91f16700Schasinglulu 			write_reg(nand, NAND_FCR0,
268*91f16700Schasinglulu 				  (NAND_CMD_READ1 << FCR_CMD0_SHIFT));
269*91f16700Schasinglulu 		} else {
270*91f16700Schasinglulu 			write_reg(nand, NAND_FCR0,
271*91f16700Schasinglulu 				  (NAND_CMD_READOOB << FCR_CMD0_SHIFT));
272*91f16700Schasinglulu 		}
273*91f16700Schasinglulu 
274*91f16700Schasinglulu 	} else {
275*91f16700Schasinglulu 		/* Program FCR for Large Page */
276*91f16700Schasinglulu 		write_reg(nand, NAND_FCR0, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
277*91f16700Schasinglulu 			  (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
278*91f16700Schasinglulu 	}
279*91f16700Schasinglulu 	if (nand->page_size == U(512)) {
280*91f16700Schasinglulu 		write_reg(nand, NAND_FIR0, ((FIR_OP_CW0 << FIR_OP0_SHIFT) |
281*91f16700Schasinglulu 					  (FIR_OP_CA0 << FIR_OP1_SHIFT) |
282*91f16700Schasinglulu 					  (FIR_OP_RA0 << FIR_OP2_SHIFT) |
283*91f16700Schasinglulu 					  (FIR_OP_BTRD << FIR_OP3_SHIFT) |
284*91f16700Schasinglulu 					  (FIR_OP_NOP << FIR_OP4_SHIFT)));
285*91f16700Schasinglulu 		write_reg(nand, NAND_FIR1, U(0x00000000));
286*91f16700Schasinglulu 	} else {
287*91f16700Schasinglulu 		write_reg(nand, NAND_FIR0, ((FIR_OP_CW0 << FIR_OP0_SHIFT) |
288*91f16700Schasinglulu 					 (FIR_OP_CA0 << FIR_OP1_SHIFT) |
289*91f16700Schasinglulu 					 (FIR_OP_RA0 << FIR_OP2_SHIFT) |
290*91f16700Schasinglulu 					 (FIR_OP_CMD1 << FIR_OP3_SHIFT) |
291*91f16700Schasinglulu 					 (FIR_OP_BTRD << FIR_OP4_SHIFT)));
292*91f16700Schasinglulu 
293*91f16700Schasinglulu 		write_reg(nand, NAND_FIR1, (FIR_OP_NOP << FIR_OP5_SHIFT));
294*91f16700Schasinglulu 	}
295*91f16700Schasinglulu 	write_reg(nand, NANDSEQ_STRT, NAND_SEQ_STRT_FIR_STRT);
296*91f16700Schasinglulu 
297*91f16700Schasinglulu 	ret = nand_wait(nand);
298*91f16700Schasinglulu 	if (ret != 0)
299*91f16700Schasinglulu 		return ret;
300*91f16700Schasinglulu 
301*91f16700Schasinglulu 	/* calculate page_size_add_bits i.e bits
302*91f16700Schasinglulu 	 * in sram address corresponding to area
303*91f16700Schasinglulu 	 * within a page for sram
304*91f16700Schasinglulu 	 */
305*91f16700Schasinglulu 	if (nand->page_size == U(512))
306*91f16700Schasinglulu 		page_size_add_bits = U(10);
307*91f16700Schasinglulu 	else if (nand->page_size == U(2048))
308*91f16700Schasinglulu 		page_size_add_bits = U(12);
309*91f16700Schasinglulu 	else if (nand->page_size == U(4096))
310*91f16700Schasinglulu 		page_size_add_bits = U(13);
311*91f16700Schasinglulu 	else if (nand->page_size == U(8192))
312*91f16700Schasinglulu 		page_size_add_bits = U(14);
313*91f16700Schasinglulu 	else if (nand->page_size == U(16384))
314*91f16700Schasinglulu 		page_size_add_bits = U(15);
315*91f16700Schasinglulu 
316*91f16700Schasinglulu 	page_add = row_add;
317*91f16700Schasinglulu 
318*91f16700Schasinglulu 	page_add_in_actual = (page_add << page_size_add_bits) & U(0x0000FFFF);
319*91f16700Schasinglulu 
320*91f16700Schasinglulu 	if (byte_cnt == 0)
321*91f16700Schasinglulu 		col_add = U(0);
322*91f16700Schasinglulu 
323*91f16700Schasinglulu 	/* Calculate SRAM address for main and spare area */
324*91f16700Schasinglulu 	if (main_spare == 0)
325*91f16700Schasinglulu 		sram_addr_calc = ifc_region_addr | page_add_in_actual | col_add;
326*91f16700Schasinglulu 	else
327*91f16700Schasinglulu 		sram_addr_calc = ifc_region_addr | page_add_in_actual |
328*91f16700Schasinglulu 				 (col_add + nand->page_size);
329*91f16700Schasinglulu 
330*91f16700Schasinglulu 	/* Depending Byte_count copy full page or partial page from SRAM */
331*91f16700Schasinglulu 	if (byte_cnt == 0)
332*91f16700Schasinglulu 		memcpy(data, (void *)sram_addr_calc,
333*91f16700Schasinglulu 			nand->page_size);
334*91f16700Schasinglulu 	else
335*91f16700Schasinglulu 		memcpy(data, (void *)sram_addr_calc, byte_cnt);
336*91f16700Schasinglulu 
337*91f16700Schasinglulu 	return 0;
338*91f16700Schasinglulu }
339*91f16700Schasinglulu 
340*91f16700Schasinglulu static int nand_read(struct nand_info *nand, int32_t src_addr,
341*91f16700Schasinglulu 		uintptr_t dst, uint32_t size)
342*91f16700Schasinglulu {
343*91f16700Schasinglulu 	uint32_t log_blk = U(0);
344*91f16700Schasinglulu 	uint32_t pg_no = U(0);
345*91f16700Schasinglulu 	uint32_t col_off = U(0);
346*91f16700Schasinglulu 	uint32_t row_off = U(0);
347*91f16700Schasinglulu 	uint32_t byte_cnt = U(0);
348*91f16700Schasinglulu 	uint32_t read_cnt = U(0);
349*91f16700Schasinglulu 	uint32_t i = U(0);
350*91f16700Schasinglulu 	uint32_t updated = U(0);
351*91f16700Schasinglulu 
352*91f16700Schasinglulu 	int ret = 0;
353*91f16700Schasinglulu 	uint8_t *out = (uint8_t *)dst;
354*91f16700Schasinglulu 
355*91f16700Schasinglulu 	uint32_t pblk;
356*91f16700Schasinglulu 
357*91f16700Schasinglulu 	/* loop till size */
358*91f16700Schasinglulu 	while (size) {
359*91f16700Schasinglulu 		log_blk = (src_addr / nand->blk_size);
360*91f16700Schasinglulu 		pg_no = ((src_addr - (log_blk * nand->blk_size)) /
361*91f16700Schasinglulu 					 nand->page_size);
362*91f16700Schasinglulu 		pblk = log_blk;
363*91f16700Schasinglulu 
364*91f16700Schasinglulu 		 // iterate the bbt to find the block
365*91f16700Schasinglulu 		for (i = 0; i <= nand->bbt_max; i++) {
366*91f16700Schasinglulu 			if (nand->bbt[i] == EMPTY_VAL_CHECK) {
367*91f16700Schasinglulu 				ret = update_bbt(i, pblk, &updated, nand);
368*91f16700Schasinglulu 
369*91f16700Schasinglulu 				if (ret != 0)
370*91f16700Schasinglulu 					return ret;
371*91f16700Schasinglulu 				 /*
372*91f16700Schasinglulu 				  * if table not updated and we reached
373*91f16700Schasinglulu 				  * end of table
374*91f16700Schasinglulu 				  */
375*91f16700Schasinglulu 				if (!updated)
376*91f16700Schasinglulu 					break;
377*91f16700Schasinglulu 			}
378*91f16700Schasinglulu 
379*91f16700Schasinglulu 			if (pblk < nand->bbt[i])
380*91f16700Schasinglulu 				break;
381*91f16700Schasinglulu 			else if (pblk >= nand->bbt[i])
382*91f16700Schasinglulu 				pblk++;
383*91f16700Schasinglulu 		}
384*91f16700Schasinglulu 
385*91f16700Schasinglulu 		col_off = (src_addr % nand->page_size);
386*91f16700Schasinglulu 		if (col_off) {
387*91f16700Schasinglulu 			if ((col_off + size) < nand->page_size)
388*91f16700Schasinglulu 				byte_cnt = size;
389*91f16700Schasinglulu 			else
390*91f16700Schasinglulu 				byte_cnt = nand->page_size - col_off;
391*91f16700Schasinglulu 
392*91f16700Schasinglulu 			row_off = (pblk << nand->pi_width) | pg_no;
393*91f16700Schasinglulu 
394*91f16700Schasinglulu 			ret = nand_read_data(
395*91f16700Schasinglulu 					nand->ifc_region_addr,
396*91f16700Schasinglulu 					row_off,
397*91f16700Schasinglulu 					col_off,
398*91f16700Schasinglulu 					byte_cnt, out, MAIN, nand);
399*91f16700Schasinglulu 
400*91f16700Schasinglulu 			if (ret != 0)
401*91f16700Schasinglulu 				return ret;
402*91f16700Schasinglulu 		} else {
403*91f16700Schasinglulu 			 /*
404*91f16700Schasinglulu 			  * fullpage/Partial Page
405*91f16700Schasinglulu 			  * if byte_cnt = 0 full page
406*91f16700Schasinglulu 			  * else partial page
407*91f16700Schasinglulu 			  */
408*91f16700Schasinglulu 			if (size < nand->page_size) {
409*91f16700Schasinglulu 				byte_cnt = size;
410*91f16700Schasinglulu 				read_cnt = size;
411*91f16700Schasinglulu 			} else	{
412*91f16700Schasinglulu 				byte_cnt = nand->page_size;
413*91f16700Schasinglulu 				read_cnt = 0;
414*91f16700Schasinglulu 			}
415*91f16700Schasinglulu 			row_off = (pblk << nand->pi_width) | pg_no;
416*91f16700Schasinglulu 
417*91f16700Schasinglulu 			ret = nand_read_data(
418*91f16700Schasinglulu 					nand->ifc_region_addr,
419*91f16700Schasinglulu 					row_off,
420*91f16700Schasinglulu 					0,
421*91f16700Schasinglulu 					read_cnt, out, MAIN, nand);
422*91f16700Schasinglulu 
423*91f16700Schasinglulu 			if (ret != 0) {
424*91f16700Schasinglulu 				ERROR("Error from nand-read_data %d\n", ret);
425*91f16700Schasinglulu 				return ret;
426*91f16700Schasinglulu 			}
427*91f16700Schasinglulu 		}
428*91f16700Schasinglulu 		src_addr += byte_cnt;
429*91f16700Schasinglulu 		out += byte_cnt;
430*91f16700Schasinglulu 		size -= byte_cnt;
431*91f16700Schasinglulu 	}
432*91f16700Schasinglulu 	return 0;
433*91f16700Schasinglulu }
434*91f16700Schasinglulu 
435*91f16700Schasinglulu static int isgoodblock(uint32_t blk, uint32_t *gb, struct nand_info *nand)
436*91f16700Schasinglulu {
437*91f16700Schasinglulu 	uint8_t buf[2];
438*91f16700Schasinglulu 	int ret;
439*91f16700Schasinglulu 	uint32_t row_add;
440*91f16700Schasinglulu 
441*91f16700Schasinglulu 	*gb = 0;
442*91f16700Schasinglulu 
443*91f16700Schasinglulu 	/* read Page 0 of blk */
444*91f16700Schasinglulu 	ret = nand_read_data(
445*91f16700Schasinglulu 			nand->ifc_region_addr,
446*91f16700Schasinglulu 			blk << nand->pi_width,
447*91f16700Schasinglulu 			nand->bad_marker_loc,
448*91f16700Schasinglulu 			0x2, buf, 1, nand);
449*91f16700Schasinglulu 
450*91f16700Schasinglulu 	if (ret != 0)
451*91f16700Schasinglulu 		return ret;
452*91f16700Schasinglulu 
453*91f16700Schasinglulu 	/* For ONFI devices check Page 0 and Last page of block for
454*91f16700Schasinglulu 	 * Bad Marker and for NON-ONFI Page 0 and 1 for Bad Marker
455*91f16700Schasinglulu 	 */
456*91f16700Schasinglulu 	row_add = (blk << nand->pi_width);
457*91f16700Schasinglulu 	if (nand->port_size == 8) {
458*91f16700Schasinglulu 		/* port size is 8 Bit */
459*91f16700Schasinglulu 		/* check if page 0 has 0xff */
460*91f16700Schasinglulu 		if (buf[0] == 0xff) {
461*91f16700Schasinglulu 			/* check page 1 */
462*91f16700Schasinglulu 			if (nand->onfi_dev_flag)
463*91f16700Schasinglulu 				ret =  nand_read_data(
464*91f16700Schasinglulu 						nand->ifc_region_addr,
465*91f16700Schasinglulu 						row_add | (nand->ppb - 1),
466*91f16700Schasinglulu 						nand->bad_marker_loc,
467*91f16700Schasinglulu 						0x2, buf, SPARE, nand);
468*91f16700Schasinglulu 			else
469*91f16700Schasinglulu 				ret =  nand_read_data(
470*91f16700Schasinglulu 						nand->ifc_region_addr,
471*91f16700Schasinglulu 						row_add | 1,
472*91f16700Schasinglulu 						nand->bad_marker_loc,
473*91f16700Schasinglulu 						0x2, buf, SPARE, nand);
474*91f16700Schasinglulu 
475*91f16700Schasinglulu 			if (ret != 0)
476*91f16700Schasinglulu 				return ret;
477*91f16700Schasinglulu 
478*91f16700Schasinglulu 			if (buf[0] == 0xff)
479*91f16700Schasinglulu 				*gb = GOOD_BLK;
480*91f16700Schasinglulu 			else
481*91f16700Schasinglulu 				*gb = BAD_BLK;
482*91f16700Schasinglulu 		} else {
483*91f16700Schasinglulu 			/* no, so it is bad blk */
484*91f16700Schasinglulu 			*gb = BAD_BLK;
485*91f16700Schasinglulu 		}
486*91f16700Schasinglulu 	} else {
487*91f16700Schasinglulu 		/* Port size 16-Bit */
488*91f16700Schasinglulu 		/* check if page 0 has 0xffff */
489*91f16700Schasinglulu 		if ((buf[0] == 0xff) &&
490*91f16700Schasinglulu 			(buf[1] == 0xff)) {
491*91f16700Schasinglulu 			/* check page 1 for 0xffff */
492*91f16700Schasinglulu 			if (nand->onfi_dev_flag) {
493*91f16700Schasinglulu 				ret =  nand_read_data(
494*91f16700Schasinglulu 						nand->ifc_region_addr,
495*91f16700Schasinglulu 						row_add | (nand->ppb - 1),
496*91f16700Schasinglulu 						nand->bad_marker_loc,
497*91f16700Schasinglulu 						0x2, buf, SPARE, nand);
498*91f16700Schasinglulu 			} else {
499*91f16700Schasinglulu 				ret =  nand_read_data(
500*91f16700Schasinglulu 						nand->ifc_region_addr,
501*91f16700Schasinglulu 						row_add | 1,
502*91f16700Schasinglulu 						nand->bad_marker_loc,
503*91f16700Schasinglulu 						0x2, buf, SPARE, nand);
504*91f16700Schasinglulu 			}
505*91f16700Schasinglulu 
506*91f16700Schasinglulu 			if (ret != 0)
507*91f16700Schasinglulu 				return ret;
508*91f16700Schasinglulu 
509*91f16700Schasinglulu 			if ((buf[0] == 0xff) &&
510*91f16700Schasinglulu 				(buf[1] == 0xff)) {
511*91f16700Schasinglulu 				*gb = GOOD_BLK;
512*91f16700Schasinglulu 			} else {
513*91f16700Schasinglulu 				*gb = BAD_BLK;
514*91f16700Schasinglulu 			}
515*91f16700Schasinglulu 		} else {
516*91f16700Schasinglulu 			/* no, so it is bad blk */
517*91f16700Schasinglulu 			*gb = BAD_BLK;
518*91f16700Schasinglulu 		}
519*91f16700Schasinglulu 	}
520*91f16700Schasinglulu 	return 0;
521*91f16700Schasinglulu }
522*91f16700Schasinglulu 
523*91f16700Schasinglulu static int update_bbt(uint32_t idx, uint32_t blk,
524*91f16700Schasinglulu 			   uint32_t *updated,  struct nand_info *nand)
525*91f16700Schasinglulu {
526*91f16700Schasinglulu 	uint32_t sblk;
527*91f16700Schasinglulu 	uint32_t lgb;
528*91f16700Schasinglulu 	int ret;
529*91f16700Schasinglulu 
530*91f16700Schasinglulu 	if (nand->bzero_good && blk == 0)
531*91f16700Schasinglulu 		return 0;
532*91f16700Schasinglulu 
533*91f16700Schasinglulu 	/* special case for lgb == 0 */
534*91f16700Schasinglulu 	/* if blk <= lgb return */
535*91f16700Schasinglulu 	if (nand->lgb != 0 && blk <= nand->lgb)
536*91f16700Schasinglulu 		return 0;
537*91f16700Schasinglulu 
538*91f16700Schasinglulu 	*updated = 0;
539*91f16700Schasinglulu 
540*91f16700Schasinglulu 	/* if blk is more than lgb, iterate from lgb till a good block
541*91f16700Schasinglulu 	 * is found for blk
542*91f16700Schasinglulu 	 */
543*91f16700Schasinglulu 
544*91f16700Schasinglulu 	if (nand->lgb < blk)
545*91f16700Schasinglulu 		sblk = nand->lgb;
546*91f16700Schasinglulu 	else
547*91f16700Schasinglulu 		/* this is when lgb = 0 */
548*91f16700Schasinglulu 		sblk = blk;
549*91f16700Schasinglulu 
550*91f16700Schasinglulu 
551*91f16700Schasinglulu 	lgb = nand->lgb;
552*91f16700Schasinglulu 
553*91f16700Schasinglulu 	/* loop from blk to find a good block */
554*91f16700Schasinglulu 	while (1) {
555*91f16700Schasinglulu 		while (lgb <= sblk) {
556*91f16700Schasinglulu 			uint32_t gb = 0;
557*91f16700Schasinglulu 
558*91f16700Schasinglulu 			ret =  isgoodblock(lgb, &gb, nand);
559*91f16700Schasinglulu 			if (ret != 0)
560*91f16700Schasinglulu 				return ret;
561*91f16700Schasinglulu 
562*91f16700Schasinglulu 			/* special case block 0 is good then set this flag */
563*91f16700Schasinglulu 			if (lgb == 0 && gb == GOOD_BLK)
564*91f16700Schasinglulu 				nand->bzero_good = 1;
565*91f16700Schasinglulu 
566*91f16700Schasinglulu 			if (gb == BAD_BLK) {
567*91f16700Schasinglulu 				if (idx >= BBT_SIZE) {
568*91f16700Schasinglulu 					ERROR("NAND BBT Table full\n");
569*91f16700Schasinglulu 					return -1;
570*91f16700Schasinglulu 				}
571*91f16700Schasinglulu 				*updated = 1;
572*91f16700Schasinglulu 				nand->bbt[idx] = lgb;
573*91f16700Schasinglulu 				idx++;
574*91f16700Schasinglulu 				blk++;
575*91f16700Schasinglulu 				sblk++;
576*91f16700Schasinglulu 				if (idx > nand->bbt_max)
577*91f16700Schasinglulu 					nand->bbt_max = idx;
578*91f16700Schasinglulu 			}
579*91f16700Schasinglulu 			lgb++;
580*91f16700Schasinglulu 		}
581*91f16700Schasinglulu 		/* the access block found */
582*91f16700Schasinglulu 		if (sblk == blk) {
583*91f16700Schasinglulu 			/* when good block found update lgb */
584*91f16700Schasinglulu 			nand->lgb =  blk;
585*91f16700Schasinglulu 			break;
586*91f16700Schasinglulu 		}
587*91f16700Schasinglulu 		sblk++;
588*91f16700Schasinglulu 	}
589*91f16700Schasinglulu 
590*91f16700Schasinglulu 	return 0;
591*91f16700Schasinglulu }
592*91f16700Schasinglulu 
593*91f16700Schasinglulu static size_t ifc_nand_read(int lba, uintptr_t buf, size_t size)
594*91f16700Schasinglulu {
595*91f16700Schasinglulu 	int ret;
596*91f16700Schasinglulu 	uint32_t page_size;
597*91f16700Schasinglulu 	uint32_t src_addr;
598*91f16700Schasinglulu 	struct nand_info *nand = &nand_drv_data;
599*91f16700Schasinglulu 
600*91f16700Schasinglulu 	page_size = nand_get_page_size(nand);
601*91f16700Schasinglulu 	src_addr = lba * page_size;
602*91f16700Schasinglulu 	ret = nand_read(nand, src_addr, buf, size);
603*91f16700Schasinglulu 	return ret ? 0 : size;
604*91f16700Schasinglulu }
605*91f16700Schasinglulu 
606*91f16700Schasinglulu static struct io_block_dev_spec ifc_nand_spec = {
607*91f16700Schasinglulu 	.buffer = {
608*91f16700Schasinglulu 		.offset = 0,
609*91f16700Schasinglulu 		.length = 0,
610*91f16700Schasinglulu 	},
611*91f16700Schasinglulu 	.ops = {
612*91f16700Schasinglulu 		.read = ifc_nand_read,
613*91f16700Schasinglulu 	},
614*91f16700Schasinglulu 	/*
615*91f16700Schasinglulu 	 * Default block size assumed as 2K
616*91f16700Schasinglulu 	 * Would be updated based on actual size
617*91f16700Schasinglulu 	 */
618*91f16700Schasinglulu 	.block_size = UL(2048),
619*91f16700Schasinglulu };
620*91f16700Schasinglulu 
621*91f16700Schasinglulu int ifc_nand_init(uintptr_t *block_dev_spec,
622*91f16700Schasinglulu 			uintptr_t ifc_region_addr,
623*91f16700Schasinglulu 			uintptr_t ifc_register_addr,
624*91f16700Schasinglulu 			size_t ifc_sram_size,
625*91f16700Schasinglulu 			uintptr_t ifc_nand_blk_offset,
626*91f16700Schasinglulu 			size_t ifc_nand_blk_size)
627*91f16700Schasinglulu {
628*91f16700Schasinglulu 	struct nand_info *nand = NULL;
629*91f16700Schasinglulu 	int ret;
630*91f16700Schasinglulu 
631*91f16700Schasinglulu 	nand = &nand_drv_data;
632*91f16700Schasinglulu 	memset(nand, 0, sizeof(struct nand_info));
633*91f16700Schasinglulu 
634*91f16700Schasinglulu 	nand->ifc_region_addr = ifc_region_addr;
635*91f16700Schasinglulu 	nand->ifc_register_addr = ifc_register_addr;
636*91f16700Schasinglulu 
637*91f16700Schasinglulu 	VERBOSE("nand_init\n");
638*91f16700Schasinglulu 	ret = nand_init(nand);
639*91f16700Schasinglulu 	if (ret) {
640*91f16700Schasinglulu 		ERROR("nand init failed\n");
641*91f16700Schasinglulu 		return ret;
642*91f16700Schasinglulu 	}
643*91f16700Schasinglulu 
644*91f16700Schasinglulu 	ifc_nand_spec.buffer.offset = ifc_nand_blk_offset;
645*91f16700Schasinglulu 	ifc_nand_spec.buffer.length = ifc_nand_blk_size;
646*91f16700Schasinglulu 
647*91f16700Schasinglulu 	ifc_nand_spec.block_size = nand_get_page_size(nand);
648*91f16700Schasinglulu 
649*91f16700Schasinglulu 	VERBOSE("Page size is %ld\n", ifc_nand_spec.block_size);
650*91f16700Schasinglulu 
651*91f16700Schasinglulu 	*block_dev_spec = (uintptr_t)&ifc_nand_spec;
652*91f16700Schasinglulu 
653*91f16700Schasinglulu 	/* Adding NAND SRAM< Buffer in XLAT Table */
654*91f16700Schasinglulu 	mmap_add_region(ifc_region_addr, ifc_region_addr,
655*91f16700Schasinglulu 			ifc_sram_size, MT_DEVICE | MT_RW);
656*91f16700Schasinglulu 
657*91f16700Schasinglulu 	return 0;
658*91f16700Schasinglulu }
659