diff options
Diffstat (limited to 'drivers/mtd/nand/s3c2410.c')
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 297 |
1 files changed, 161 insertions, 136 deletions
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d05e9b97947d..891e3a1b9110 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c | |||
@@ -1,17 +1,24 @@ | |||
1 | /* linux/drivers/mtd/nand/s3c2410.c | 1 | /* linux/drivers/mtd/nand/s3c2410.c |
2 | * | 2 | * |
3 | * Copyright (c) 2004 Simtec Electronics | 3 | * Copyright (c) 2004,2005 Simtec Electronics |
4 | * Ben Dooks <ben@simtec.co.uk> | 4 | * http://www.simtec.co.uk/products/SWLINUX/ |
5 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | 6 | * |
6 | * Samsung S3C2410 NAND driver | 7 | * Samsung S3C2410/S3C240 NAND driver |
7 | * | 8 | * |
8 | * Changelog: | 9 | * Changelog: |
9 | * 21-Sep-2004 BJD Initial version | 10 | * 21-Sep-2004 BJD Initial version |
10 | * 23-Sep-2004 BJD Mulitple device support | 11 | * 23-Sep-2004 BJD Mulitple device support |
11 | * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode | 12 | * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode |
12 | * 12-Oct-2004 BJD Fixed errors in use of platform data | 13 | * 12-Oct-2004 BJD Fixed errors in use of platform data |
14 | * 18-Feb-2005 BJD Fix sparse errors | ||
15 | * 14-Mar-2005 BJD Applied tglx's code reduction patch | ||
16 | * 02-May-2005 BJD Fixed s3c2440 support | ||
17 | * 02-May-2005 BJD Reduced hwcontrol decode | ||
18 | * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug | ||
19 | * 08-Jul-2005 BJD Fix OOPS when no platform data supplied | ||
13 | * | 20 | * |
14 | * $Id: s3c2410.c,v 1.7 2005/01/05 18:05:14 dwmw2 Exp $ | 21 | * $Id: s3c2410.c,v 1.14 2005/07/06 20:05:06 bjd Exp $ |
15 | * | 22 | * |
16 | * This program is free software; you can redistribute it and/or modify | 23 | * This program is free software; you can redistribute it and/or modify |
17 | * it under the terms of the GNU General Public License as published by | 24 | * it under the terms of the GNU General Public License as published by |
@@ -69,10 +76,10 @@ static int hardware_ecc = 0; | |||
69 | */ | 76 | */ |
70 | 77 | ||
71 | static struct nand_oobinfo nand_hw_eccoob = { | 78 | static struct nand_oobinfo nand_hw_eccoob = { |
72 | .useecc = MTD_NANDECC_AUTOPLACE, | 79 | .useecc = MTD_NANDECC_AUTOPLACE, |
73 | .eccbytes = 3, | 80 | .eccbytes = 3, |
74 | .eccpos = {0, 1, 2 }, | 81 | .eccpos = {0, 1, 2 }, |
75 | .oobfree = { {8, 8} } | 82 | .oobfree = { {8, 8} } |
76 | }; | 83 | }; |
77 | 84 | ||
78 | /* controller and mtd information */ | 85 | /* controller and mtd information */ |
@@ -99,8 +106,10 @@ struct s3c2410_nand_info { | |||
99 | struct device *device; | 106 | struct device *device; |
100 | struct resource *area; | 107 | struct resource *area; |
101 | struct clk *clk; | 108 | struct clk *clk; |
102 | void *regs; | 109 | void __iomem *regs; |
103 | int mtd_count; | 110 | int mtd_count; |
111 | |||
112 | unsigned char is_s3c2440; | ||
104 | }; | 113 | }; |
105 | 114 | ||
106 | /* conversion functions */ | 115 | /* conversion functions */ |
@@ -165,12 +174,12 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, | |||
165 | /* calculate the timing information for the controller */ | 174 | /* calculate the timing information for the controller */ |
166 | 175 | ||
167 | if (plat != NULL) { | 176 | if (plat != NULL) { |
168 | tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); | 177 | tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4); |
169 | twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); | 178 | twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); |
170 | twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); | 179 | twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); |
171 | } else { | 180 | } else { |
172 | /* default timings */ | 181 | /* default timings */ |
173 | tacls = 8; | 182 | tacls = 4; |
174 | twrph0 = 8; | 183 | twrph0 = 8; |
175 | twrph1 = 8; | 184 | twrph1 = 8; |
176 | } | 185 | } |
@@ -185,10 +194,16 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, | |||
185 | to_ns(twrph0, clkrate), | 194 | to_ns(twrph0, clkrate), |
186 | to_ns(twrph1, clkrate)); | 195 | to_ns(twrph1, clkrate)); |
187 | 196 | ||
188 | cfg = S3C2410_NFCONF_EN; | 197 | if (!info->is_s3c2440) { |
189 | cfg |= S3C2410_NFCONF_TACLS(tacls-1); | 198 | cfg = S3C2410_NFCONF_EN; |
190 | cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); | 199 | cfg |= S3C2410_NFCONF_TACLS(tacls-1); |
191 | cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); | 200 | cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); |
201 | cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); | ||
202 | } else { | ||
203 | cfg = S3C2440_NFCONF_TACLS(tacls-1); | ||
204 | cfg |= S3C2440_NFCONF_TWRPH0(twrph0-1); | ||
205 | cfg |= S3C2440_NFCONF_TWRPH1(twrph1-1); | ||
206 | } | ||
192 | 207 | ||
193 | pr_debug(PFX "NF_CONF is 0x%lx\n", cfg); | 208 | pr_debug(PFX "NF_CONF is 0x%lx\n", cfg); |
194 | 209 | ||
@@ -203,17 +218,22 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) | |||
203 | struct s3c2410_nand_info *info; | 218 | struct s3c2410_nand_info *info; |
204 | struct s3c2410_nand_mtd *nmtd; | 219 | struct s3c2410_nand_mtd *nmtd; |
205 | struct nand_chip *this = mtd->priv; | 220 | struct nand_chip *this = mtd->priv; |
221 | void __iomem *reg; | ||
206 | unsigned long cur; | 222 | unsigned long cur; |
223 | unsigned long bit; | ||
207 | 224 | ||
208 | nmtd = this->priv; | 225 | nmtd = this->priv; |
209 | info = nmtd->info; | 226 | info = nmtd->info; |
210 | 227 | ||
211 | cur = readl(info->regs + S3C2410_NFCONF); | 228 | bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE; |
229 | reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF); | ||
230 | |||
231 | cur = readl(reg); | ||
212 | 232 | ||
213 | if (chip == -1) { | 233 | if (chip == -1) { |
214 | cur |= S3C2410_NFCONF_nFCE; | 234 | cur |= bit; |
215 | } else { | 235 | } else { |
216 | if (chip > nmtd->set->nr_chips) { | 236 | if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { |
217 | printk(KERN_ERR PFX "chip %d out of range\n", chip); | 237 | printk(KERN_ERR PFX "chip %d out of range\n", chip); |
218 | return; | 238 | return; |
219 | } | 239 | } |
@@ -223,143 +243,76 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) | |||
223 | (info->platform->select_chip)(nmtd->set, chip); | 243 | (info->platform->select_chip)(nmtd->set, chip); |
224 | } | 244 | } |
225 | 245 | ||
226 | cur &= ~S3C2410_NFCONF_nFCE; | 246 | cur &= ~bit; |
227 | } | 247 | } |
228 | 248 | ||
229 | writel(cur, info->regs + S3C2410_NFCONF); | 249 | writel(cur, reg); |
230 | } | 250 | } |
231 | 251 | ||
232 | /* command and control functions */ | 252 | /* command and control functions |
253 | * | ||
254 | * Note, these all use tglx's method of changing the IO_ADDR_W field | ||
255 | * to make the code simpler, and use the nand layer's code to issue the | ||
256 | * command and address sequences via the proper IO ports. | ||
257 | * | ||
258 | */ | ||
233 | 259 | ||
234 | static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd) | 260 | static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd) |
235 | { | 261 | { |
236 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 262 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
237 | unsigned long cur; | 263 | struct nand_chip *chip = mtd->priv; |
238 | 264 | ||
239 | switch (cmd) { | 265 | switch (cmd) { |
240 | case NAND_CTL_SETNCE: | 266 | case NAND_CTL_SETNCE: |
241 | cur = readl(info->regs + S3C2410_NFCONF); | ||
242 | cur &= ~S3C2410_NFCONF_nFCE; | ||
243 | writel(cur, info->regs + S3C2410_NFCONF); | ||
244 | break; | ||
245 | |||
246 | case NAND_CTL_CLRNCE: | 267 | case NAND_CTL_CLRNCE: |
247 | cur = readl(info->regs + S3C2410_NFCONF); | 268 | printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__); |
248 | cur |= S3C2410_NFCONF_nFCE; | ||
249 | writel(cur, info->regs + S3C2410_NFCONF); | ||
250 | break; | 269 | break; |
251 | 270 | ||
252 | /* we don't need to implement these */ | ||
253 | case NAND_CTL_SETCLE: | 271 | case NAND_CTL_SETCLE: |
254 | case NAND_CTL_CLRCLE: | 272 | chip->IO_ADDR_W = info->regs + S3C2410_NFCMD; |
273 | break; | ||
274 | |||
255 | case NAND_CTL_SETALE: | 275 | case NAND_CTL_SETALE: |
256 | case NAND_CTL_CLRALE: | 276 | chip->IO_ADDR_W = info->regs + S3C2410_NFADDR; |
257 | pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd); | 277 | break; |
278 | |||
279 | /* NAND_CTL_CLRCLE: */ | ||
280 | /* NAND_CTL_CLRALE: */ | ||
281 | default: | ||
282 | chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; | ||
258 | break; | 283 | break; |
259 | } | 284 | } |
260 | } | 285 | } |
261 | 286 | ||
262 | /* s3c2410_nand_command | 287 | /* command and control functions */ |
263 | * | ||
264 | * This function implements sending commands and the relevant address | ||
265 | * information to the chip, via the hardware controller. Since the | ||
266 | * S3C2410 generates the correct ALE/CLE signaling automatically, we | ||
267 | * do not need to use hwcontrol. | ||
268 | */ | ||
269 | 288 | ||
270 | static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command, | 289 | static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd) |
271 | int column, int page_addr) | ||
272 | { | 290 | { |
273 | register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 291 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
274 | register struct nand_chip *this = mtd->priv; | 292 | struct nand_chip *chip = mtd->priv; |
275 | 293 | ||
276 | /* | 294 | switch (cmd) { |
277 | * Write out the command to the device. | 295 | case NAND_CTL_SETNCE: |
278 | */ | 296 | case NAND_CTL_CLRNCE: |
279 | if (command == NAND_CMD_SEQIN) { | 297 | printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__); |
280 | int readcmd; | 298 | break; |
281 | |||
282 | if (column >= mtd->oobblock) { | ||
283 | /* OOB area */ | ||
284 | column -= mtd->oobblock; | ||
285 | readcmd = NAND_CMD_READOOB; | ||
286 | } else if (column < 256) { | ||
287 | /* First 256 bytes --> READ0 */ | ||
288 | readcmd = NAND_CMD_READ0; | ||
289 | } else { | ||
290 | column -= 256; | ||
291 | readcmd = NAND_CMD_READ1; | ||
292 | } | ||
293 | |||
294 | writeb(readcmd, info->regs + S3C2410_NFCMD); | ||
295 | } | ||
296 | writeb(command, info->regs + S3C2410_NFCMD); | ||
297 | 299 | ||
298 | /* Set ALE and clear CLE to start address cycle */ | 300 | case NAND_CTL_SETCLE: |
301 | chip->IO_ADDR_W = info->regs + S3C2440_NFCMD; | ||
302 | break; | ||
299 | 303 | ||
300 | if (column != -1 || page_addr != -1) { | 304 | case NAND_CTL_SETALE: |
305 | chip->IO_ADDR_W = info->regs + S3C2440_NFADDR; | ||
306 | break; | ||
301 | 307 | ||
302 | /* Serially input address */ | 308 | /* NAND_CTL_CLRCLE: */ |
303 | if (column != -1) { | 309 | /* NAND_CTL_CLRALE: */ |
304 | /* Adjust columns for 16 bit buswidth */ | ||
305 | if (this->options & NAND_BUSWIDTH_16) | ||
306 | column >>= 1; | ||
307 | writeb(column, info->regs + S3C2410_NFADDR); | ||
308 | } | ||
309 | if (page_addr != -1) { | ||
310 | writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR); | ||
311 | writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR); | ||
312 | /* One more address cycle for higher density devices */ | ||
313 | if (this->chipsize & 0x0c000000) | ||
314 | writeb((unsigned char) ((page_addr >> 16) & 0x0f), | ||
315 | info->regs + S3C2410_NFADDR); | ||
316 | } | ||
317 | /* Latch in address */ | ||
318 | } | ||
319 | |||
320 | /* | ||
321 | * program and erase have their own busy handlers | ||
322 | * status and sequential in needs no delay | ||
323 | */ | ||
324 | switch (command) { | ||
325 | |||
326 | case NAND_CMD_PAGEPROG: | ||
327 | case NAND_CMD_ERASE1: | ||
328 | case NAND_CMD_ERASE2: | ||
329 | case NAND_CMD_SEQIN: | ||
330 | case NAND_CMD_STATUS: | ||
331 | return; | ||
332 | |||
333 | case NAND_CMD_RESET: | ||
334 | if (this->dev_ready) | ||
335 | break; | ||
336 | |||
337 | udelay(this->chip_delay); | ||
338 | writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD); | ||
339 | |||
340 | while ( !(this->read_byte(mtd) & 0x40)); | ||
341 | return; | ||
342 | |||
343 | /* This applies to read commands */ | ||
344 | default: | 310 | default: |
345 | /* | 311 | chip->IO_ADDR_W = info->regs + S3C2440_NFDATA; |
346 | * If we don't have access to the busy pin, we apply the given | 312 | break; |
347 | * command delay | ||
348 | */ | ||
349 | if (!this->dev_ready) { | ||
350 | udelay (this->chip_delay); | ||
351 | return; | ||
352 | } | ||
353 | } | 313 | } |
354 | |||
355 | /* Apply this short delay always to ensure that we do wait tWB in | ||
356 | * any case on any machine. */ | ||
357 | ndelay (100); | ||
358 | /* wait until command is processed */ | ||
359 | while (!this->dev_ready(mtd)); | ||
360 | } | 314 | } |
361 | 315 | ||
362 | |||
363 | /* s3c2410_nand_devready() | 316 | /* s3c2410_nand_devready() |
364 | * | 317 | * |
365 | * returns 0 if the nand is busy, 1 if it is ready | 318 | * returns 0 if the nand is busy, 1 if it is ready |
@@ -369,9 +322,12 @@ static int s3c2410_nand_devready(struct mtd_info *mtd) | |||
369 | { | 322 | { |
370 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 323 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
371 | 324 | ||
325 | if (info->is_s3c2440) | ||
326 | return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; | ||
372 | return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; | 327 | return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; |
373 | } | 328 | } |
374 | 329 | ||
330 | |||
375 | /* ECC handling functions */ | 331 | /* ECC handling functions */ |
376 | 332 | ||
377 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, | 333 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, |
@@ -394,6 +350,12 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, | |||
394 | return -1; | 350 | return -1; |
395 | } | 351 | } |
396 | 352 | ||
353 | /* ECC functions | ||
354 | * | ||
355 | * These allow the s3c2410 and s3c2440 to use the controller's ECC | ||
356 | * generator block to ECC the data as it passes through] | ||
357 | */ | ||
358 | |||
397 | static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) | 359 | static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) |
398 | { | 360 | { |
399 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 361 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
@@ -404,6 +366,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) | |||
404 | writel(ctrl, info->regs + S3C2410_NFCONF); | 366 | writel(ctrl, info->regs + S3C2410_NFCONF); |
405 | } | 367 | } |
406 | 368 | ||
369 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) | ||
370 | { | ||
371 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | ||
372 | unsigned long ctrl; | ||
373 | |||
374 | ctrl = readl(info->regs + S3C2440_NFCONT); | ||
375 | writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); | ||
376 | } | ||
377 | |||
407 | static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, | 378 | static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, |
408 | const u_char *dat, u_char *ecc_code) | 379 | const u_char *dat, u_char *ecc_code) |
409 | { | 380 | { |
@@ -420,7 +391,26 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, | |||
420 | } | 391 | } |
421 | 392 | ||
422 | 393 | ||
423 | /* over-ride the standard functions for a little more speed? */ | 394 | static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, |
395 | const u_char *dat, u_char *ecc_code) | ||
396 | { | ||
397 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | ||
398 | unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); | ||
399 | |||
400 | ecc_code[0] = ecc; | ||
401 | ecc_code[1] = ecc >> 8; | ||
402 | ecc_code[2] = ecc >> 16; | ||
403 | |||
404 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", | ||
405 | ecc_code[0], ecc_code[1], ecc_code[2]); | ||
406 | |||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | |||
411 | /* over-ride the standard functions for a little more speed. We can | ||
412 | * use read/write block to move the data buffers to/from the controller | ||
413 | */ | ||
424 | 414 | ||
425 | static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) | 415 | static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) |
426 | { | 416 | { |
@@ -523,11 +513,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | |||
523 | { | 513 | { |
524 | struct nand_chip *chip = &nmtd->chip; | 514 | struct nand_chip *chip = &nmtd->chip; |
525 | 515 | ||
526 | chip->IO_ADDR_R = (char *)info->regs + S3C2410_NFDATA; | 516 | chip->IO_ADDR_R = info->regs + S3C2410_NFDATA; |
527 | chip->IO_ADDR_W = (char *)info->regs + S3C2410_NFDATA; | 517 | chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; |
528 | chip->hwcontrol = s3c2410_nand_hwcontrol; | 518 | chip->hwcontrol = s3c2410_nand_hwcontrol; |
529 | chip->dev_ready = s3c2410_nand_devready; | 519 | chip->dev_ready = s3c2410_nand_devready; |
530 | chip->cmdfunc = s3c2410_nand_command; | ||
531 | chip->write_buf = s3c2410_nand_write_buf; | 520 | chip->write_buf = s3c2410_nand_write_buf; |
532 | chip->read_buf = s3c2410_nand_read_buf; | 521 | chip->read_buf = s3c2410_nand_read_buf; |
533 | chip->select_chip = s3c2410_nand_select_chip; | 522 | chip->select_chip = s3c2410_nand_select_chip; |
@@ -536,6 +525,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | |||
536 | chip->options = 0; | 525 | chip->options = 0; |
537 | chip->controller = &info->controller; | 526 | chip->controller = &info->controller; |
538 | 527 | ||
528 | if (info->is_s3c2440) { | ||
529 | chip->IO_ADDR_R = info->regs + S3C2440_NFDATA; | ||
530 | chip->IO_ADDR_W = info->regs + S3C2440_NFDATA; | ||
531 | chip->hwcontrol = s3c2440_nand_hwcontrol; | ||
532 | } | ||
533 | |||
539 | nmtd->info = info; | 534 | nmtd->info = info; |
540 | nmtd->mtd.priv = chip; | 535 | nmtd->mtd.priv = chip; |
541 | nmtd->set = set; | 536 | nmtd->set = set; |
@@ -546,6 +541,11 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | |||
546 | chip->calculate_ecc = s3c2410_nand_calculate_ecc; | 541 | chip->calculate_ecc = s3c2410_nand_calculate_ecc; |
547 | chip->eccmode = NAND_ECC_HW3_512; | 542 | chip->eccmode = NAND_ECC_HW3_512; |
548 | chip->autooob = &nand_hw_eccoob; | 543 | chip->autooob = &nand_hw_eccoob; |
544 | |||
545 | if (info->is_s3c2440) { | ||
546 | chip->enable_hwecc = s3c2440_nand_enable_hwecc; | ||
547 | chip->calculate_ecc = s3c2440_nand_calculate_ecc; | ||
548 | } | ||
549 | } else { | 549 | } else { |
550 | chip->eccmode = NAND_ECC_SOFT; | 550 | chip->eccmode = NAND_ECC_SOFT; |
551 | } | 551 | } |
@@ -559,7 +559,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | |||
559 | * nand layer to look for devices | 559 | * nand layer to look for devices |
560 | */ | 560 | */ |
561 | 561 | ||
562 | static int s3c2410_nand_probe(struct device *dev) | 562 | static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) |
563 | { | 563 | { |
564 | struct platform_device *pdev = to_platform_device(dev); | 564 | struct platform_device *pdev = to_platform_device(dev); |
565 | struct s3c2410_platform_nand *plat = to_nand_plat(dev); | 565 | struct s3c2410_platform_nand *plat = to_nand_plat(dev); |
@@ -585,6 +585,7 @@ static int s3c2410_nand_probe(struct device *dev) | |||
585 | dev_set_drvdata(dev, info); | 585 | dev_set_drvdata(dev, info); |
586 | 586 | ||
587 | spin_lock_init(&info->controller.lock); | 587 | spin_lock_init(&info->controller.lock); |
588 | init_waitqueue_head(&info->controller.wq); | ||
588 | 589 | ||
589 | /* get the clock source and enable it */ | 590 | /* get the clock source and enable it */ |
590 | 591 | ||
@@ -600,7 +601,8 @@ static int s3c2410_nand_probe(struct device *dev) | |||
600 | 601 | ||
601 | /* allocate and map the resource */ | 602 | /* allocate and map the resource */ |
602 | 603 | ||
603 | res = pdev->resource; /* assume that the flash has one resource */ | 604 | /* currently we assume we have the one resource */ |
605 | res = pdev->resource; | ||
604 | size = res->end - res->start + 1; | 606 | size = res->end - res->start + 1; |
605 | 607 | ||
606 | info->area = request_mem_region(res->start, size, pdev->name); | 608 | info->area = request_mem_region(res->start, size, pdev->name); |
@@ -611,9 +613,10 @@ static int s3c2410_nand_probe(struct device *dev) | |||
611 | goto exit_error; | 613 | goto exit_error; |
612 | } | 614 | } |
613 | 615 | ||
614 | info->device = dev; | 616 | info->device = dev; |
615 | info->platform = plat; | 617 | info->platform = plat; |
616 | info->regs = ioremap(res->start, size); | 618 | info->regs = ioremap(res->start, size); |
619 | info->is_s3c2440 = is_s3c2440; | ||
617 | 620 | ||
618 | if (info->regs == NULL) { | 621 | if (info->regs == NULL) { |
619 | printk(KERN_ERR PFX "cannot reserve register region\n"); | 622 | printk(KERN_ERR PFX "cannot reserve register region\n"); |
@@ -678,6 +681,18 @@ static int s3c2410_nand_probe(struct device *dev) | |||
678 | return err; | 681 | return err; |
679 | } | 682 | } |
680 | 683 | ||
684 | /* driver device registration */ | ||
685 | |||
686 | static int s3c2410_nand_probe(struct device *dev) | ||
687 | { | ||
688 | return s3c24xx_nand_probe(dev, 0); | ||
689 | } | ||
690 | |||
691 | static int s3c2440_nand_probe(struct device *dev) | ||
692 | { | ||
693 | return s3c24xx_nand_probe(dev, 1); | ||
694 | } | ||
695 | |||
681 | static struct device_driver s3c2410_nand_driver = { | 696 | static struct device_driver s3c2410_nand_driver = { |
682 | .name = "s3c2410-nand", | 697 | .name = "s3c2410-nand", |
683 | .bus = &platform_bus_type, | 698 | .bus = &platform_bus_type, |
@@ -685,14 +700,24 @@ static struct device_driver s3c2410_nand_driver = { | |||
685 | .remove = s3c2410_nand_remove, | 700 | .remove = s3c2410_nand_remove, |
686 | }; | 701 | }; |
687 | 702 | ||
703 | static struct device_driver s3c2440_nand_driver = { | ||
704 | .name = "s3c2440-nand", | ||
705 | .bus = &platform_bus_type, | ||
706 | .probe = s3c2440_nand_probe, | ||
707 | .remove = s3c2410_nand_remove, | ||
708 | }; | ||
709 | |||
688 | static int __init s3c2410_nand_init(void) | 710 | static int __init s3c2410_nand_init(void) |
689 | { | 711 | { |
690 | printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n"); | 712 | printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); |
713 | |||
714 | driver_register(&s3c2440_nand_driver); | ||
691 | return driver_register(&s3c2410_nand_driver); | 715 | return driver_register(&s3c2410_nand_driver); |
692 | } | 716 | } |
693 | 717 | ||
694 | static void __exit s3c2410_nand_exit(void) | 718 | static void __exit s3c2410_nand_exit(void) |
695 | { | 719 | { |
720 | driver_unregister(&s3c2440_nand_driver); | ||
696 | driver_unregister(&s3c2410_nand_driver); | 721 | driver_unregister(&s3c2410_nand_driver); |
697 | } | 722 | } |
698 | 723 | ||
@@ -701,4 +726,4 @@ module_exit(s3c2410_nand_exit); | |||
701 | 726 | ||
702 | MODULE_LICENSE("GPL"); | 727 | MODULE_LICENSE("GPL"); |
703 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); | 728 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); |
704 | MODULE_DESCRIPTION("S3C2410 MTD NAND driver"); | 729 | MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); |