xref: /arm-trusted-firmware/drivers/mtd/nand/spi_nand.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2019-2023,  STMicroelectronics - All Rights Reserved
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <assert.h>
8*91f16700Schasinglulu #include <errno.h>
9*91f16700Schasinglulu #include <stddef.h>
10*91f16700Schasinglulu 
11*91f16700Schasinglulu #include <common/debug.h>
12*91f16700Schasinglulu #include <drivers/delay_timer.h>
13*91f16700Schasinglulu #include <drivers/spi_nand.h>
14*91f16700Schasinglulu #include <lib/utils.h>
15*91f16700Schasinglulu 
16*91f16700Schasinglulu #include <platform_def.h>
17*91f16700Schasinglulu 
18*91f16700Schasinglulu #define SPI_NAND_MAX_ID_LEN		4U
19*91f16700Schasinglulu #define DELAY_US_400MS			400000U
20*91f16700Schasinglulu 
21*91f16700Schasinglulu static struct spinand_device spinand_dev;
22*91f16700Schasinglulu 
23*91f16700Schasinglulu #pragma weak plat_get_spi_nand_data
24*91f16700Schasinglulu int plat_get_spi_nand_data(struct spinand_device *device)
25*91f16700Schasinglulu {
26*91f16700Schasinglulu 	return 0;
27*91f16700Schasinglulu }
28*91f16700Schasinglulu 
29*91f16700Schasinglulu static int spi_nand_reg(bool read_reg, uint8_t reg, uint8_t *val,
30*91f16700Schasinglulu 			enum spi_mem_data_dir dir)
31*91f16700Schasinglulu {
32*91f16700Schasinglulu 	struct spi_mem_op op;
33*91f16700Schasinglulu 
34*91f16700Schasinglulu 	zeromem(&op, sizeof(struct spi_mem_op));
35*91f16700Schasinglulu 	if (read_reg) {
36*91f16700Schasinglulu 		op.cmd.opcode = SPI_NAND_OP_GET_FEATURE;
37*91f16700Schasinglulu 	} else {
38*91f16700Schasinglulu 		op.cmd.opcode = SPI_NAND_OP_SET_FEATURE;
39*91f16700Schasinglulu 	}
40*91f16700Schasinglulu 
41*91f16700Schasinglulu 	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
42*91f16700Schasinglulu 	op.addr.val = reg;
43*91f16700Schasinglulu 	op.addr.nbytes = 1U;
44*91f16700Schasinglulu 	op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
45*91f16700Schasinglulu 	op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
46*91f16700Schasinglulu 	op.data.dir = dir;
47*91f16700Schasinglulu 	op.data.nbytes = 1U;
48*91f16700Schasinglulu 	op.data.buf = val;
49*91f16700Schasinglulu 
50*91f16700Schasinglulu 	return spi_mem_exec_op(&op);
51*91f16700Schasinglulu }
52*91f16700Schasinglulu 
53*91f16700Schasinglulu static int spi_nand_read_reg(uint8_t reg, uint8_t *val)
54*91f16700Schasinglulu {
55*91f16700Schasinglulu 	return spi_nand_reg(true, reg, val, SPI_MEM_DATA_IN);
56*91f16700Schasinglulu }
57*91f16700Schasinglulu 
58*91f16700Schasinglulu static int spi_nand_write_reg(uint8_t reg, uint8_t val)
59*91f16700Schasinglulu {
60*91f16700Schasinglulu 	return spi_nand_reg(false, reg, &val, SPI_MEM_DATA_OUT);
61*91f16700Schasinglulu }
62*91f16700Schasinglulu 
63*91f16700Schasinglulu static int spi_nand_update_cfg(uint8_t mask, uint8_t val)
64*91f16700Schasinglulu {
65*91f16700Schasinglulu 	int ret;
66*91f16700Schasinglulu 	uint8_t cfg = spinand_dev.cfg_cache;
67*91f16700Schasinglulu 
68*91f16700Schasinglulu 	cfg &= ~mask;
69*91f16700Schasinglulu 	cfg |= val;
70*91f16700Schasinglulu 
71*91f16700Schasinglulu 	if (cfg == spinand_dev.cfg_cache) {
72*91f16700Schasinglulu 		return 0;
73*91f16700Schasinglulu 	}
74*91f16700Schasinglulu 
75*91f16700Schasinglulu 	ret = spi_nand_write_reg(SPI_NAND_REG_CFG, cfg);
76*91f16700Schasinglulu 	if (ret == 0) {
77*91f16700Schasinglulu 		spinand_dev.cfg_cache = cfg;
78*91f16700Schasinglulu 	}
79*91f16700Schasinglulu 
80*91f16700Schasinglulu 	return ret;
81*91f16700Schasinglulu }
82*91f16700Schasinglulu 
83*91f16700Schasinglulu static int spi_nand_ecc_enable(bool enable)
84*91f16700Schasinglulu {
85*91f16700Schasinglulu 	return spi_nand_update_cfg(SPI_NAND_CFG_ECC_EN,
86*91f16700Schasinglulu 				   enable ? SPI_NAND_CFG_ECC_EN : 0U);
87*91f16700Schasinglulu }
88*91f16700Schasinglulu 
89*91f16700Schasinglulu static int spi_nand_quad_enable(uint8_t manufacturer_id)
90*91f16700Schasinglulu {
91*91f16700Schasinglulu 	bool enable = false;
92*91f16700Schasinglulu 
93*91f16700Schasinglulu 	if ((spinand_dev.flags & SPI_NAND_HAS_QE_BIT) == 0U) {
94*91f16700Schasinglulu 		return 0;
95*91f16700Schasinglulu 	}
96*91f16700Schasinglulu 
97*91f16700Schasinglulu 	if (spinand_dev.spi_read_cache_op.data.buswidth ==
98*91f16700Schasinglulu 	    SPI_MEM_BUSWIDTH_4_LINE) {
99*91f16700Schasinglulu 		enable = true;
100*91f16700Schasinglulu 	}
101*91f16700Schasinglulu 
102*91f16700Schasinglulu 	return spi_nand_update_cfg(SPI_NAND_CFG_QE,
103*91f16700Schasinglulu 				   enable ? SPI_NAND_CFG_QE : 0U);
104*91f16700Schasinglulu }
105*91f16700Schasinglulu 
106*91f16700Schasinglulu static int spi_nand_wait_ready(uint8_t *status)
107*91f16700Schasinglulu {
108*91f16700Schasinglulu 	int ret;
109*91f16700Schasinglulu 	uint64_t timeout = timeout_init_us(DELAY_US_400MS);
110*91f16700Schasinglulu 
111*91f16700Schasinglulu 	while (!timeout_elapsed(timeout)) {
112*91f16700Schasinglulu 		ret = spi_nand_read_reg(SPI_NAND_REG_STATUS, status);
113*91f16700Schasinglulu 		if (ret != 0) {
114*91f16700Schasinglulu 			return ret;
115*91f16700Schasinglulu 		}
116*91f16700Schasinglulu 
117*91f16700Schasinglulu 		VERBOSE("%s Status %x\n", __func__, *status);
118*91f16700Schasinglulu 		if ((*status & SPI_NAND_STATUS_BUSY) == 0U) {
119*91f16700Schasinglulu 			return 0;
120*91f16700Schasinglulu 		}
121*91f16700Schasinglulu 	}
122*91f16700Schasinglulu 
123*91f16700Schasinglulu 	return -ETIMEDOUT;
124*91f16700Schasinglulu }
125*91f16700Schasinglulu 
126*91f16700Schasinglulu static int spi_nand_reset(void)
127*91f16700Schasinglulu {
128*91f16700Schasinglulu 	struct spi_mem_op op;
129*91f16700Schasinglulu 	uint8_t status;
130*91f16700Schasinglulu 	int ret;
131*91f16700Schasinglulu 
132*91f16700Schasinglulu 	zeromem(&op, sizeof(struct spi_mem_op));
133*91f16700Schasinglulu 	op.cmd.opcode = SPI_NAND_OP_RESET;
134*91f16700Schasinglulu 	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
135*91f16700Schasinglulu 
136*91f16700Schasinglulu 	ret = spi_mem_exec_op(&op);
137*91f16700Schasinglulu 	if (ret != 0) {
138*91f16700Schasinglulu 		return ret;
139*91f16700Schasinglulu 	}
140*91f16700Schasinglulu 
141*91f16700Schasinglulu 	return spi_nand_wait_ready(&status);
142*91f16700Schasinglulu }
143*91f16700Schasinglulu 
144*91f16700Schasinglulu static int spi_nand_read_id(uint8_t *id)
145*91f16700Schasinglulu {
146*91f16700Schasinglulu 	struct spi_mem_op op;
147*91f16700Schasinglulu 
148*91f16700Schasinglulu 	zeromem(&op, sizeof(struct spi_mem_op));
149*91f16700Schasinglulu 	op.cmd.opcode = SPI_NAND_OP_READ_ID;
150*91f16700Schasinglulu 	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
151*91f16700Schasinglulu 	op.data.dir = SPI_MEM_DATA_IN;
152*91f16700Schasinglulu 	op.data.nbytes = SPI_NAND_MAX_ID_LEN;
153*91f16700Schasinglulu 	op.data.buf = id;
154*91f16700Schasinglulu 	op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
155*91f16700Schasinglulu 
156*91f16700Schasinglulu 	return spi_mem_exec_op(&op);
157*91f16700Schasinglulu }
158*91f16700Schasinglulu 
159*91f16700Schasinglulu static int spi_nand_load_page(unsigned int page)
160*91f16700Schasinglulu {
161*91f16700Schasinglulu 	struct spi_mem_op op;
162*91f16700Schasinglulu 	uint32_t block_nb = page / spinand_dev.nand_dev->block_size;
163*91f16700Schasinglulu 	uint32_t page_nb = page - (block_nb * spinand_dev.nand_dev->page_size);
164*91f16700Schasinglulu 	uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size /
165*91f16700Schasinglulu 				     spinand_dev.nand_dev->page_size;
166*91f16700Schasinglulu 	uint32_t block_sh = __builtin_ctz(nbpages_per_block) + 1U;
167*91f16700Schasinglulu 
168*91f16700Schasinglulu 	zeromem(&op, sizeof(struct spi_mem_op));
169*91f16700Schasinglulu 	op.cmd.opcode = SPI_NAND_OP_LOAD_PAGE;
170*91f16700Schasinglulu 	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
171*91f16700Schasinglulu 	op.addr.val = (block_nb << block_sh) | page_nb;
172*91f16700Schasinglulu 	op.addr.nbytes = 3U;
173*91f16700Schasinglulu 	op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
174*91f16700Schasinglulu 
175*91f16700Schasinglulu 	return spi_mem_exec_op(&op);
176*91f16700Schasinglulu }
177*91f16700Schasinglulu 
178*91f16700Schasinglulu static int spi_nand_read_from_cache(unsigned int page, unsigned int offset,
179*91f16700Schasinglulu 				    uint8_t *buffer, unsigned int len)
180*91f16700Schasinglulu {
181*91f16700Schasinglulu 	uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size /
182*91f16700Schasinglulu 				     spinand_dev.nand_dev->page_size;
183*91f16700Schasinglulu 	uint32_t block_nb = page / nbpages_per_block;
184*91f16700Schasinglulu 	uint32_t page_sh = __builtin_ctz(spinand_dev.nand_dev->page_size) + 1U;
185*91f16700Schasinglulu 
186*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.addr.val = offset;
187*91f16700Schasinglulu 
188*91f16700Schasinglulu 	if ((spinand_dev.nand_dev->nb_planes > 1U) && ((block_nb % 2U) == 1U)) {
189*91f16700Schasinglulu 		spinand_dev.spi_read_cache_op.addr.val |= 1U << page_sh;
190*91f16700Schasinglulu 	}
191*91f16700Schasinglulu 
192*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.data.buf = buffer;
193*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.data.nbytes = len;
194*91f16700Schasinglulu 
195*91f16700Schasinglulu 	return spi_mem_exec_op(&spinand_dev.spi_read_cache_op);
196*91f16700Schasinglulu }
197*91f16700Schasinglulu 
198*91f16700Schasinglulu static int spi_nand_read_page(unsigned int page, unsigned int offset,
199*91f16700Schasinglulu 			      uint8_t *buffer, unsigned int len,
200*91f16700Schasinglulu 			      bool ecc_enabled)
201*91f16700Schasinglulu {
202*91f16700Schasinglulu 	uint8_t status;
203*91f16700Schasinglulu 	int ret;
204*91f16700Schasinglulu 
205*91f16700Schasinglulu 	ret = spi_nand_ecc_enable(ecc_enabled);
206*91f16700Schasinglulu 	if (ret != 0) {
207*91f16700Schasinglulu 		return ret;
208*91f16700Schasinglulu 	}
209*91f16700Schasinglulu 
210*91f16700Schasinglulu 	ret = spi_nand_load_page(page);
211*91f16700Schasinglulu 	if (ret != 0) {
212*91f16700Schasinglulu 		return ret;
213*91f16700Schasinglulu 	}
214*91f16700Schasinglulu 
215*91f16700Schasinglulu 	ret = spi_nand_wait_ready(&status);
216*91f16700Schasinglulu 	if (ret != 0) {
217*91f16700Schasinglulu 		return ret;
218*91f16700Schasinglulu 	}
219*91f16700Schasinglulu 
220*91f16700Schasinglulu 	ret = spi_nand_read_from_cache(page, offset, buffer, len);
221*91f16700Schasinglulu 	if (ret != 0) {
222*91f16700Schasinglulu 		return ret;
223*91f16700Schasinglulu 	}
224*91f16700Schasinglulu 
225*91f16700Schasinglulu 	if (ecc_enabled && ((status & SPI_NAND_STATUS_ECC_UNCOR) != 0U)) {
226*91f16700Schasinglulu 		return -EBADMSG;
227*91f16700Schasinglulu 	}
228*91f16700Schasinglulu 
229*91f16700Schasinglulu 	return 0;
230*91f16700Schasinglulu }
231*91f16700Schasinglulu 
232*91f16700Schasinglulu static int spi_nand_mtd_block_is_bad(unsigned int block)
233*91f16700Schasinglulu {
234*91f16700Schasinglulu 	unsigned int nbpages_per_block = spinand_dev.nand_dev->block_size /
235*91f16700Schasinglulu 					 spinand_dev.nand_dev->page_size;
236*91f16700Schasinglulu 	uint8_t bbm_marker[2];
237*91f16700Schasinglulu 	int ret;
238*91f16700Schasinglulu 
239*91f16700Schasinglulu 	ret = spi_nand_read_page(block * nbpages_per_block,
240*91f16700Schasinglulu 				 spinand_dev.nand_dev->page_size,
241*91f16700Schasinglulu 				 bbm_marker, sizeof(bbm_marker), false);
242*91f16700Schasinglulu 	if (ret != 0) {
243*91f16700Schasinglulu 		return ret;
244*91f16700Schasinglulu 	}
245*91f16700Schasinglulu 
246*91f16700Schasinglulu 	if ((bbm_marker[0] != GENMASK_32(7, 0)) ||
247*91f16700Schasinglulu 	    (bbm_marker[1] != GENMASK_32(7, 0))) {
248*91f16700Schasinglulu 		WARN("Block %u is bad\n", block);
249*91f16700Schasinglulu 		return 1;
250*91f16700Schasinglulu 	}
251*91f16700Schasinglulu 
252*91f16700Schasinglulu 	return 0;
253*91f16700Schasinglulu }
254*91f16700Schasinglulu 
255*91f16700Schasinglulu static int spi_nand_mtd_read_page(struct nand_device *nand, unsigned int page,
256*91f16700Schasinglulu 				  uintptr_t buffer)
257*91f16700Schasinglulu {
258*91f16700Schasinglulu 	return spi_nand_read_page(page, 0, (uint8_t *)buffer,
259*91f16700Schasinglulu 				  spinand_dev.nand_dev->page_size, true);
260*91f16700Schasinglulu }
261*91f16700Schasinglulu 
262*91f16700Schasinglulu int spi_nand_init(unsigned long long *size, unsigned int *erase_size)
263*91f16700Schasinglulu {
264*91f16700Schasinglulu 	uint8_t id[SPI_NAND_MAX_ID_LEN];
265*91f16700Schasinglulu 	int ret;
266*91f16700Schasinglulu 
267*91f16700Schasinglulu 	spinand_dev.nand_dev = get_nand_device();
268*91f16700Schasinglulu 	if (spinand_dev.nand_dev == NULL) {
269*91f16700Schasinglulu 		return -EINVAL;
270*91f16700Schasinglulu 	}
271*91f16700Schasinglulu 
272*91f16700Schasinglulu 	spinand_dev.nand_dev->mtd_block_is_bad = spi_nand_mtd_block_is_bad;
273*91f16700Schasinglulu 	spinand_dev.nand_dev->mtd_read_page = spi_nand_mtd_read_page;
274*91f16700Schasinglulu 	spinand_dev.nand_dev->nb_planes = 1;
275*91f16700Schasinglulu 
276*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.cmd.opcode = SPI_NAND_OP_READ_FROM_CACHE;
277*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
278*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.addr.nbytes = 2U;
279*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
280*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.dummy.nbytes = 1U;
281*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
282*91f16700Schasinglulu 	spinand_dev.spi_read_cache_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
283*91f16700Schasinglulu 
284*91f16700Schasinglulu 	if (plat_get_spi_nand_data(&spinand_dev) != 0) {
285*91f16700Schasinglulu 		return -EINVAL;
286*91f16700Schasinglulu 	}
287*91f16700Schasinglulu 
288*91f16700Schasinglulu 	assert((spinand_dev.nand_dev->page_size != 0U) &&
289*91f16700Schasinglulu 	       (spinand_dev.nand_dev->block_size != 0U) &&
290*91f16700Schasinglulu 	       (spinand_dev.nand_dev->size != 0U));
291*91f16700Schasinglulu 
292*91f16700Schasinglulu 	ret = spi_nand_reset();
293*91f16700Schasinglulu 	if (ret != 0) {
294*91f16700Schasinglulu 		return ret;
295*91f16700Schasinglulu 	}
296*91f16700Schasinglulu 
297*91f16700Schasinglulu 	ret = spi_nand_read_id(id);
298*91f16700Schasinglulu 	if (ret != 0) {
299*91f16700Schasinglulu 		return ret;
300*91f16700Schasinglulu 	}
301*91f16700Schasinglulu 
302*91f16700Schasinglulu 	ret = spi_nand_read_reg(SPI_NAND_REG_CFG, &spinand_dev.cfg_cache);
303*91f16700Schasinglulu 	if (ret != 0) {
304*91f16700Schasinglulu 		return ret;
305*91f16700Schasinglulu 	}
306*91f16700Schasinglulu 
307*91f16700Schasinglulu 	ret = spi_nand_quad_enable(id[1]);
308*91f16700Schasinglulu 	if (ret != 0) {
309*91f16700Schasinglulu 		return ret;
310*91f16700Schasinglulu 	}
311*91f16700Schasinglulu 
312*91f16700Schasinglulu 	VERBOSE("SPI_NAND Detected ID 0x%x\n", id[1]);
313*91f16700Schasinglulu 
314*91f16700Schasinglulu 	VERBOSE("Page size %u, Block size %u, size %llu\n",
315*91f16700Schasinglulu 		spinand_dev.nand_dev->page_size,
316*91f16700Schasinglulu 		spinand_dev.nand_dev->block_size,
317*91f16700Schasinglulu 		spinand_dev.nand_dev->size);
318*91f16700Schasinglulu 
319*91f16700Schasinglulu 	*size = spinand_dev.nand_dev->size;
320*91f16700Schasinglulu 	*erase_size = spinand_dev.nand_dev->block_size;
321*91f16700Schasinglulu 
322*91f16700Schasinglulu 	return 0;
323*91f16700Schasinglulu }
324