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