blob: c2f4f2cbf72b1e6d4fa2a2df815f6fd4939f88a6 [file] [log] [blame]
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +02001/*
2 * Copyright (c) 2014-2015, Antmicro Ltd <www.antmicro.com>
3 * Copyright (c) 2015, AW-SOM Technologies <www.aw-som.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 */
7
Hans de Goede466a3182015-08-15 11:59:25 +02008#include <asm/arch/clock.h>
9#include <asm/io.h>
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +020010#include <common.h>
11#include <config.h>
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +020012#include <nand.h>
13
14/* registers */
15#define NFC_CTL 0x00000000
16#define NFC_ST 0x00000004
17#define NFC_INT 0x00000008
18#define NFC_TIMING_CTL 0x0000000C
19#define NFC_TIMING_CFG 0x00000010
20#define NFC_ADDR_LOW 0x00000014
21#define NFC_ADDR_HIGH 0x00000018
22#define NFC_SECTOR_NUM 0x0000001C
23#define NFC_CNT 0x00000020
24#define NFC_CMD 0x00000024
25#define NFC_RCMD_SET 0x00000028
26#define NFC_WCMD_SET 0x0000002C
27#define NFC_IO_DATA 0x00000030
28#define NFC_ECC_CTL 0x00000034
29#define NFC_ECC_ST 0x00000038
30#define NFC_DEBUG 0x0000003C
31#define NFC_ECC_CNT0 0x00000040
32#define NFC_ECC_CNT1 0x00000044
33#define NFC_ECC_CNT2 0x00000048
34#define NFC_ECC_CNT3 0x0000004C
35#define NFC_USER_DATA_BASE 0x00000050
36#define NFC_EFNAND_STATUS 0x00000090
37#define NFC_SPARE_AREA 0x000000A0
38#define NFC_PATTERN_ID 0x000000A4
39#define NFC_RAM0_BASE 0x00000400
40#define NFC_RAM1_BASE 0x00000800
41
42#define NFC_CTL_EN (1 << 0)
43#define NFC_CTL_RESET (1 << 1)
44#define NFC_CTL_RAM_METHOD (1 << 14)
Hans de Goedef8511202015-08-15 20:05:13 +020045#define NFC_CTL_PAGE_SIZE_MASK (0xf << 8)
46#define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +020047
48
49#define NFC_ECC_EN (1 << 0)
50#define NFC_ECC_PIPELINE (1 << 3)
51#define NFC_ECC_EXCEPTION (1 << 4)
52#define NFC_ECC_BLOCK_SIZE (1 << 5)
53#define NFC_ECC_RANDOM_EN (1 << 9)
54#define NFC_ECC_RANDOM_DIRECTION (1 << 10)
55
56
57#define NFC_ADDR_NUM_OFFSET 16
Miquel Raynala450de72018-02-28 20:51:46 +010058#define NFC_SEND_ADDR (1 << 19)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +020059#define NFC_ACCESS_DIR (1 << 20)
60#define NFC_DATA_TRANS (1 << 21)
61#define NFC_SEND_CMD1 (1 << 22)
62#define NFC_WAIT_FLAG (1 << 23)
63#define NFC_SEND_CMD2 (1 << 24)
64#define NFC_SEQ (1 << 25)
65#define NFC_DATA_SWAP_METHOD (1 << 26)
66#define NFC_ROW_AUTO_INC (1 << 27)
67#define NFC_SEND_CMD3 (1 << 28)
68#define NFC_SEND_CMD4 (1 << 29)
Boris Brezillon87cc16c2016-06-06 10:17:01 +020069#define NFC_RAW_CMD (0 << 30)
70#define NFC_PAGE_CMD (2 << 30)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +020071
Boris Brezillon353f3d02015-08-29 12:29:38 +020072#define NFC_ST_CMD_INT_FLAG (1 << 1)
73#define NFC_ST_DMA_INT_FLAG (1 << 2)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +020074
75#define NFC_READ_CMD_OFFSET 0
76#define NFC_RANDOM_READ_CMD0_OFFSET 8
77#define NFC_RANDOM_READ_CMD1_OFFSET 16
78
79#define NFC_CMD_RNDOUTSTART 0xE0
80#define NFC_CMD_RNDOUT 0x05
81#define NFC_CMD_READSTART 0x30
82
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +020083#define SUNXI_DMA_CFG_REG0 0x300
84#define SUNXI_DMA_SRC_START_ADDR_REG0 0x304
85#define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308
86#define SUNXI_DMA_DDMA_BC_REG0 0x30C
87#define SUNXI_DMA_DDMA_PARA_REG0 0x318
88
89#define SUNXI_DMA_DDMA_CFG_REG_LOADING (1 << 31)
90#define SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25)
Hans de Goede534e6072015-08-15 09:33:41 +020091#define SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM (1 << 16)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +020092#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9)
93#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5)
94#define SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0)
95
96#define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0)
97#define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8)
98
Boris Brezillon87cc16c2016-06-06 10:17:01 +020099struct nfc_config {
100 int page_size;
101 int ecc_strength;
102 int ecc_size;
103 int addr_cycles;
104 int nseeds;
105 bool randomize;
Boris Brezillon601361f2016-06-06 10:17:02 +0200106 bool valid;
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200107};
108
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200109/* minimal "boot0" style NAND support for Allwinner A20 */
110
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200111/* random seed used by linux */
112const uint16_t random_seed[128] = {
113 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
114 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
115 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
116 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
117 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
118 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
119 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
120 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
121 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
122 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
123 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
124 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
125 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
126 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
127 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
128 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
129};
130
Boris Brezillondbd87102016-06-06 10:17:00 +0200131#define DEFAULT_TIMEOUT_US 100000
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200132
133static int check_value_inner(int offset, int expected_bits,
Boris Brezillondbd87102016-06-06 10:17:00 +0200134 int timeout_us, int negation)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200135{
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200136 do {
137 int val = readl(offset) & expected_bits;
138 if (negation ? !val : val)
139 return 1;
Boris Brezillondbd87102016-06-06 10:17:00 +0200140 udelay(1);
141 } while (--timeout_us);
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200142
143 return 0;
144}
145
146static inline int check_value(int offset, int expected_bits,
Boris Brezillondbd87102016-06-06 10:17:00 +0200147 int timeout_us)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200148{
Boris Brezillondbd87102016-06-06 10:17:00 +0200149 return check_value_inner(offset, expected_bits, timeout_us, 0);
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200150}
151
152static inline int check_value_negated(int offset, int unexpected_bits,
Boris Brezillondbd87102016-06-06 10:17:00 +0200153 int timeout_us)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200154{
Boris Brezillondbd87102016-06-06 10:17:00 +0200155 return check_value_inner(offset, unexpected_bits, timeout_us, 1);
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200156}
157
Miquel Raynalb0b98ca2018-02-28 20:51:47 +0100158static int nand_wait_int(void)
159{
160 if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG,
161 DEFAULT_TIMEOUT_US)) {
162 printf("nand: timeout waiting for interruption\n");
163 return -ETIMEDOUT;
164 }
165
166 return 0;
167}
168
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200169void nand_init(void)
170{
171 uint32_t val;
172
Hans de Goede5ed52f62015-08-15 11:55:26 +0200173 board_nand_init();
174
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200175 val = readl(SUNXI_NFC_BASE + NFC_CTL);
176 /* enable and reset CTL */
177 writel(val | NFC_CTL_EN | NFC_CTL_RESET,
178 SUNXI_NFC_BASE + NFC_CTL);
179
180 if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL,
Boris Brezillondbd87102016-06-06 10:17:00 +0200181 NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) {
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200182 printf("Couldn't initialize nand\n");
183 }
Hans de Goede7c7fbfb2015-08-15 11:38:33 +0200184
185 /* reset NAND */
Boris Brezillon353f3d02015-08-29 12:29:38 +0200186 writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
Hans de Goede7c7fbfb2015-08-15 11:38:33 +0200187 writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET,
188 SUNXI_NFC_BASE + NFC_CMD);
189
Miquel Raynalb0b98ca2018-02-28 20:51:47 +0100190 nand_wait_int();
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200191}
192
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200193static void nand_apply_config(const struct nfc_config *conf)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200194{
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200195 u32 val;
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200196
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200197 val = readl(SUNXI_NFC_BASE + NFC_CTL);
198 val &= ~NFC_CTL_PAGE_SIZE_MASK;
199 writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size),
200 SUNXI_NFC_BASE + NFC_CTL);
201 writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
202 writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA);
203}
204
205static int nand_load_page(const struct nfc_config *conf, u32 offs)
206{
207 int page = offs / conf->page_size;
208
209 writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
210 (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
211 (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET),
212 SUNXI_NFC_BASE + NFC_RCMD_SET);
213 writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW);
214 writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH);
215 writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
216 writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | NFC_WAIT_FLAG |
Miquel Raynala450de72018-02-28 20:51:46 +0100217 ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR,
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200218 SUNXI_NFC_BASE + NFC_CMD);
219
Miquel Raynalb0b98ca2018-02-28 20:51:47 +0100220 return nand_wait_int();
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200221}
222
Boris Brezillon601361f2016-06-06 10:17:02 +0200223static int nand_reset_column(void)
224{
225 writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
226 (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
227 (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET),
228 SUNXI_NFC_BASE + NFC_RCMD_SET);
229 writel(0, SUNXI_NFC_BASE + NFC_ADDR_LOW);
230 writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
Miquel Raynala450de72018-02-28 20:51:46 +0100231 (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR | NFC_CMD_RNDOUT,
Boris Brezillon601361f2016-06-06 10:17:02 +0200232 SUNXI_NFC_BASE + NFC_CMD);
233
Miquel Raynalb0b98ca2018-02-28 20:51:47 +0100234 return nand_wait_int();
Boris Brezillon601361f2016-06-06 10:17:02 +0200235}
236
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200237static int nand_read_page(const struct nfc_config *conf, u32 offs,
238 void *dest, int len)
239{
240 dma_addr_t dst = (dma_addr_t)dest;
241 int nsectors = len / conf->ecc_size;
Maxime Ripardcccddf52017-02-27 18:22:00 +0100242 u16 rand_seed = 0;
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200243 u32 val;
244 int page;
245
246 page = offs / conf->page_size;
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200247
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200248 if (offs % conf->page_size || len % conf->ecc_size ||
249 len > conf->page_size || len < 0)
250 return -EINVAL;
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200251
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200252 /* clear ecc status */
253 writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
254
Maxime Ripardcccddf52017-02-27 18:22:00 +0100255 /* Choose correct seed if randomized */
256 if (conf->randomize)
257 rand_seed = random_seed[page % conf->nseeds];
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200258
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200259 writel((rand_seed << 16) | (conf->ecc_strength << 12) |
260 (conf->randomize ? NFC_ECC_RANDOM_EN : 0) |
261 (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) |
262 NFC_ECC_EN | NFC_ECC_PIPELINE | NFC_ECC_EXCEPTION,
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200263 SUNXI_NFC_BASE + NFC_ECC_CTL);
264
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200265 flush_dcache_range(dst, ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
Hans de Goede4de74a42015-08-15 12:32:24 +0200266
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200267 /* SUNXI_DMA */
268 writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */
269 /* read from REG_IO_DATA */
270 writel(SUNXI_NFC_BASE + NFC_IO_DATA,
271 SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0);
272 /* read to RAM */
Hans de Goede534e6072015-08-15 09:33:41 +0200273 writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0);
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200274 writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC |
275 SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE,
276 SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0);
277 writel(len, SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0);
278 writel(SUNXI_DMA_DDMA_CFG_REG_LOADING |
279 SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 |
280 SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM |
281 SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 |
282 SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO |
283 SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC,
284 SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0);
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200285
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200286 writel(nsectors, SUNXI_NFC_BASE + NFC_SECTOR_NUM);
Boris Brezillon353f3d02015-08-29 12:29:38 +0200287 writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200288 writel(NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_DATA_SWAP_METHOD,
289 SUNXI_NFC_BASE + NFC_CMD);
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200290
Boris Brezillon353f3d02015-08-29 12:29:38 +0200291 if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG,
Boris Brezillondbd87102016-06-06 10:17:00 +0200292 DEFAULT_TIMEOUT_US)) {
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200293 printf("Error while initializing dma interrupt\n");
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200294 return -EIO;
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200295 }
Boris Brezillon353f3d02015-08-29 12:29:38 +0200296 writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200297
298 if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0,
Boris Brezillondbd87102016-06-06 10:17:00 +0200299 SUNXI_DMA_DDMA_CFG_REG_LOADING,
300 DEFAULT_TIMEOUT_US)) {
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200301 printf("Error while waiting for dma transfer to finish\n");
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200302 return -EIO;
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200303 }
304
Hans de Goede4de74a42015-08-15 12:32:24 +0200305 invalidate_dcache_range(dst,
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200306 ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
Hans de Goede4de74a42015-08-15 12:32:24 +0200307
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200308 val = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
Hans de Goedea44f40e2015-08-15 20:51:53 +0200309
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200310 /* ECC error detected. */
311 if (val & 0xffff)
312 return -EIO;
313
314 /*
315 * Return 1 if the page is empty.
316 * We consider the page as empty if the first ECC block is marked
317 * empty.
318 */
319 return (val & 0x10000) ? 1 : 0;
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200320}
321
Boris Brezillon601361f2016-06-06 10:17:02 +0200322static int nand_max_ecc_strength(struct nfc_config *conf)
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200323{
Boris Brezillon601361f2016-06-06 10:17:02 +0200324 static const int ecc_bytes[] = { 32, 46, 54, 60, 74, 88, 102, 110, 116 };
325 int max_oobsize, max_ecc_bytes;
326 int nsectors = conf->page_size / conf->ecc_size;
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200327 int i;
328
Boris Brezillon601361f2016-06-06 10:17:02 +0200329 /*
330 * ECC strength is limited by the size of the OOB area which is
331 * correlated with the page size.
332 */
333 switch (conf->page_size) {
334 case 2048:
335 max_oobsize = 64;
336 break;
337 case 4096:
338 max_oobsize = 256;
339 break;
340 case 8192:
341 max_oobsize = 640;
342 break;
343 case 16384:
344 max_oobsize = 1664;
345 break;
346 default:
347 return -EINVAL;
348 }
349
350 max_ecc_bytes = max_oobsize / nsectors;
351
352 for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) {
353 if (ecc_bytes[i] > max_ecc_bytes)
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200354 break;
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200355 }
356
Boris Brezillon601361f2016-06-06 10:17:02 +0200357 if (!i)
358 return -EINVAL;
Boris Brezillon87cc16c2016-06-06 10:17:01 +0200359
Boris Brezillon601361f2016-06-06 10:17:02 +0200360 return i - 1;
361}
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200362
Boris Brezillon601361f2016-06-06 10:17:02 +0200363static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs,
364 void *dest)
365{
366 /* NAND with pages > 4k will likely require 1k sector size. */
367 int min_ecc_size = conf->page_size > 4096 ? 1024 : 512;
368 int page = offs / conf->page_size;
369 int ret;
Hans de Goedef8511202015-08-15 20:05:13 +0200370
Boris Brezillon601361f2016-06-06 10:17:02 +0200371 /*
372 * In most cases, 1k sectors are preferred over 512b ones, start
373 * testing this config first.
374 */
375 for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size;
376 conf->ecc_size >>= 1) {
377 int max_ecc_strength = nand_max_ecc_strength(conf);
378
379 nand_apply_config(conf);
380
381 /*
382 * We are starting from the maximum ECC strength because
383 * most of the time NAND vendors provide an OOB area that
384 * barely meets the ECC requirements.
385 */
386 for (conf->ecc_strength = max_ecc_strength;
387 conf->ecc_strength >= 0;
388 conf->ecc_strength--) {
389 conf->randomize = false;
390 if (nand_reset_column())
391 return -EIO;
392
393 /*
394 * Only read the first sector to speedup detection.
395 */
396 ret = nand_read_page(conf, offs, dest, conf->ecc_size);
397 if (!ret) {
398 return 0;
399 } else if (ret > 0) {
400 /*
401 * If page is empty we can't deduce anything
402 * about the ECC config => stop the detection.
403 */
404 return -EINVAL;
405 }
406
407 conf->randomize = true;
408 conf->nseeds = ARRAY_SIZE(random_seed);
409 do {
410 if (nand_reset_column())
411 return -EIO;
412
413 if (!nand_read_page(conf, offs, dest,
414 conf->ecc_size))
415 return 0;
416
417 /*
418 * Find the next ->nseeds value that would
419 * change the randomizer seed for the page
420 * we're trying to read.
421 */
422 while (conf->nseeds >= 16) {
423 int seed = page % conf->nseeds;
424
425 conf->nseeds >>= 1;
426 if (seed != page % conf->nseeds)
427 break;
428 }
429 } while (conf->nseeds >= 16);
430 }
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200431 }
Hans de Goedea44f40e2015-08-15 20:51:53 +0200432
Boris Brezillon601361f2016-06-06 10:17:02 +0200433 return -EINVAL;
Hans de Goedea44f40e2015-08-15 20:51:53 +0200434}
435
Boris Brezillon601361f2016-06-06 10:17:02 +0200436static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest)
Hans de Goedea44f40e2015-08-15 20:51:53 +0200437{
Boris Brezillon601361f2016-06-06 10:17:02 +0200438 if (conf->valid)
439 return 0;
Hans de Goede01c69ed2015-08-15 21:23:08 +0200440
Boris Brezillon601361f2016-06-06 10:17:02 +0200441 /*
442 * Modern NANDs are more likely than legacy ones, so we start testing
443 * with 5 address cycles.
444 */
445 for (conf->addr_cycles = 5;
446 conf->addr_cycles >= 4;
447 conf->addr_cycles--) {
448 int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384;
449
450 /*
451 * Ignoring 1k pages cause I'm not even sure this case exist
452 * in the real world.
453 */
454 for (conf->page_size = 2048; conf->page_size <= max_page_size;
455 conf->page_size <<= 1) {
456 if (nand_load_page(conf, offs))
457 return -1;
458
459 if (!nand_detect_ecc_config(conf, offs, dest)) {
460 conf->valid = true;
Hans de Goede01c69ed2015-08-15 21:23:08 +0200461 return 0;
462 }
Hans de Goede01c69ed2015-08-15 21:23:08 +0200463 }
Hans de Goede01c69ed2015-08-15 21:23:08 +0200464 }
465
Boris Brezillon601361f2016-06-06 10:17:02 +0200466 return -EINVAL;
Hans de Goedea44f40e2015-08-15 20:51:53 +0200467}
468
Boris Brezillon601361f2016-06-06 10:17:02 +0200469static int nand_read_buffer(struct nfc_config *conf, uint32_t offs,
470 unsigned int size, void *dest)
471{
Miquel Raynale8595032018-02-28 20:51:45 +0100472 int first_seed = 0, page, ret;
Boris Brezillon601361f2016-06-06 10:17:02 +0200473
474 size = ALIGN(size, conf->page_size);
475 page = offs / conf->page_size;
Miquel Raynale8595032018-02-28 20:51:45 +0100476 if (conf->randomize)
477 first_seed = page % conf->nseeds;
Boris Brezillon601361f2016-06-06 10:17:02 +0200478
479 for (; size; size -= conf->page_size) {
480 if (nand_load_page(conf, offs))
481 return -1;
482
483 ret = nand_read_page(conf, offs, dest, conf->page_size);
484 /*
485 * The ->nseeds value should be equal to the number of pages
486 * in an eraseblock. Since we don't know this information in
487 * advance we might have picked a wrong value.
488 */
489 if (ret < 0 && conf->randomize) {
490 int cur_seed = page % conf->nseeds;
491
492 /*
493 * We already tried all the seed values => we are
494 * facing a real corruption.
495 */
496 if (cur_seed < first_seed)
497 return -EIO;
498
499 /* Try to adjust ->nseeds and read the page again... */
500 conf->nseeds = cur_seed;
501
502 if (nand_reset_column())
503 return -EIO;
504
505 /* ... it still fails => it's a real corruption. */
506 if (nand_read_page(conf, offs, dest, conf->page_size))
507 return -EIO;
508 } else if (ret && conf->randomize) {
509 memset(dest, 0xff, conf->page_size);
510 }
511
512 page++;
513 offs += conf->page_size;
514 dest += conf->page_size;
515 }
516
517 return 0;
518}
519
Hans de Goedea44f40e2015-08-15 20:51:53 +0200520int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
521{
Boris Brezillon601361f2016-06-06 10:17:02 +0200522 static struct nfc_config conf = { };
523 int ret;
524
525 ret = nand_detect_config(&conf, offs, dest);
526 if (ret)
527 return ret;
528
529 return nand_read_buffer(&conf, offs, size, dest);
Piotr Zierhoffer4ac391c2015-07-23 14:33:02 +0200530}
531
Hans de Goede466a3182015-08-15 11:59:25 +0200532void nand_deselect(void)
533{
534 struct sunxi_ccm_reg *const ccm =
535 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
536
537 clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
538#ifdef CONFIG_MACH_SUN9I
539 clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
540#else
541 clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
542#endif
543 clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
544}