1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#include <common.h>
29
30#include <nand.h>
31#include <linux/mtd/nand.h>
32
33#include <asm/arch/s3c6400.h>
34
35#include <asm/io.h>
36#include <asm/errno.h>
37
38#define MAX_CHIPS 2
39static int nand_cs[MAX_CHIPS] = {0, 1};
40
41#ifdef CONFIG_NAND_SPL
42#define printf(arg...) do {} while (0)
43#endif
44
45
46#ifdef S3C_NAND_DEBUG
47
48
49
50
51static void print_oob(const char *header, struct mtd_info *mtd)
52{
53 int i;
54 struct nand_chip *chip = mtd->priv;
55
56 printf("%s:\t", header);
57
58 for (i = 0; i < 64; i++)
59 printf("%02x ", chip->oob_poi[i]);
60
61 printf("\n");
62}
63#endif
64
65static void s3c_nand_select_chip(struct mtd_info *mtd, int chip)
66{
67 int ctrl = readl(NFCONT);
68
69 switch (chip) {
70 case -1:
71 ctrl |= 6;
72 break;
73 case 0:
74 ctrl &= ~2;
75 break;
76 case 1:
77 ctrl &= ~4;
78 break;
79 default:
80 return;
81 }
82
83 writel(ctrl, NFCONT);
84}
85
86
87
88
89
90static void s3c_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
91{
92 struct nand_chip *this = mtd->priv;
93
94 if (ctrl & NAND_CTRL_CHANGE) {
95 if (ctrl & NAND_CLE)
96 this->IO_ADDR_W = (void __iomem *)NFCMMD;
97 else if (ctrl & NAND_ALE)
98 this->IO_ADDR_W = (void __iomem *)NFADDR;
99 else
100 this->IO_ADDR_W = (void __iomem *)NFDATA;
101 if (ctrl & NAND_NCE)
102 s3c_nand_select_chip(mtd, *(int *)this->priv);
103 else
104 s3c_nand_select_chip(mtd, -1);
105 }
106
107 if (cmd != NAND_CMD_NONE)
108 writeb(cmd, this->IO_ADDR_W);
109}
110
111
112
113
114
115static int s3c_nand_device_ready(struct mtd_info *mtdinfo)
116{
117 return !!(readl(NFSTAT) & NFSTAT_RnB);
118}
119
120#ifdef CONFIG_SYS_S3C_NAND_HWECC
121
122
123
124
125static void s3c_nand_enable_hwecc(struct mtd_info *mtd, int mode)
126{
127 u_long nfcont, nfconf;
128
129
130
131
132
133
134 nfconf = readl(NFCONF) & ~NFCONF_ECC_4BIT;
135
136 writel(nfconf, NFCONF);
137
138
139 nfcont = readl(NFCONT);
140 nfcont |= NFCONT_INITECC;
141 nfcont &= ~NFCONT_MECCLOCK;
142
143 if (mode == NAND_ECC_WRITE)
144 nfcont |= NFCONT_ECC_ENC;
145 else if (mode == NAND_ECC_READ)
146 nfcont &= ~NFCONT_ECC_ENC;
147
148 writel(nfcont, NFCONT);
149}
150
151
152
153
154
155
156static int s3c_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
157 u_char *ecc_code)
158{
159 u_long nfcont, nfmecc0;
160
161
162 nfcont = readl(NFCONT);
163 nfcont |= NFCONT_MECCLOCK;
164 writel(nfcont, NFCONT);
165
166 nfmecc0 = readl(NFMECC0);
167
168 ecc_code[0] = nfmecc0 & 0xff;
169 ecc_code[1] = (nfmecc0 >> 8) & 0xff;
170 ecc_code[2] = (nfmecc0 >> 16) & 0xff;
171 ecc_code[3] = (nfmecc0 >> 24) & 0xff;
172
173 return 0;
174}
175
176
177
178
179
180
181
182
183
184
185static int s3c_nand_correct_data(struct mtd_info *mtd, u_char *dat,
186 u_char *read_ecc, u_char *calc_ecc)
187{
188 int ret = -1;
189 u_long nfestat0, nfmeccdata0, nfmeccdata1, err_byte_addr;
190 u_char err_type, repaired;
191
192
193 nfmeccdata0 = (calc_ecc[1] << 16) | calc_ecc[0];
194 nfmeccdata1 = (calc_ecc[3] << 16) | calc_ecc[2];
195 writel(nfmeccdata0, NFMECCDATA0);
196 writel(nfmeccdata1, NFMECCDATA1);
197
198
199 nfestat0 = readl(NFESTAT0);
200 err_type = nfestat0 & 0x3;
201
202 switch (err_type) {
203 case 0:
204 ret = 0;
205 break;
206
207 case 1:
208
209
210
211
212
213 err_byte_addr = (nfestat0 >> 7) & 0x7ff;
214 repaired = dat[err_byte_addr] ^ (1 << ((nfestat0 >> 4) & 0x7));
215
216 printf("S3C NAND: 1 bit error detected at byte %ld. "
217 "Correcting from 0x%02x to 0x%02x...OK\n",
218 err_byte_addr, dat[err_byte_addr], repaired);
219
220 dat[err_byte_addr] = repaired;
221
222 ret = 1;
223 break;
224
225 case 2:
226 case 3:
227 printf("S3C NAND: ECC uncorrectable error detected. "
228 "Not correctable.\n");
229 ret = -1;
230 break;
231 }
232
233 return ret;
234}
235#endif
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255int board_nand_init(struct nand_chip *nand)
256{
257 static int chip_n;
258
259 if (chip_n >= MAX_CHIPS)
260 return -ENODEV;
261
262 NFCONT_REG = (NFCONT_REG & ~NFCONT_WP) | NFCONT_ENABLE | 0x6;
263
264 nand->IO_ADDR_R = (void __iomem *)NFDATA;
265 nand->IO_ADDR_W = (void __iomem *)NFDATA;
266 nand->cmd_ctrl = s3c_nand_hwcontrol;
267 nand->dev_ready = s3c_nand_device_ready;
268 nand->select_chip = s3c_nand_select_chip;
269 nand->options = 0;
270#ifdef CONFIG_NAND_SPL
271 nand->read_byte = nand_read_byte;
272 nand->write_buf = nand_write_buf;
273 nand->read_buf = nand_read_buf;
274#endif
275
276#ifdef CONFIG_SYS_S3C_NAND_HWECC
277 nand->ecc.hwctl = s3c_nand_enable_hwecc;
278 nand->ecc.calculate = s3c_nand_calculate_ecc;
279 nand->ecc.correct = s3c_nand_correct_data;
280
281
282
283
284
285 nand->ecc.mode = NAND_ECC_HW;
286 nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
287 nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
288#else
289 nand->ecc.mode = NAND_ECC_SOFT;
290#endif
291
292 nand->priv = nand_cs + chip_n++;
293
294 return 0;
295}
296