[MINI2440] Added partial support for hardware ECC
[u-boot-openmoko/mini2440.git] / cpu / arm920t / s3c24x0 / nand.c
blob2d3a41917ce81f327e59609f7d34812d3d164675
1 /*
2 * (C) Copyright 2006 OpenMoko, Inc.
3 * Author: Harald Welte <laforge@openmoko.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18 * MA 02111-1307 USA
21 #include <common.h>
23 #if 0
24 #define DEBUGN printf
25 #else
26 #define DEBUGN(x, args ...) {}
27 #endif
29 #if defined(CONFIG_CMD_NAND)
30 #if !defined(CFG_NAND_LEGACY)
32 #include <nand.h>
34 #define __REGb(x) (*(volatile unsigned char *)(x))
35 #define __REGi(x) (*(volatile unsigned int *)(x))
37 //#define NF_BASE 0x4e000000
39 //#define NFCONF __REGi(NF_BASE + 0x0)
41 #if defined(CONFIG_S3C2410)
42 #include <s3c2410.h>
44 #define NFB_BASE S3C2410_NAND_BASE
46 #define oNFCMD 0x4
47 #define oNFADDR 0x8
48 #define oNFDATA 0xc
49 #define oNFSTAT 0x10
50 #define NFECC0 __REGb(NF_BASE + 0x14)
51 #define NFECC1 __REGb(NF_BASE + 0x15)
52 #define NFECC2 __REGb(NF_BASE + 0x16)
53 #define NFCONF_nFCE (1<<11)
55 #define S3C2410_NFCONF_EN (1<<15)
56 #define S3C2410_NFCONF_512BYTE (1<<14)
57 #define S3C2410_NFCONF_4STEP (1<<13)
58 #define S3C2410_NFCONF_INITECC (1<<12)
59 #define S3C2410_NFCONF_TACLS(x) ((x)<<8)
60 #define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)
61 #define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)
63 #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
64 #include <s3c2440.h>
66 #define NF_BASE S3C2440_NAND_BASE
68 #define oNFCMD 0x8
69 #define oNFADDR 0xc
70 #define oNFDATA 0x10
71 #define oNFSTAT 0x20
73 #define NFECC0 __REGb(NF_BASE + 0x14)
74 #define NFECC1 __REGb(NF_BASE + 0x15)
75 #define NFECC2 __REGb(NF_BASE + 0x16)
77 //#define NFCONT __REGi(NF_BASE + 0x04)
78 //#define NFMECC0 __REGi(NF_BASE + 0x2C)
79 #define NFCONF_nFCE (1<<1)
80 #define S3C2440_NFCONT_INITECC (1<<4)
81 #define S3C2440_NFCONT_MAINECCLOCK (1<<5)
82 #define nand_select() (NFCONT &= ~(1 << 1))
83 #define nand_deselect() (NFCONT |= (1 << 1))
84 #define nand_clear_RnB() (NFSTAT |= (1 << 2))
85 #define nand_detect_RB() { while(!(NFSTAT&(1<<2))); }
86 #define nand_wait() { while(!(NFSTAT & 0x4)); } /* RnB_TransDectect */
88 #endif
90 //#define NFCMD __REGb(NF_BASE + oNFCMD)
91 //#define NFADDR __REGb(NF_BASE + oNFADDR)
92 //#define NFDATA __REGb(NF_BASE + oNFDATA)
93 //#define NFSTAT __REGb(NF_BASE + oNFSTAT)
95 #if defined(CONFIG_HXD8)
96 static int hxd8_nand_dev_ready(struct mtd_info *mtd)
98 S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
99 u_int32_t val = gpio->GPCDAT;
101 switch (nand_curr_device) {
102 case 0:
103 return (NFSTAT & 0x01);
104 case 1: /* RnB 3 */
105 return ((val>>6) & 0x01);
106 case 2: /* RnB 4 */
107 return ((val>>7) & 0x01);
108 case 3: /* RnB 2 */
109 return ((val>>5) & 0x01);
110 default:
111 return 0;
115 /* 4G Nand flash chip select function */
116 static void hxd8_nand_select_chip(struct nand_chip *this, int chip)
118 S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
120 if (chip == 0)
121 gpio->GPGDAT &= ~(1 << 1);
122 else
123 gpio->GPGDAT |= (1 << 1);
125 if (chip == 1)
126 gpio->GPADAT &= ~(1 << 15);
127 else
128 gpio->GPADAT |= (1 << 15);
130 if (chip == 2)
131 gpio->GPADAT &= ~(1 << 16);
132 else
133 gpio->GPADAT |= (1 << 16);
135 if (chip == 3)
136 gpio->GPADAT &= ~(1 << 14);
137 else
138 gpio->GPADAT |= (1 << 14);
140 /* UGLY: ew don't have mtd_info pointer, but know that
141 * s3c24xx hwcontrol function does not use it for CLRNCE */
142 if (chip == -1)
143 this->hwcontrol(NULL, NAND_CTL_CLRNCE);
144 else
145 this->hwcontrol(NULL, NAND_CTL_SETNCE);
147 #endif
149 static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd)
151 struct nand_chip *chip = mtd->priv;
153 DEBUGN("hwcontrol(): 0x%02x: ", cmd);
155 switch (cmd) {
156 case NAND_CTL_SETNCE:
157 #if defined(CONFIG_S3C2410)
158 NFCONF &= ~NFCONF_nFCE;
159 #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
160 #ifdef CONFIG_S3C2440_NAND_HWECC
161 NFCONT = (NFCONT & ~((1 << 5) | NFCONF_nFCE)) | S3C2440_NFCONT_INITECC;
163 #else
164 NFCONT &= ~NFCONF_nFCE;
165 #endif
166 #endif
167 DEBUGN("NFCONF=0x%08x\n", NFCONF);
168 break;
169 case NAND_CTL_CLRNCE:
170 #if defined(CONFIG_S3C2410)
171 NFCONF |= NFCONF_nFCE;
172 #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
173 NFCONT &= ~NFCONF_nFCE;
174 #endif
175 DEBUGN("NFCONF=0x%08x\n", NFCONF);
176 break;
177 case NAND_CTL_SETALE:
178 chip->IO_ADDR_W = NF_BASE + oNFADDR;
179 DEBUGN("SETALE\n");
180 break;
181 case NAND_CTL_SETCLE:
182 chip->IO_ADDR_W = NF_BASE + oNFCMD;
183 DEBUGN("SETCLE\n");
184 break;
185 default:
186 chip->IO_ADDR_W = NF_BASE + oNFDATA;
187 break;
189 return;
192 static int s3c2410_dev_ready(struct mtd_info *mtd)
194 DEBUGN("dev_ready\n");
195 return (NFSTAT & 0x01);
198 #ifdef CONFIG_S3C2410_NAND_HWECC
199 void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
201 DEBUGN("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd ,mode);
202 NFCONF |= S3C2410_NFCONF_INITECC;
205 static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
206 u_char *ecc_code)
208 ecc_code[0] = NFECC0;
209 ecc_code[1] = NFECC1;
210 ecc_code[2] = NFECC2;
211 DEBUGN("s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
212 mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
214 return 0;
217 static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
218 u_char *read_ecc, u_char *calc_ecc)
220 if (read_ecc[0] == calc_ecc[0] &&
221 read_ecc[1] == calc_ecc[1] &&
222 read_ecc[2] == calc_ecc[2])
223 return 0;
225 printf("s3c2410_nand_correct_data: not implemented\n");
226 return -1;
228 #endif
230 #ifdef CONFIG_S3C2440_NAND_HWECC
231 void s3c244x_nand_enable_hwecc(struct mtd_info *mtd, int mode)
233 DEBUGN("s3c244x_nand_enable_hwecc(%p, %d)\n", mtd ,mode);
234 NFCONT = (NFCONT & ~(1 << 5)) | S3C2440_NFCONT_INITECC;
237 static int s3c244x_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
238 u_char *ecc_code)
240 ecc_code[0] = NFECC0;
241 ecc_code[1] = NFECC1;
242 ecc_code[2] = NFECC2;
243 DEBUGN("s3c244x_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
244 mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
246 return 0;
249 static int s3c244x_nand_correct_data(struct mtd_info *mtd, u_char *dat,
250 u_char *read_ecc, u_char *calc_ecc)
252 if (read_ecc[0] == calc_ecc[0] &&
253 read_ecc[1] == calc_ecc[1] &&
254 read_ecc[2] == calc_ecc[2])
255 return 0;
257 printf("s3c244x_nand_correct_data: not implemented\n");
258 return -1;
260 #endif
262 int board_nand_init(void) __attribute__((weak, alias("__board_nand_init")));
264 int __board_nand_init(struct nand_chip *nand)
266 u_int32_t cfg;
267 u_int8_t tacls, twrph0, twrph1;
268 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
270 DEBUGN("board_nand_init()\n");
272 clk_power->CLKCON |= (1 << 4);
274 /* initialize hardware */
275 twrph0 = 3; twrph1 = 0; tacls = 0;
277 #if defined(CONFIG_S3C2410)
278 cfg = S3C2410_NFCONF_EN;
279 cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
280 cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
281 cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
283 NFCONF = cfg;
284 #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
285 twrph0 = 7; twrph1 = 7; tacls = 7;
286 NFCONF = (tacls<<12)|(twrph0<<8)|(twrph1<<4)|(0<<0);
287 NFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
288 #endif
290 /* initialize nand_chip data structure */
291 nand->IO_ADDR_R = nand->IO_ADDR_W = NF_BASE + oNFDATA;
293 /* read_buf and write_buf are default */
294 /* read_byte and write_byte are default */
296 /* hwcontrol always must be implemented */
297 nand->hwcontrol = s3c2410_hwcontrol;
299 nand->dev_ready = s3c2410_dev_ready;
301 #ifdef CONFIG_S3C2410_NAND_HWECC
302 nand->enable_hwecc = s3c2410_nand_enable_hwecc;
303 nand->calculate_ecc = s3c2410_nand_calculate_ecc;
304 nand->correct_data = s3c2410_nand_correct_data;
305 nand->eccmode = NAND_ECC_HW3_512;
306 #elif CONFIG_S3C2440_NAND_HWECC
307 nand->enable_hwecc = s3c244x_nand_enable_hwecc;
308 nand->calculate_ecc = s3c244x_nand_calculate_ecc;
309 nand->correct_data = s3c244x_nand_correct_data;
310 nand->eccmode = NAND_ECC_HW3_512;
311 #else
312 nand->eccmode = NAND_ECC_SOFT;
313 #endif
315 #if defined(CONFIG_HXD8)
316 nand->dev_ready = hxd8_nand_dev_ready;
317 nand->select_chip = hxd8_nand_select_chip;
318 #endif
320 #ifdef CONFIG_S3C2410_NAND_BBT
321 nand->options = NAND_USE_FLASH_BBT | NAND_DONT_CREATE_BBT;
322 #else
323 nand->options = 0;
324 #endif
326 #if defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
328 nand_select();
329 nand_clear_RnB();
330 NFCMD = NAND_CMD_RESET;
331 { volatile int i; for (i = 0; i < 10; i ++); }
332 nand_detect_RB();
333 nand_deselect();
335 #endif
337 DEBUGN("end of nand_init\n");
339 return 0;
342 #else
343 #error "U-Boot legacy NAND support not available for S3C24xx"
344 #endif
345 #endif