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