xref: /arm-trusted-firmware/plat/socionext/uniphier/uniphier_nand.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2017-2020, ARM Limited and Contributors. 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 <stdint.h>
9*91f16700Schasinglulu 
10*91f16700Schasinglulu #include <platform_def.h>
11*91f16700Schasinglulu 
12*91f16700Schasinglulu #include <arch_helpers.h>
13*91f16700Schasinglulu #include <common/debug.h>
14*91f16700Schasinglulu #include <drivers/io/io_block.h>
15*91f16700Schasinglulu #include <lib/mmio.h>
16*91f16700Schasinglulu #include <lib/utils_def.h>
17*91f16700Schasinglulu 
18*91f16700Schasinglulu #include "uniphier.h"
19*91f16700Schasinglulu 
20*91f16700Schasinglulu #define NAND_CMD_READ0		0
21*91f16700Schasinglulu #define NAND_CMD_READSTART	0x30
22*91f16700Schasinglulu 
23*91f16700Schasinglulu #define DENALI_ECC_ENABLE			0x0e0
24*91f16700Schasinglulu #define DENALI_PAGES_PER_BLOCK			0x150
25*91f16700Schasinglulu #define DENALI_DEVICE_MAIN_AREA_SIZE		0x170
26*91f16700Schasinglulu #define DENALI_DEVICE_SPARE_AREA_SIZE		0x180
27*91f16700Schasinglulu #define DENALI_TWO_ROW_ADDR_CYCLES		0x190
28*91f16700Schasinglulu #define DENALI_INTR_STATUS0			0x410
29*91f16700Schasinglulu #define   DENALI_INTR_ECC_UNCOR_ERR			BIT(1)
30*91f16700Schasinglulu #define   DENALI_INTR_DMA_CMD_COMP			BIT(2)
31*91f16700Schasinglulu #define   DENALI_INTR_INT_ACT				BIT(12)
32*91f16700Schasinglulu 
33*91f16700Schasinglulu #define DENALI_DMA_ENABLE			0x700
34*91f16700Schasinglulu 
35*91f16700Schasinglulu #define DENALI_HOST_ADDR			0x00
36*91f16700Schasinglulu #define DENALI_HOST_DATA			0x10
37*91f16700Schasinglulu 
38*91f16700Schasinglulu #define DENALI_MAP01				(1 << 26)
39*91f16700Schasinglulu #define DENALI_MAP10				(2 << 26)
40*91f16700Schasinglulu #define DENALI_MAP11				(3 << 26)
41*91f16700Schasinglulu 
42*91f16700Schasinglulu #define DENALI_MAP11_CMD			((DENALI_MAP11) | 0)
43*91f16700Schasinglulu #define DENALI_MAP11_ADDR			((DENALI_MAP11) | 1)
44*91f16700Schasinglulu #define DENALI_MAP11_DATA			((DENALI_MAP11) | 2)
45*91f16700Schasinglulu 
46*91f16700Schasinglulu #define DENALI_ACCESS_DEFAULT_AREA		0x42
47*91f16700Schasinglulu 
48*91f16700Schasinglulu #define UNIPHIER_NAND_BBT_UNKNOWN		0xff
49*91f16700Schasinglulu 
50*91f16700Schasinglulu struct uniphier_nand {
51*91f16700Schasinglulu 	uintptr_t host_base;
52*91f16700Schasinglulu 	uintptr_t reg_base;
53*91f16700Schasinglulu 	int pages_per_block;
54*91f16700Schasinglulu 	int page_size;
55*91f16700Schasinglulu 	int two_row_addr_cycles;
56*91f16700Schasinglulu 	uint8_t bbt[16];
57*91f16700Schasinglulu };
58*91f16700Schasinglulu 
59*91f16700Schasinglulu struct uniphier_nand uniphier_nand;
60*91f16700Schasinglulu 
61*91f16700Schasinglulu static void uniphier_nand_host_write(struct uniphier_nand *nand,
62*91f16700Schasinglulu 				     uint32_t addr, uint32_t data)
63*91f16700Schasinglulu {
64*91f16700Schasinglulu 	mmio_write_32(nand->host_base + DENALI_HOST_ADDR, addr);
65*91f16700Schasinglulu 	mmio_write_32(nand->host_base + DENALI_HOST_DATA, data);
66*91f16700Schasinglulu }
67*91f16700Schasinglulu 
68*91f16700Schasinglulu static uint32_t uniphier_nand_host_read(struct uniphier_nand *nand,
69*91f16700Schasinglulu 					uint32_t addr)
70*91f16700Schasinglulu {
71*91f16700Schasinglulu 	mmio_write_32(nand->host_base + DENALI_HOST_ADDR, addr);
72*91f16700Schasinglulu 	return mmio_read_32(nand->host_base + DENALI_HOST_DATA);
73*91f16700Schasinglulu }
74*91f16700Schasinglulu 
75*91f16700Schasinglulu static int uniphier_nand_block_isbad(struct uniphier_nand *nand, int block)
76*91f16700Schasinglulu {
77*91f16700Schasinglulu 	int page = nand->pages_per_block * block;
78*91f16700Schasinglulu 	int column = nand->page_size;
79*91f16700Schasinglulu 	uint8_t bbm;
80*91f16700Schasinglulu 	uint32_t status;
81*91f16700Schasinglulu 	int is_bad;
82*91f16700Schasinglulu 
83*91f16700Schasinglulu 	/* use cache if available */
84*91f16700Schasinglulu 	if (block < ARRAY_SIZE(nand->bbt) &&
85*91f16700Schasinglulu 	    nand->bbt[block] != UNIPHIER_NAND_BBT_UNKNOWN)
86*91f16700Schasinglulu 		return nand->bbt[block];
87*91f16700Schasinglulu 
88*91f16700Schasinglulu 	mmio_write_32(nand->reg_base + DENALI_ECC_ENABLE, 0);
89*91f16700Schasinglulu 
90*91f16700Schasinglulu 	mmio_write_32(nand->reg_base + DENALI_INTR_STATUS0, -1);
91*91f16700Schasinglulu 
92*91f16700Schasinglulu 	uniphier_nand_host_write(nand, DENALI_MAP11_CMD, NAND_CMD_READ0);
93*91f16700Schasinglulu 	uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, column & 0xff);
94*91f16700Schasinglulu 	uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, (column >> 8) & 0xff);
95*91f16700Schasinglulu 	uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, page & 0xff);
96*91f16700Schasinglulu 	uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, (page >> 8) & 0xff);
97*91f16700Schasinglulu 	if (!nand->two_row_addr_cycles)
98*91f16700Schasinglulu 		uniphier_nand_host_write(nand, DENALI_MAP11_ADDR,
99*91f16700Schasinglulu 					 (page >> 16) & 0xff);
100*91f16700Schasinglulu 	uniphier_nand_host_write(nand, DENALI_MAP11_CMD, NAND_CMD_READSTART);
101*91f16700Schasinglulu 
102*91f16700Schasinglulu 	do {
103*91f16700Schasinglulu 		status = mmio_read_32(nand->reg_base + DENALI_INTR_STATUS0);
104*91f16700Schasinglulu 	} while (!(status & DENALI_INTR_INT_ACT));
105*91f16700Schasinglulu 
106*91f16700Schasinglulu 	bbm = uniphier_nand_host_read(nand, DENALI_MAP11_DATA);
107*91f16700Schasinglulu 
108*91f16700Schasinglulu 	is_bad = bbm != 0xff;
109*91f16700Schasinglulu 
110*91f16700Schasinglulu 	/* if possible, save the result for future re-use */
111*91f16700Schasinglulu 	if (block < ARRAY_SIZE(nand->bbt))
112*91f16700Schasinglulu 		nand->bbt[block] = is_bad;
113*91f16700Schasinglulu 
114*91f16700Schasinglulu 	if (is_bad)
115*91f16700Schasinglulu 		WARN("found bad block at %d. skip.\n", block);
116*91f16700Schasinglulu 
117*91f16700Schasinglulu 	return is_bad;
118*91f16700Schasinglulu }
119*91f16700Schasinglulu 
120*91f16700Schasinglulu static int uniphier_nand_read_pages(struct uniphier_nand *nand, uintptr_t buf,
121*91f16700Schasinglulu 				    int page_start, int page_count)
122*91f16700Schasinglulu {
123*91f16700Schasinglulu 	uint32_t status;
124*91f16700Schasinglulu 
125*91f16700Schasinglulu 	mmio_write_32(nand->reg_base + DENALI_ECC_ENABLE, 1);
126*91f16700Schasinglulu 	mmio_write_32(nand->reg_base + DENALI_DMA_ENABLE, 1);
127*91f16700Schasinglulu 
128*91f16700Schasinglulu 	mmio_write_32(nand->reg_base + DENALI_INTR_STATUS0, -1);
129*91f16700Schasinglulu 
130*91f16700Schasinglulu 	/* use Data DMA (64bit) */
131*91f16700Schasinglulu 	mmio_write_32(nand->host_base + DENALI_HOST_ADDR,
132*91f16700Schasinglulu 		      DENALI_MAP10 | page_start);
133*91f16700Schasinglulu 
134*91f16700Schasinglulu 	/*
135*91f16700Schasinglulu 	 * 1. setup transfer type, interrupt when complete,
136*91f16700Schasinglulu 	 *    burst len = 64 bytes, the number of pages
137*91f16700Schasinglulu 	 */
138*91f16700Schasinglulu 	mmio_write_32(nand->host_base + DENALI_HOST_DATA,
139*91f16700Schasinglulu 		      0x01002000 | (64 << 16) | page_count);
140*91f16700Schasinglulu 
141*91f16700Schasinglulu 	/* 2. set memory low address */
142*91f16700Schasinglulu 	mmio_write_32(nand->host_base + DENALI_HOST_DATA, buf);
143*91f16700Schasinglulu 
144*91f16700Schasinglulu 	/* 3. set memory high address */
145*91f16700Schasinglulu 	mmio_write_32(nand->host_base + DENALI_HOST_DATA, buf >> 32);
146*91f16700Schasinglulu 
147*91f16700Schasinglulu 	do {
148*91f16700Schasinglulu 		status = mmio_read_32(nand->reg_base + DENALI_INTR_STATUS0);
149*91f16700Schasinglulu 	} while (!(status & DENALI_INTR_DMA_CMD_COMP));
150*91f16700Schasinglulu 
151*91f16700Schasinglulu 	mmio_write_32(nand->reg_base + DENALI_DMA_ENABLE, 0);
152*91f16700Schasinglulu 
153*91f16700Schasinglulu 	if (status & DENALI_INTR_ECC_UNCOR_ERR) {
154*91f16700Schasinglulu 		ERROR("uncorrectable error in page range %d-%d",
155*91f16700Schasinglulu 		      page_start, page_start + page_count - 1);
156*91f16700Schasinglulu 		return -EBADMSG;
157*91f16700Schasinglulu 	}
158*91f16700Schasinglulu 
159*91f16700Schasinglulu 	return 0;
160*91f16700Schasinglulu }
161*91f16700Schasinglulu 
162*91f16700Schasinglulu static size_t __uniphier_nand_read(struct uniphier_nand *nand, int lba,
163*91f16700Schasinglulu 				   uintptr_t buf, size_t size)
164*91f16700Schasinglulu {
165*91f16700Schasinglulu 	int pages_per_block = nand->pages_per_block;
166*91f16700Schasinglulu 	int page_size = nand->page_size;
167*91f16700Schasinglulu 	int blocks_to_skip = lba / pages_per_block;
168*91f16700Schasinglulu 	int pages_to_read = div_round_up(size, page_size);
169*91f16700Schasinglulu 	int page = lba % pages_per_block;
170*91f16700Schasinglulu 	int block = 0;
171*91f16700Schasinglulu 	uintptr_t p = buf;
172*91f16700Schasinglulu 	int page_count, ret;
173*91f16700Schasinglulu 
174*91f16700Schasinglulu 	while (blocks_to_skip) {
175*91f16700Schasinglulu 		ret = uniphier_nand_block_isbad(nand, block);
176*91f16700Schasinglulu 		if (ret < 0)
177*91f16700Schasinglulu 			goto out;
178*91f16700Schasinglulu 
179*91f16700Schasinglulu 		if (!ret)
180*91f16700Schasinglulu 			blocks_to_skip--;
181*91f16700Schasinglulu 
182*91f16700Schasinglulu 		block++;
183*91f16700Schasinglulu 	}
184*91f16700Schasinglulu 
185*91f16700Schasinglulu 	while (pages_to_read) {
186*91f16700Schasinglulu 		ret = uniphier_nand_block_isbad(nand, block);
187*91f16700Schasinglulu 		if (ret < 0)
188*91f16700Schasinglulu 			goto out;
189*91f16700Schasinglulu 
190*91f16700Schasinglulu 		if (ret) {
191*91f16700Schasinglulu 			block++;
192*91f16700Schasinglulu 			continue;
193*91f16700Schasinglulu 		}
194*91f16700Schasinglulu 
195*91f16700Schasinglulu 		page_count = MIN(pages_per_block - page, pages_to_read);
196*91f16700Schasinglulu 
197*91f16700Schasinglulu 		ret = uniphier_nand_read_pages(nand, p,
198*91f16700Schasinglulu 					       block * pages_per_block + page,
199*91f16700Schasinglulu 					       page_count);
200*91f16700Schasinglulu 		if (ret)
201*91f16700Schasinglulu 			goto out;
202*91f16700Schasinglulu 
203*91f16700Schasinglulu 		block++;
204*91f16700Schasinglulu 		page = 0;
205*91f16700Schasinglulu 		p += page_size * page_count;
206*91f16700Schasinglulu 		pages_to_read -= page_count;
207*91f16700Schasinglulu 	}
208*91f16700Schasinglulu 
209*91f16700Schasinglulu out:
210*91f16700Schasinglulu 	/* number of read bytes */
211*91f16700Schasinglulu 	return MIN(size, p - buf);
212*91f16700Schasinglulu }
213*91f16700Schasinglulu 
214*91f16700Schasinglulu static size_t uniphier_nand_read(int lba, uintptr_t buf, size_t size)
215*91f16700Schasinglulu {
216*91f16700Schasinglulu 	size_t count;
217*91f16700Schasinglulu 
218*91f16700Schasinglulu 	inv_dcache_range(buf, size);
219*91f16700Schasinglulu 
220*91f16700Schasinglulu 	count = __uniphier_nand_read(&uniphier_nand, lba, buf, size);
221*91f16700Schasinglulu 
222*91f16700Schasinglulu 	inv_dcache_range(buf, size);
223*91f16700Schasinglulu 
224*91f16700Schasinglulu 	return count;
225*91f16700Schasinglulu }
226*91f16700Schasinglulu 
227*91f16700Schasinglulu static struct io_block_dev_spec uniphier_nand_dev_spec = {
228*91f16700Schasinglulu 	.ops = {
229*91f16700Schasinglulu 		.read = uniphier_nand_read,
230*91f16700Schasinglulu 	},
231*91f16700Schasinglulu 	/* fill .block_size at run-time */
232*91f16700Schasinglulu };
233*91f16700Schasinglulu 
234*91f16700Schasinglulu static int uniphier_nand_hw_init(struct uniphier_nand *nand)
235*91f16700Schasinglulu {
236*91f16700Schasinglulu 	int i;
237*91f16700Schasinglulu 
238*91f16700Schasinglulu 	for (i = 0; i < ARRAY_SIZE(nand->bbt); i++)
239*91f16700Schasinglulu 		nand->bbt[i] = UNIPHIER_NAND_BBT_UNKNOWN;
240*91f16700Schasinglulu 
241*91f16700Schasinglulu 	nand->reg_base = nand->host_base + 0x100000;
242*91f16700Schasinglulu 
243*91f16700Schasinglulu 	nand->pages_per_block =
244*91f16700Schasinglulu 			mmio_read_32(nand->reg_base + DENALI_PAGES_PER_BLOCK);
245*91f16700Schasinglulu 
246*91f16700Schasinglulu 	nand->page_size =
247*91f16700Schasinglulu 		mmio_read_32(nand->reg_base + DENALI_DEVICE_MAIN_AREA_SIZE);
248*91f16700Schasinglulu 
249*91f16700Schasinglulu 	if (mmio_read_32(nand->reg_base + DENALI_TWO_ROW_ADDR_CYCLES) & BIT(0))
250*91f16700Schasinglulu 		nand->two_row_addr_cycles = 1;
251*91f16700Schasinglulu 
252*91f16700Schasinglulu 	uniphier_nand_host_write(nand, DENALI_MAP10,
253*91f16700Schasinglulu 				 DENALI_ACCESS_DEFAULT_AREA);
254*91f16700Schasinglulu 
255*91f16700Schasinglulu 	return 0;
256*91f16700Schasinglulu }
257*91f16700Schasinglulu 
258*91f16700Schasinglulu static const uintptr_t uniphier_nand_base[] = {
259*91f16700Schasinglulu 	[UNIPHIER_SOC_LD11] = 0x68000000,
260*91f16700Schasinglulu 	[UNIPHIER_SOC_LD20] = 0x68000000,
261*91f16700Schasinglulu 	[UNIPHIER_SOC_PXS3] = 0x68000000,
262*91f16700Schasinglulu };
263*91f16700Schasinglulu 
264*91f16700Schasinglulu int uniphier_nand_init(unsigned int soc,
265*91f16700Schasinglulu 		       struct io_block_dev_spec **block_dev_spec)
266*91f16700Schasinglulu {
267*91f16700Schasinglulu 	int ret;
268*91f16700Schasinglulu 
269*91f16700Schasinglulu 	assert(soc < ARRAY_SIZE(uniphier_nand_base));
270*91f16700Schasinglulu 	uniphier_nand.host_base = uniphier_nand_base[soc];
271*91f16700Schasinglulu 	if (!uniphier_nand.host_base)
272*91f16700Schasinglulu 		return -ENOTSUP;
273*91f16700Schasinglulu 
274*91f16700Schasinglulu 	ret = uniphier_nand_hw_init(&uniphier_nand);
275*91f16700Schasinglulu 	if (ret)
276*91f16700Schasinglulu 		return ret;
277*91f16700Schasinglulu 
278*91f16700Schasinglulu 	uniphier_nand_dev_spec.block_size = uniphier_nand.page_size;
279*91f16700Schasinglulu 
280*91f16700Schasinglulu 	*block_dev_spec = &uniphier_nand_dev_spec;
281*91f16700Schasinglulu 
282*91f16700Schasinglulu 	return 0;
283*91f16700Schasinglulu }
284