2 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
4 * SPDX-License-Identifier: BSD-3-Clause
7 #include <arch_helpers.h>
9 #include <io/io_block.h>
11 #include <platform_def.h>
13 #include <utils_def.h>
17 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
19 #define NAND_CMD_READ0 0
20 #define NAND_CMD_READSTART 0x30
22 #define DENALI_ECC_ENABLE 0x0e0
23 #define DENALI_PAGES_PER_BLOCK 0x150
24 #define DENALI_DEVICE_MAIN_AREA_SIZE 0x170
25 #define DENALI_DEVICE_SPARE_AREA_SIZE 0x180
26 #define DENALI_TWO_ROW_ADDR_CYCLES 0x190
27 #define DENALI_INTR_STATUS0 0x410
28 #define DENALI_INTR_ECC_UNCOR_ERR BIT(1)
29 #define DENALI_INTR_DMA_CMD_COMP BIT(2)
30 #define DENALI_INTR_INT_ACT BIT(12)
32 #define DENALI_DMA_ENABLE 0x700
34 #define DENALI_HOST_ADDR 0x00
35 #define DENALI_HOST_DATA 0x10
37 #define DENALI_MAP01 (1 << 26)
38 #define DENALI_MAP10 (2 << 26)
39 #define DENALI_MAP11 (3 << 26)
41 #define DENALI_MAP11_CMD ((DENALI_MAP11) | 0)
42 #define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1)
43 #define DENALI_MAP11_DATA ((DENALI_MAP11) | 2)
45 #define DENALI_ACCESS_DEFAULT_AREA 0x42
47 #define UNIPHIER_NAND_BBT_UNKNOWN 0xff
49 struct uniphier_nand
{
54 int two_row_addr_cycles
;
58 struct uniphier_nand uniphier_nand
;
60 static void uniphier_nand_host_write(struct uniphier_nand
*nand
,
61 uint32_t addr
, uint32_t data
)
63 mmio_write_32(nand
->host_base
+ DENALI_HOST_ADDR
, addr
);
64 mmio_write_32(nand
->host_base
+ DENALI_HOST_DATA
, data
);
67 static uint32_t uniphier_nand_host_read(struct uniphier_nand
*nand
,
70 mmio_write_32(nand
->host_base
+ DENALI_HOST_ADDR
, addr
);
71 return mmio_read_32(nand
->host_base
+ DENALI_HOST_DATA
);
74 static int uniphier_nand_block_isbad(struct uniphier_nand
*nand
, int block
)
76 int page
= nand
->pages_per_block
* block
;
77 int column
= nand
->page_size
;
82 /* use cache if available */
83 if (block
< ARRAY_SIZE(nand
->bbt
) &&
84 nand
->bbt
[block
] != UNIPHIER_NAND_BBT_UNKNOWN
)
85 return nand
->bbt
[block
];
87 mmio_write_32(nand
->reg_base
+ DENALI_ECC_ENABLE
, 0);
89 mmio_write_32(nand
->reg_base
+ DENALI_INTR_STATUS0
, -1);
91 uniphier_nand_host_write(nand
, DENALI_MAP11_CMD
, NAND_CMD_READ0
);
92 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
, column
& 0xff);
93 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
, (column
>> 8) & 0xff);
94 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
, page
& 0xff);
95 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
, (page
>> 8) & 0xff);
96 if (!nand
->two_row_addr_cycles
)
97 uniphier_nand_host_write(nand
, DENALI_MAP11_ADDR
,
99 uniphier_nand_host_write(nand
, DENALI_MAP11_CMD
, NAND_CMD_READSTART
);
102 status
= mmio_read_32(nand
->reg_base
+ DENALI_INTR_STATUS0
);
103 } while (!(status
& DENALI_INTR_INT_ACT
));
105 bbm
= uniphier_nand_host_read(nand
, DENALI_MAP11_DATA
);
107 is_bad
= bbm
!= 0xff;
109 /* if possible, save the result for future re-use */
110 if (block
< ARRAY_SIZE(nand
->bbt
))
111 nand
->bbt
[block
] = is_bad
;
114 WARN("found bad block at %d. skip.\n", block
);
119 static int uniphier_nand_read_pages(struct uniphier_nand
*nand
, uintptr_t buf
,
120 int page_start
, int page_count
)
124 mmio_write_32(nand
->reg_base
+ DENALI_ECC_ENABLE
, 1);
125 mmio_write_32(nand
->reg_base
+ DENALI_DMA_ENABLE
, 1);
127 mmio_write_32(nand
->reg_base
+ DENALI_INTR_STATUS0
, -1);
129 /* use Data DMA (64bit) */
130 mmio_write_32(nand
->host_base
+ DENALI_HOST_ADDR
,
131 DENALI_MAP10
| page_start
);
134 * 1. setup transfer type, interrupt when complete,
135 * burst len = 64 bytes, the number of pages
137 mmio_write_32(nand
->host_base
+ DENALI_HOST_DATA
,
138 0x01002000 | (64 << 16) | page_count
);
140 /* 2. set memory low address */
141 mmio_write_32(nand
->host_base
+ DENALI_HOST_DATA
, buf
);
143 /* 3. set memory high address */
144 mmio_write_32(nand
->host_base
+ DENALI_HOST_DATA
, buf
>> 32);
147 status
= mmio_read_32(nand
->reg_base
+ DENALI_INTR_STATUS0
);
148 } while (!(status
& DENALI_INTR_DMA_CMD_COMP
));
150 mmio_write_32(nand
->reg_base
+ DENALI_DMA_ENABLE
, 0);
152 if (status
& DENALI_INTR_ECC_UNCOR_ERR
) {
153 ERROR("uncorrectable error in page range %d-%d",
154 page_start
, page_start
+ page_count
- 1);
161 static size_t __uniphier_nand_read(struct uniphier_nand
*nand
, int lba
,
162 uintptr_t buf
, size_t size
)
164 int pages_per_block
= nand
->pages_per_block
;
165 int page_size
= nand
->page_size
;
166 int blocks_to_skip
= lba
/ pages_per_block
;
167 int pages_to_read
= DIV_ROUND_UP(size
, page_size
);
168 int page
= lba
% pages_per_block
;
173 while (blocks_to_skip
) {
174 ret
= uniphier_nand_block_isbad(nand
, block
);
184 while (pages_to_read
) {
185 ret
= uniphier_nand_block_isbad(nand
, block
);
194 page_count
= MIN(pages_per_block
- page
, pages_to_read
);
196 ret
= uniphier_nand_read_pages(nand
, p
,
197 block
* pages_per_block
+ page
,
204 p
+= page_size
* page_count
;
205 pages_to_read
-= page_count
;
209 /* number of read bytes */
210 return MIN(size
, p
- buf
);
213 static size_t uniphier_nand_read(int lba
, uintptr_t buf
, size_t size
)
217 inv_dcache_range(buf
, size
);
219 count
= __uniphier_nand_read(&uniphier_nand
, lba
, buf
, size
);
221 inv_dcache_range(buf
, size
);
226 static struct io_block_dev_spec uniphier_nand_dev_spec
= {
228 .offset
= UNIPHIER_BLOCK_BUF_BASE
,
229 .length
= UNIPHIER_BLOCK_BUF_SIZE
,
232 .read
= uniphier_nand_read
,
234 /* fill .block_size at run-time */
237 static int uniphier_nand_hw_init(struct uniphier_nand
*nand
)
241 for (i
= 0; i
< ARRAY_SIZE(nand
->bbt
); i
++)
242 nand
->bbt
[i
] = UNIPHIER_NAND_BBT_UNKNOWN
;
244 nand
->host_base
= 0x68000000;
245 nand
->reg_base
= 0x68100000;
247 nand
->pages_per_block
=
248 mmio_read_32(nand
->reg_base
+ DENALI_PAGES_PER_BLOCK
);
251 mmio_read_32(nand
->reg_base
+ DENALI_DEVICE_MAIN_AREA_SIZE
);
253 if (mmio_read_32(nand
->reg_base
+ DENALI_TWO_ROW_ADDR_CYCLES
) & BIT(0))
254 nand
->two_row_addr_cycles
= 1;
256 uniphier_nand_host_write(nand
, DENALI_MAP10
,
257 DENALI_ACCESS_DEFAULT_AREA
);
262 int uniphier_nand_init(uintptr_t *block_dev_spec
)
266 ret
= uniphier_nand_hw_init(&uniphier_nand
);
270 uniphier_nand_dev_spec
.block_size
= uniphier_nand
.page_size
;
272 *block_dev_spec
= (uintptr_t)&uniphier_nand_dev_spec
;