2 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
4 * SPDX-License-Identifier: BSD-3-Clause
9 #include <platform_def.h>
11 #include <arch_helpers.h>
12 #include <common/debug.h>
13 #include <drivers/io/io_block.h>
15 #include <lib/utils_def.h>
19 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
21 #define NAND_CMD_READ0 0
22 #define NAND_CMD_READSTART 0x30
24 #define DENALI_ECC_ENABLE 0x0e0
25 #define DENALI_PAGES_PER_BLOCK 0x150
26 #define DENALI_DEVICE_MAIN_AREA_SIZE 0x170
27 #define DENALI_DEVICE_SPARE_AREA_SIZE 0x180
28 #define DENALI_TWO_ROW_ADDR_CYCLES 0x190
29 #define DENALI_INTR_STATUS0 0x410
30 #define DENALI_INTR_ECC_UNCOR_ERR BIT(1)
31 #define DENALI_INTR_DMA_CMD_COMP BIT(2)
32 #define DENALI_INTR_INT_ACT BIT(12)
34 #define DENALI_DMA_ENABLE 0x700
36 #define DENALI_HOST_ADDR 0x00
37 #define DENALI_HOST_DATA 0x10
39 #define DENALI_MAP01 (1 << 26)
40 #define DENALI_MAP10 (2 << 26)
41 #define DENALI_MAP11 (3 << 26)
43 #define DENALI_MAP11_CMD ((DENALI_MAP11) | 0)
44 #define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1)
45 #define DENALI_MAP11_DATA ((DENALI_MAP11) | 2)
47 #define DENALI_ACCESS_DEFAULT_AREA 0x42
49 #define UNIPHIER_NAND_BBT_UNKNOWN 0xff
51 struct uniphier_nand
{
56 int two_row_addr_cycles
;
60 struct uniphier_nand uniphier_nand
;
62 static void uniphier_nand_host_write(struct uniphier_nand
*nand
,
63 uint32_t addr
, uint32_t data
)
65 mmio_write_32(nand
->host_base
+ DENALI_HOST_ADDR
, addr
);
66 mmio_write_32(nand
->host_base
+ DENALI_HOST_DATA
, data
);
69 static uint32_t uniphier_nand_host_read(struct uniphier_nand
*nand
,
72 mmio_write_32(nand
->host_base
+ DENALI_HOST_ADDR
, addr
);
73 return mmio_read_32(nand
->host_base
+ DENALI_HOST_DATA
);
76 static int uniphier_nand_block_isbad(struct uniphier_nand
*nand
, int block
)
78 int page
= nand
->pages_per_block
* block
;
79 int column
= nand
->page_size
;
84 /* use cache if available */
85 if (block
< ARRAY_SIZE(nand
->bbt
) &&
86 nand
->bbt
[block
] != UNIPHIER_NAND_BBT_UNKNOWN
)
87 return nand
->bbt
[block
];
89 mmio_write_32(nand
->reg_base
+ DENALI_ECC_ENABLE
, 0);
91 mmio_write_32(nand
->reg_base
+ DENALI_INTR_STATUS0
, -1);
93 uniphier_nand_host_write(nand
, DENALI_MAP11_CMD
, NAND_CMD_READ0
);
94 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
, column
& 0xff);
95 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
, (column
>> 8) & 0xff);
96 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
, page
& 0xff);
97 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
, (page
>> 8) & 0xff);
98 if (!nand
->two_row_addr_cycles
)
99 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
,
100 (page
>> 16) & 0xff);
101 uniphier_nand_host_write(nand
, DENALI_MAP11_CMD
, NAND_CMD_READSTART
);
104 status
= mmio_read_32(nand
->reg_base
+ DENALI_INTR_STATUS0
);
105 } while (!(status
& DENALI_INTR_INT_ACT
));
107 bbm
= uniphier_nand_host_read(nand
, DENALI_MAP11_DATA
);
109 is_bad
= bbm
!= 0xff;
111 /* if possible, save the result for future re-use */
112 if (block
< ARRAY_SIZE(nand
->bbt
))
113 nand
->bbt
[block
] = is_bad
;
116 WARN("found bad block at %d. skip.\n", block
);
121 static int uniphier_nand_read_pages(struct uniphier_nand
*nand
, uintptr_t buf
,
122 int page_start
, int page_count
)
126 mmio_write_32(nand
->reg_base
+ DENALI_ECC_ENABLE
, 1);
127 mmio_write_32(nand
->reg_base
+ DENALI_DMA_ENABLE
, 1);
129 mmio_write_32(nand
->reg_base
+ DENALI_INTR_STATUS0
, -1);
131 /* use Data DMA (64bit) */
132 mmio_write_32(nand
->host_base
+ DENALI_HOST_ADDR
,
133 DENALI_MAP10
| page_start
);
136 * 1. setup transfer type, interrupt when complete,
137 * burst len = 64 bytes, the number of pages
139 mmio_write_32(nand
->host_base
+ DENALI_HOST_DATA
,
140 0x01002000 | (64 << 16) | page_count
);
142 /* 2. set memory low address */
143 mmio_write_32(nand
->host_base
+ DENALI_HOST_DATA
, buf
);
145 /* 3. set memory high address */
146 mmio_write_32(nand
->host_base
+ DENALI_HOST_DATA
, buf
>> 32);
149 status
= mmio_read_32(nand
->reg_base
+ DENALI_INTR_STATUS0
);
150 } while (!(status
& DENALI_INTR_DMA_CMD_COMP
));
152 mmio_write_32(nand
->reg_base
+ DENALI_DMA_ENABLE
, 0);
154 if (status
& DENALI_INTR_ECC_UNCOR_ERR
) {
155 ERROR("uncorrectable error in page range %d-%d",
156 page_start
, page_start
+ page_count
- 1);
163 static size_t __uniphier_nand_read(struct uniphier_nand
*nand
, int lba
,
164 uintptr_t buf
, size_t size
)
166 int pages_per_block
= nand
->pages_per_block
;
167 int page_size
= nand
->page_size
;
168 int blocks_to_skip
= lba
/ pages_per_block
;
169 int pages_to_read
= DIV_ROUND_UP(size
, page_size
);
170 int page
= lba
% pages_per_block
;
175 while (blocks_to_skip
) {
176 ret
= uniphier_nand_block_isbad(nand
, block
);
186 while (pages_to_read
) {
187 ret
= uniphier_nand_block_isbad(nand
, block
);
196 page_count
= MIN(pages_per_block
- page
, pages_to_read
);
198 ret
= uniphier_nand_read_pages(nand
, p
,
199 block
* pages_per_block
+ page
,
206 p
+= page_size
* page_count
;
207 pages_to_read
-= page_count
;
211 /* number of read bytes */
212 return MIN(size
, p
- buf
);
215 static size_t uniphier_nand_read(int lba
, uintptr_t buf
, size_t size
)
219 inv_dcache_range(buf
, size
);
221 count
= __uniphier_nand_read(&uniphier_nand
, lba
, buf
, size
);
223 inv_dcache_range(buf
, size
);
228 static struct io_block_dev_spec uniphier_nand_dev_spec
= {
230 .offset
= UNIPHIER_BLOCK_BUF_BASE
,
231 .length
= UNIPHIER_BLOCK_BUF_SIZE
,
234 .read
= uniphier_nand_read
,
236 /* fill .block_size at run-time */
239 static int uniphier_nand_hw_init(struct uniphier_nand
*nand
)
243 for (i
= 0; i
< ARRAY_SIZE(nand
->bbt
); i
++)
244 nand
->bbt
[i
] = UNIPHIER_NAND_BBT_UNKNOWN
;
246 nand
->host_base
= 0x68000000;
247 nand
->reg_base
= 0x68100000;
249 nand
->pages_per_block
=
250 mmio_read_32(nand
->reg_base
+ DENALI_PAGES_PER_BLOCK
);
253 mmio_read_32(nand
->reg_base
+ DENALI_DEVICE_MAIN_AREA_SIZE
);
255 if (mmio_read_32(nand
->reg_base
+ DENALI_TWO_ROW_ADDR_CYCLES
) & BIT(0))
256 nand
->two_row_addr_cycles
= 1;
258 uniphier_nand_host_write(nand
, DENALI_MAP10
,
259 DENALI_ACCESS_DEFAULT_AREA
);
264 int uniphier_nand_init(uintptr_t *block_dev_spec
)
268 ret
= uniphier_nand_hw_init(&uniphier_nand
);
272 uniphier_nand_dev_spec
.block_size
= uniphier_nand
.page_size
;
274 *block_dev_spec
= (uintptr_t)&uniphier_nand_dev_spec
;