]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/mtd/nand/sunxi_nand_spl.c
sunxi/nand: Add support to the SPL for loading u-boot from internal NAND memory
[karo-tx-uboot.git] / drivers / mtd / nand / sunxi_nand_spl.c
1 /*
2  * Copyright (c) 2014, Antmicro Ltd <www.antmicro.com>
3  * Copyright (c) 2015, Turtle Solutions <www.turtle-solutions.eu>
4  * Copyright (c) 2015, Roy Spliet <rspliet@ultimaker.com>
5  *
6  * SPDX-License-Identifier:     GPL-2.0+
7  *
8  * \todo Detect chip parameters (page size, ECC mode, randomisation...)
9  */
10
11 #include <common.h>
12 #include <config.h>
13 #include <asm/io.h>
14 #include <nand.h>
15 #include <asm/arch/cpu.h>
16 #include <asm/arch/clock.h>
17 #include <asm/arch/dma.h>
18 #include <asm/arch/nand.h>
19
20 void
21 nand_init(void)
22 {
23         struct sunxi_ccm_reg * const ccm =
24                         (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
25         struct sunxi_nand * const nand = (struct sunxi_nand *)SUNXI_NFC_BASE;
26         u32 val;
27
28         board_nand_init();
29
30         /* "un-gate" NAND clock and clock source
31          * This assumes that the clock was already correctly configured by
32          * BootROM */
33         setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0));
34 #ifdef CONFIG_MACH_SUN9I
35         setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
36 #else
37         setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
38 #endif
39         setbits_le32(&ccm->nand0_clk_cfg, 0x80000000);
40
41         val = readl(&nand->ctl);
42         val |= SUNXI_NAND_CTL_RST;
43         writel(val, &nand->ctl);
44
45         /* Wait until reset pin is deasserted */
46         do {
47                 val = readl(&nand->ctl);
48                 if (!(val & SUNXI_NAND_CTL_RST))
49                         break;
50         } while (1);
51
52         /** \todo Chip select, currently kind of static */
53         val = readl(&nand->ctl);
54         val &= 0xf0fff0f2;
55         val |= SUNXI_NAND_CTL_EN;
56         val |= SUNXI_NAND_CTL_PAGE_SIZE(CONFIG_NAND_SUNXI_PAGE_SIZE);
57         writel(val, &nand->ctl);
58
59         writel(0x100, &nand->timing_ctl);
60         writel(0x7ff, &nand->timing_cfg);
61
62         /* reset CMD  */
63         val = SUNXI_NAND_CMD_SEND_CMD1 | SUNXI_NAND_CMD_WAIT_FLAG |
64                         NAND_CMD_RESET;
65         writel(val, &nand->cmd);
66         do {
67                 val = readl(&nand->st);
68                 if (val & (1<<1))
69                         break;
70                 udelay(1000);
71         } while (1);
72
73         printf("Nand initialised\n");
74 }
75
76 int
77 nand_wait_timeout(u32 *reg, u32 mask, u32 val)
78 {
79         unsigned long tmo = timer_get_us() + 1000000; /* 1s */
80
81         while ((readl(reg) & mask) != val) {
82                 if (timer_get_us() > tmo)
83                         return -ETIMEDOUT;
84         }
85
86         return 0;
87 }
88
89 /* random seed */
90 static const uint16_t random_seed[128] = {
91         0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
92         0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
93         0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
94         0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
95         0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
96         0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
97         0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
98         0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
99         0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
100         0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
101         0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
102         0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
103         0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
104         0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
105         0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
106         0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
107 };
108
109 uint32_t ecc_errors = 0;
110
111 static void
112 nand_config_ecc(struct sunxi_nand *nand, uint32_t page, int syndrome)
113 {
114         static u8 strength[] = {16, 24, 28, 32, 40, 48, 56, 60, 64};
115         int i;
116         uint32_t ecc_mode;
117         u32 ecc;
118         u16 seed = 0;
119
120         for (i = 0; i < ARRAY_SIZE(strength); i++) {
121                 if (CONFIG_NAND_SUNXI_ECC_STRENGTH == strength[i]) {
122                         ecc_mode = i;
123                         break;
124                 }
125         }
126
127         if (i == ARRAY_SIZE(strength)) {
128                 printf("ECC strength unsupported\n");
129                 return;
130         }
131
132         ecc =   SUNXI_NAND_ECC_CTL_ECC_EN |
133                 SUNXI_NAND_ECC_CTL_PIPELINE |
134                 SUNXI_NAND_ECC_CTL_RND_EN |
135                 SUNXI_NAND_ECC_CTL_MODE(ecc_mode);
136
137         if (CONFIG_NAND_SUNXI_ECC_STEP == 512)
138                 ecc |= SUNXI_NAND_ECC_CTL_BS_512B;
139
140         if (syndrome)
141                 seed = 0x4A80;
142         else
143                 seed = random_seed[page % ARRAY_SIZE(random_seed)];
144
145         ecc |= SUNXI_NAND_ECC_CTL_RND_SEED(seed);
146
147         writel(ecc, &nand->ecc_ctl);
148 }
149
150 /* read CONFIG_NAND_SUNXI_ECC_STEP bytes from real_addr to temp_buf */
151 void
152 nand_read_block(struct sunxi_nand *nand, phys_addr_t src, dma_addr_t dst,
153                 int syndrome)
154 {
155         struct sunxi_dma * const dma = (struct sunxi_dma *)SUNXI_DMA_BASE;
156         struct sunxi_dma_cfg * const dma_cfg = &dma->ddma[0];
157
158         uint32_t shift;
159         uint32_t page;
160         uint32_t addr;
161         uint32_t oob_offset;
162         uint32_t ecc_bytes;
163         u32 val;
164         u32 cmd;
165
166         page = src / CONFIG_NAND_SUNXI_PAGE_SIZE;
167         if (page > 0xFFFF) {
168                 /* TODO: currently this is not supported */
169                 printf("Reading from address >= %08X is not allowed.\n",
170                        0xFFFF * CONFIG_NAND_SUNXI_PAGE_SIZE);
171                 return;
172         }
173
174         shift = src % CONFIG_NAND_SUNXI_PAGE_SIZE;
175         writel(0, &nand->ecc_st);
176
177         /* ECC_CTL, randomization */
178         ecc_bytes = CONFIG_NAND_SUNXI_ECC_STRENGTH *
179                         fls(CONFIG_NAND_SUNXI_ECC_STEP * 8);
180         ecc_bytes = DIV_ROUND_UP(ecc_bytes, 8);
181         ecc_bytes += (ecc_bytes & 1); /* Align to 2-bytes */
182         ecc_bytes += 4;
183
184         nand_config_ecc(nand, page, syndrome);
185         if (syndrome) {
186                 /* shift every 1kB in syndrome */
187                 shift += (shift / CONFIG_NAND_SUNXI_ECC_STEP) * ecc_bytes;
188                 oob_offset = CONFIG_NAND_SUNXI_ECC_STEP + shift;
189         } else {
190                 oob_offset = CONFIG_NAND_SUNXI_PAGE_SIZE  +
191                         (shift / CONFIG_NAND_SUNXI_ECC_STEP) * ecc_bytes;
192         }
193
194         addr = (page << 16) | shift;
195
196         /* DMA */
197         val = readl(&nand->ctl);
198         writel(val | SUNXI_NAND_CTL_RAM_METHOD_DMA, &nand->ctl);
199
200         writel(oob_offset, &nand->spare_area);
201
202         /* DMAC
203          * \todo Separate this into a tidy driver */
204         writel(0x0, &dma->irq_en); /* clear dma interrupts */
205         writel((uint32_t) &nand->io_data , &dma_cfg->src_addr);
206         writel(dst            , &dma_cfg->dst_addr);
207         writel(0x00007F0F     , &dma_cfg->ddma_para);
208         writel(CONFIG_NAND_SUNXI_ECC_STEP, &dma_cfg->bc);
209
210         val =   SUNXI_DMA_CTL_SRC_DRQ(DDMA_SRC_DRQ_NAND) |
211                 SUNXI_DMA_CTL_MODE_IO |
212                 SUNXI_DMA_CTL_SRC_DATA_WIDTH_32 |
213                 SUNXI_DMA_CTL_DST_DRQ(DDMA_DST_DRQ_SDRAM) |
214                 SUNXI_DMA_CTL_DST_DATA_WIDTH_32 |
215                 SUNXI_DMA_CTL_TRIGGER;
216         writel(val, &dma_cfg->ctl);
217
218         writel(0x00E00530, &nand->rcmd_set);
219         nand_wait_timeout(&nand->st, SUNXI_NAND_ST_FIFO_FULL, 0);
220
221         writel(1   , &nand->block_num);
222         writel(addr, &nand->addr_low);
223         writel(0   , &nand->addr_high);
224
225         /* CMD (PAGE READ) */
226         cmd = 0x85E80000;
227         cmd |= SUNXI_NAND_CMD_ADDR_CYCLES(CONFIG_NAND_SUNXI_ADDR_CYCLES);
228         cmd |= (syndrome ? SUNXI_NAND_CMD_ORDER_SEQ :
229                         SUNXI_NAND_CMD_ORDER_INTERLEAVE);
230         writel(cmd, &nand->cmd);
231
232         if(nand_wait_timeout(&nand->st, SUNXI_NAND_ST_DMA_INT,
233                         SUNXI_NAND_ST_DMA_INT)) {
234                 printf("NAND timeout reading data\n");
235                 return;
236         }
237
238         if(nand_wait_timeout(&dma_cfg->ctl, SUNXI_DMA_CTL_TRIGGER, 0)) {
239                 printf("NAND timeout reading data\n");
240                 return;
241         }
242
243         if (readl(&nand->ecc_st))
244                 ecc_errors++;
245 }
246
247 int
248 nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
249 {
250         struct sunxi_nand * const nand = (struct sunxi_nand *)SUNXI_NFC_BASE;
251         dma_addr_t dst_block;
252         dma_addr_t dst_end;
253         phys_addr_t addr = offs;
254
255         dst_end = ((dma_addr_t) dest) + size;
256
257         memset((void *)dest, 0x0, size);
258         ecc_errors = 0;
259         for (dst_block = (dma_addr_t) dest; dst_block < dst_end;
260                         dst_block += CONFIG_NAND_SUNXI_ECC_STEP,
261                         addr += CONFIG_NAND_SUNXI_ECC_STEP) {
262                 /* syndrome read first 4MiB to match Allwinner BootROM */
263                 nand_read_block(nand, addr, dst_block, addr < 0x400000);
264         }
265
266         if (ecc_errors)
267                 printf("Error: %d ECC failures detected\n", ecc_errors);
268         return ecc_errors == 0;
269 }
270
271 void
272 nand_deselect(void)
273 {}