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