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