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