]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
5846b11e IY |
2 | /* |
3 | * (C) Copyright 2012 | |
4 | * Konstantin Kozhevnikov, Cogent Embedded | |
5 | * | |
6 | * based on nand_spl_simple code | |
7 | * | |
8 | * (C) Copyright 2006-2008 | |
9 | * Stefan Roese, DENX Software Engineering, sr@denx.de. | |
5846b11e IY |
10 | */ |
11 | ||
d678a59d | 12 | #include <common.h> |
5846b11e | 13 | #include <nand.h> |
601b8901 | 14 | #include <system-constants.h> |
5846b11e | 15 | #include <asm/io.h> |
c05ed00a | 16 | #include <linux/delay.h> |
5846b11e | 17 | #include <linux/mtd/nand_ecc.h> |
1cefed1e | 18 | #include <linux/mtd/rawnand.h> |
5846b11e | 19 | |
4e590945 | 20 | static int nand_ecc_pos[] = CFG_SYS_NAND_ECCPOS; |
b616d9b0 | 21 | static struct mtd_info *mtd; |
5846b11e IY |
22 | static struct nand_chip nand_chip; |
23 | ||
24 | #define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ | |
4e590945 TR |
25 | CFG_SYS_NAND_ECCSIZE) |
26 | #define ECCTOTAL (ECCSTEPS * CFG_SYS_NAND_ECCBYTES) | |
5846b11e IY |
27 | |
28 | ||
29 | /* | |
30 | * NAND command for large page NAND devices (2k) | |
31 | */ | |
32 | static int nand_command(int block, int page, uint32_t offs, | |
33 | u8 cmd) | |
34 | { | |
17cb4b8f | 35 | struct nand_chip *this = mtd_to_nand(mtd); |
601b8901 | 36 | int page_addr = page + block * SYS_NAND_BLOCK_PAGES; |
5846b11e IY |
37 | void (*hwctrl)(struct mtd_info *mtd, int cmd, |
38 | unsigned int ctrl) = this->cmd_ctrl; | |
39 | ||
b616d9b0 | 40 | while (!this->dev_ready(mtd)) |
5846b11e IY |
41 | ; |
42 | ||
43 | /* Emulate NAND_CMD_READOOB */ | |
44 | if (cmd == NAND_CMD_READOOB) { | |
45 | offs += CONFIG_SYS_NAND_PAGE_SIZE; | |
46 | cmd = NAND_CMD_READ0; | |
47 | } | |
48 | ||
49 | /* Begin command latch cycle */ | |
b616d9b0 | 50 | hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); |
5846b11e IY |
51 | |
52 | if (cmd == NAND_CMD_RESET) { | |
b616d9b0 | 53 | hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); |
6e1eb089 CJF |
54 | |
55 | /* | |
56 | * Apply this short delay always to ensure that we do wait | |
57 | * tWB in any case on any machine. | |
58 | */ | |
59 | ndelay(150); | |
60 | ||
b616d9b0 | 61 | while (!this->dev_ready(mtd)) |
5846b11e IY |
62 | ; |
63 | return 0; | |
64 | } | |
65 | ||
66 | /* Shift the offset from byte addressing to word addressing. */ | |
27ce9e42 | 67 | if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd)) |
5846b11e IY |
68 | offs >>= 1; |
69 | ||
70 | /* Set ALE and clear CLE to start address cycle */ | |
71 | /* Column address */ | |
b616d9b0 | 72 | hwctrl(mtd, offs & 0xff, |
5846b11e | 73 | NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ |
b616d9b0 | 74 | hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ |
5846b11e | 75 | /* Row address */ |
2f665945 | 76 | if (cmd != NAND_CMD_RNDOUT) { |
b616d9b0 | 77 | hwctrl(mtd, (page_addr & 0xff), |
2f665945 | 78 | NAND_CTRL_ALE); /* A[19:12] */ |
b616d9b0 | 79 | hwctrl(mtd, ((page_addr >> 8) & 0xff), |
5846b11e IY |
80 | NAND_CTRL_ALE); /* A[27:20] */ |
81 | #ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE | |
2f665945 | 82 | /* One more address cycle for devices > 128MiB */ |
b616d9b0 | 83 | hwctrl(mtd, (page_addr >> 16) & 0x0f, |
5846b11e IY |
84 | NAND_CTRL_ALE); /* A[31:28] */ |
85 | #endif | |
2f665945 RL |
86 | } |
87 | ||
b616d9b0 | 88 | hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); |
5846b11e | 89 | |
5846b11e | 90 | |
6e1eb089 CJF |
91 | /* |
92 | * Program and erase have their own busy handlers status, sequential | |
93 | * in and status need no delay. | |
94 | */ | |
95 | switch (cmd) { | |
96 | case NAND_CMD_CACHEDPROG: | |
97 | case NAND_CMD_PAGEPROG: | |
98 | case NAND_CMD_ERASE1: | |
99 | case NAND_CMD_ERASE2: | |
100 | case NAND_CMD_SEQIN: | |
101 | case NAND_CMD_RNDIN: | |
102 | case NAND_CMD_STATUS: | |
103 | return 0; | |
104 | ||
105 | case NAND_CMD_RNDOUT: | |
106 | /* No ready / busy check necessary */ | |
b616d9b0 | 107 | hwctrl(mtd, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE | |
6e1eb089 CJF |
108 | NAND_CTRL_CHANGE); |
109 | hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); | |
110 | return 0; | |
111 | ||
112 | case NAND_CMD_READ0: | |
113 | /* Latch in address */ | |
114 | hwctrl(mtd, NAND_CMD_READSTART, | |
115 | NAND_CTRL_CLE | NAND_CTRL_CHANGE); | |
b616d9b0 | 116 | hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); |
5846b11e IY |
117 | } |
118 | ||
6e1eb089 CJF |
119 | /* |
120 | * Apply this short delay always to ensure that we do wait tWB in | |
121 | * any case on any machine. | |
122 | */ | |
123 | ndelay(150); | |
124 | ||
125 | while (!this->dev_ready(mtd)) | |
126 | ; | |
127 | ||
5846b11e IY |
128 | return 0; |
129 | } | |
130 | ||
131 | static int nand_is_bad_block(int block) | |
132 | { | |
17cb4b8f | 133 | struct nand_chip *this = mtd_to_nand(mtd); |
5846b11e IY |
134 | |
135 | nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, | |
136 | NAND_CMD_READOOB); | |
137 | ||
138 | /* | |
139 | * Read one byte (or two if it's a 16 bit chip). | |
140 | */ | |
141 | if (this->options & NAND_BUSWIDTH_16) { | |
142 | if (readw(this->IO_ADDR_R) != 0xffff) | |
143 | return 1; | |
144 | } else { | |
145 | if (readb(this->IO_ADDR_R) != 0xff) | |
146 | return 1; | |
147 | } | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | static int nand_read_page(int block, int page, void *dst) | |
153 | { | |
17cb4b8f | 154 | struct nand_chip *this = mtd_to_nand(mtd); |
5846b11e IY |
155 | u_char ecc_calc[ECCTOTAL]; |
156 | u_char ecc_code[ECCTOTAL]; | |
157 | u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; | |
158 | int i; | |
4e590945 TR |
159 | int eccsize = CFG_SYS_NAND_ECCSIZE; |
160 | int eccbytes = CFG_SYS_NAND_ECCBYTES; | |
5846b11e IY |
161 | int eccsteps = ECCSTEPS; |
162 | uint8_t *p = dst; | |
163 | uint32_t data_pos = 0; | |
164 | uint8_t *oob = &oob_data[0] + nand_ecc_pos[0]; | |
165 | uint32_t oob_pos = eccsize * eccsteps + nand_ecc_pos[0]; | |
166 | ||
167 | nand_command(block, page, 0, NAND_CMD_READ0); | |
168 | ||
169 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | |
b616d9b0 | 170 | this->ecc.hwctl(mtd, NAND_ECC_READ); |
5846b11e IY |
171 | nand_command(block, page, data_pos, NAND_CMD_RNDOUT); |
172 | ||
b616d9b0 | 173 | this->read_buf(mtd, p, eccsize); |
5846b11e IY |
174 | |
175 | nand_command(block, page, oob_pos, NAND_CMD_RNDOUT); | |
176 | ||
b616d9b0 SW |
177 | this->read_buf(mtd, oob, eccbytes); |
178 | this->ecc.calculate(mtd, p, &ecc_calc[i]); | |
5846b11e IY |
179 | |
180 | data_pos += eccsize; | |
181 | oob_pos += eccbytes; | |
182 | oob += eccbytes; | |
183 | } | |
184 | ||
185 | /* Pick the ECC bytes out of the oob data */ | |
186 | for (i = 0; i < ECCTOTAL; i++) | |
187 | ecc_code[i] = oob_data[nand_ecc_pos[i]]; | |
188 | ||
189 | eccsteps = ECCSTEPS; | |
190 | p = dst; | |
191 | ||
192 | for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | |
193 | /* No chance to do something with the possible error message | |
194 | * from correct_data(). We just hope that all possible errors | |
195 | * are corrected by this routine. | |
196 | */ | |
b616d9b0 | 197 | this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); |
5846b11e IY |
198 | } |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
5846b11e IY |
203 | /* nand_init() - initialize data to make nand usable by SPL */ |
204 | void nand_init(void) | |
205 | { | |
206 | /* | |
207 | * Init board specific nand support | |
208 | */ | |
30780f94 | 209 | mtd = nand_to_mtd(&nand_chip); |
5846b11e | 210 | nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = |
4e590945 | 211 | (void __iomem *)CFG_SYS_NAND_BASE; |
5846b11e IY |
212 | board_nand_init(&nand_chip); |
213 | ||
214 | if (nand_chip.select_chip) | |
b616d9b0 | 215 | nand_chip.select_chip(mtd, 0); |
5846b11e IY |
216 | |
217 | /* NAND chip may require reset after power-on */ | |
218 | nand_command(0, 0, 0, NAND_CMD_RESET); | |
219 | } | |
220 | ||
38ef64e6 SA |
221 | unsigned int nand_page_size(void) |
222 | { | |
223 | return nand_to_mtd(&nand_chip)->writesize; | |
224 | } | |
225 | ||
5846b11e IY |
226 | /* Unselect after operation */ |
227 | void nand_deselect(void) | |
228 | { | |
229 | if (nand_chip.select_chip) | |
b616d9b0 | 230 | nand_chip.select_chip(mtd, -1); |
5846b11e | 231 | } |
18cae43b LM |
232 | |
233 | #include "nand_spl_loaders.c" |