blob: 71cb96c0a1fbf48d5bd59f373cb16b123cce44e8 [file] [log] [blame]
Masahiro Yamada574388c2016-09-03 11:37:40 +09001/*
Masahiro Yamada229e2112020-01-17 13:46:13 +09002 * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
Masahiro Yamada574388c2016-09-03 11:37:40 +09003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
Masahiro Yamada6ebf6382020-02-03 19:30:27 +09007#include <assert.h>
Antonio Nino Diaz4b32e622018-08-16 16:52:57 +01008#include <stdint.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +00009
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>
Masahiro Yamada574388c2016-09-03 11:37:40 +090017
18#include "uniphier.h"
19
Masahiro Yamada574388c2016-09-03 11:37:40 +090020#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
50struct 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
59struct uniphier_nand uniphier_nand;
60
61static 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
68static 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
75static 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
Masahiro Yamadae8da5c12017-06-15 09:32:12 +0900110 /* if possible, save the result for future re-use */
111 if (block < ARRAY_SIZE(nand->bbt))
Masahiro Yamada71ac5962017-08-31 19:58:11 +0900112 nand->bbt[block] = is_bad;
Masahiro Yamada574388c2016-09-03 11:37:40 +0900113
114 if (is_bad)
115 WARN("found bad block at %d. skip.\n", block);
116
117 return is_bad;
118}
119
120static 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
162static 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;
Masahiro Yamada837630f2019-07-26 20:07:08 +0900168 int pages_to_read = div_round_up(size, page_size);
Masahiro Yamada574388c2016-09-03 11:37:40 +0900169 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
209out:
210 /* number of read bytes */
211 return MIN(size, p - buf);
212}
213
214static 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
227static struct io_block_dev_spec uniphier_nand_dev_spec = {
Masahiro Yamada574388c2016-09-03 11:37:40 +0900228 .ops = {
229 .read = uniphier_nand_read,
230 },
231 /* fill .block_size at run-time */
232};
233
234static 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
Masahiro Yamada6ebf6382020-02-03 19:30:27 +0900241 nand->reg_base = nand->host_base + 0x100000;
Masahiro Yamada574388c2016-09-03 11:37:40 +0900242
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
Masahiro Yamada6ebf6382020-02-03 19:30:27 +0900258static const uintptr_t uniphier_nand_base[] = {
259 [UNIPHIER_SOC_LD11] = 0x68000000,
260 [UNIPHIER_SOC_LD20] = 0x68000000,
261 [UNIPHIER_SOC_PXS3] = 0x68000000,
262};
263
264int uniphier_nand_init(unsigned int soc,
265 struct io_block_dev_spec **block_dev_spec)
Masahiro Yamada574388c2016-09-03 11:37:40 +0900266{
267 int ret;
268
Masahiro Yamada6ebf6382020-02-03 19:30:27 +0900269 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
Masahiro Yamada574388c2016-09-03 11:37:40 +0900274 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
Masahiro Yamada229e2112020-01-17 13:46:13 +0900280 *block_dev_spec = &uniphier_nand_dev_spec;
Masahiro Yamada574388c2016-09-03 11:37:40 +0900281
282 return 0;
283}