Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / drivers / mtd / nand / s3c2410.c
1 /* linux/drivers/mtd/nand/s3c2410.c
2  *
3  * Copyright (c) 2004,2005 Simtec Electronics
4  *      http://www.simtec.co.uk/products/SWLINUX/
5  *      Ben Dooks <ben@simtec.co.uk>
6  *
7  * Samsung S3C2410/S3C240 NAND driver
8  *
9  * Changelog:
10  *      21-Sep-2004  BJD  Initial version
11  *      23-Sep-2004  BJD  Mulitple device support
12  *      28-Sep-2004  BJD  Fixed ECC placement for Hardware mode
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
20  *      20-Oct-2005  BJD  Fix timing calculation bug
21  *      14-Jan-2006  BJD  Allow clock to be stopped when idle
22  *
23  * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $
24  *
25  * This program is free software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation; either version 2 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program; if not, write to the Free Software
37  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
38 */
39
40 #ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
41 #define DEBUG
42 #endif
43
44 #include <linux/module.h>
45 #include <linux/types.h>
46 #include <linux/init.h>
47 #include <linux/kernel.h>
48 #include <linux/string.h>
49 #include <linux/ioport.h>
50 #include <linux/platform_device.h>
51 #include <linux/delay.h>
52 #include <linux/err.h>
53 #include <linux/slab.h>
54 #include <linux/clk.h>
55
56 #include <linux/mtd/mtd.h>
57 #include <linux/mtd/nand.h>
58 #include <linux/mtd/nand_ecc.h>
59 #include <linux/mtd/partitions.h>
60
61 #include <asm/io.h>
62
63 #include <asm/arch/regs-nand.h>
64 #include <asm/arch/nand.h>
65
66 #ifdef CONFIG_MTD_NAND_S3C2410_HWECC
67 static int hardware_ecc = 1;
68 #else
69 static int hardware_ecc = 0;
70 #endif
71
72 #ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
73 static int clock_stop = 1;
74 #else
75 static const int clock_stop = 0;
76 #endif
77
78
79 /* new oob placement block for use with hardware ecc generation
80  */
81
82 static struct nand_ecclayout nand_hw_eccoob = {
83         .eccbytes = 3,
84         .eccpos = {0, 1, 2},
85         .oobfree = {{8, 8}}
86 };
87
88 /* controller and mtd information */
89
90 struct s3c2410_nand_info;
91
92 struct s3c2410_nand_mtd {
93         struct mtd_info                 mtd;
94         struct nand_chip                chip;
95         struct s3c2410_nand_set         *set;
96         struct s3c2410_nand_info        *info;
97         int                             scan_res;
98 };
99
100 /* overview of the s3c2410 nand state */
101
102 struct s3c2410_nand_info {
103         /* mtd info */
104         struct nand_hw_control          controller;
105         struct s3c2410_nand_mtd         *mtds;
106         struct s3c2410_platform_nand    *platform;
107
108         /* device info */
109         struct device                   *device;
110         struct resource                 *area;
111         struct clk                      *clk;
112         void __iomem                    *regs;
113         int                             mtd_count;
114
115         unsigned char                   is_s3c2440;
116 };
117
118 /* conversion functions */
119
120 static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
121 {
122         return container_of(mtd, struct s3c2410_nand_mtd, mtd);
123 }
124
125 static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd)
126 {
127         return s3c2410_nand_mtd_toours(mtd)->info;
128 }
129
130 static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev)
131 {
132         return platform_get_drvdata(dev);
133 }
134
135 static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
136 {
137         return dev->dev.platform_data;
138 }
139
140 static inline int allow_clk_stop(struct s3c2410_nand_info *info)
141 {
142         return clock_stop;
143 }
144
145 /* timing calculations */
146
147 #define NS_IN_KHZ 1000000
148
149 static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
150 {
151         int result;
152
153         result = (wanted * clk) / NS_IN_KHZ;
154         result++;
155
156         pr_debug("result %d from %ld, %d\n", result, clk, wanted);
157
158         if (result > max) {
159                 printk("%d ns is too big for current clock rate %ld\n", wanted, clk);
160                 return -1;
161         }
162
163         if (result < 1)
164                 result = 1;
165
166         return result;
167 }
168
169 #define to_ns(ticks,clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk))
170
171 /* controller setup */
172
173 static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_device *pdev)
174 {
175         struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
176         unsigned long clkrate = clk_get_rate(info->clk);
177         int tacls, twrph0, twrph1;
178         unsigned long cfg;
179
180         /* calculate the timing information for the controller */
181
182         clkrate /= 1000;        /* turn clock into kHz for ease of use */
183
184         if (plat != NULL) {
185                 tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
186                 twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
187                 twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
188         } else {
189                 /* default timings */
190                 tacls = 4;
191                 twrph0 = 8;
192                 twrph1 = 8;
193         }
194
195         if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
196                 dev_err(info->device, "cannot get suitable timings\n");
197                 return -EINVAL;
198         }
199
200         dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
201                tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
202
203         if (!info->is_s3c2440) {
204                 cfg = S3C2410_NFCONF_EN;
205                 cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
206                 cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
207                 cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
208         } else {
209                 cfg = S3C2440_NFCONF_TACLS(tacls - 1);
210                 cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
211                 cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
212
213                 /* enable the controller and de-assert nFCE */
214
215                 writel(S3C2440_NFCONT_ENABLE | S3C2440_NFCONT_ENABLE,
216                        info->regs + S3C2440_NFCONT);
217         }
218
219         dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
220
221         writel(cfg, info->regs + S3C2410_NFCONF);
222         return 0;
223 }
224
225 /* select chip */
226
227 static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
228 {
229         struct s3c2410_nand_info *info;
230         struct s3c2410_nand_mtd *nmtd;
231         struct nand_chip *this = mtd->priv;
232         void __iomem *reg;
233         unsigned long cur;
234         unsigned long bit;
235
236         nmtd = this->priv;
237         info = nmtd->info;
238
239         bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
240         reg = info->regs + ((info->is_s3c2440) ? S3C2440_NFCONT : S3C2410_NFCONF);
241
242         if (chip != -1 && allow_clk_stop(info))
243                 clk_enable(info->clk);
244
245         cur = readl(reg);
246
247         if (chip == -1) {
248                 cur |= bit;
249         } else {
250                 if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
251                         dev_err(info->device, "invalid chip %d\n", chip);
252                         return;
253                 }
254
255                 if (info->platform != NULL) {
256                         if (info->platform->select_chip != NULL)
257                                 (info->platform->select_chip) (nmtd->set, chip);
258                 }
259
260                 cur &= ~bit;
261         }
262
263         writel(cur, reg);
264
265         if (chip == -1 && allow_clk_stop(info))
266                 clk_disable(info->clk);
267 }
268
269 /* s3c2410_nand_hwcontrol
270  *
271  * Issue command and address cycles to the chip
272 */
273
274 static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
275                                    unsigned int ctrl)
276 {
277         struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
278         
279         if (cmd == NAND_CMD_NONE)
280                 return;
281
282         if (ctrl & NAND_CLE)
283                 writeb(cmd, info->regs + S3C2410_NFCMD);
284         else
285                 writeb(cmd, info->regs + S3C2410_NFADDR);
286 }
287
288 /* command and control functions */
289
290 static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
291                                    unsigned int ctrl)
292 {
293         struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
294
295         if (cmd == NAND_CMD_NONE)
296                 return;
297
298         if (ctrl & NAND_CLE)
299                 writeb(cmd, info->regs + S3C2440_NFCMD);
300         else
301                 writeb(cmd, info->regs + S3C2440_NFADDR);
302 }
303
304 /* s3c2410_nand_devready()
305  *
306  * returns 0 if the nand is busy, 1 if it is ready
307 */
308
309 static int s3c2410_nand_devready(struct mtd_info *mtd)
310 {
311         struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
312
313         if (info->is_s3c2440)
314                 return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
315         return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
316 }
317
318 /* ECC handling functions */
319
320 static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
321 {
322         pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
323
324         pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
325                  read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
326
327         if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2])
328                 return 0;
329
330         /* we curently have no method for correcting the error */
331
332         return -1;
333 }
334
335 /* ECC functions
336  *
337  * These allow the s3c2410 and s3c2440 to use the controller's ECC
338  * generator block to ECC the data as it passes through]
339 */
340
341 static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
342 {
343         struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
344         unsigned long ctrl;
345
346         ctrl = readl(info->regs + S3C2410_NFCONF);
347         ctrl |= S3C2410_NFCONF_INITECC;
348         writel(ctrl, info->regs + S3C2410_NFCONF);
349 }
350
351 static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
352 {
353         struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
354         unsigned long ctrl;
355
356         ctrl = readl(info->regs + S3C2440_NFCONT);
357         writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
358 }
359
360 static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
361 {
362         struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
363
364         ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0);
365         ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
366         ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
367
368         pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
369
370         return 0;
371 }
372
373 static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
374 {
375         struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
376         unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
377
378         ecc_code[0] = ecc;
379         ecc_code[1] = ecc >> 8;
380         ecc_code[2] = ecc >> 16;
381
382         pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
383
384         return 0;
385 }
386
387 /* over-ride the standard functions for a little more speed. We can
388  * use read/write block to move the data buffers to/from the controller
389 */
390
391 static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
392 {
393         struct nand_chip *this = mtd->priv;
394         readsb(this->IO_ADDR_R, buf, len);
395 }
396
397 static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
398 {
399         struct nand_chip *this = mtd->priv;
400         writesb(this->IO_ADDR_W, buf, len);
401 }
402
403 /* device management functions */
404
405 static int s3c2410_nand_remove(struct platform_device *pdev)
406 {
407         struct s3c2410_nand_info *info = to_nand_info(pdev);
408
409         platform_set_drvdata(pdev, NULL);
410
411         if (info == NULL)
412                 return 0;
413
414         /* first thing we need to do is release all our mtds
415          * and their partitions, then go through freeing the
416          * resources used
417          */
418
419         if (info->mtds != NULL) {
420                 struct s3c2410_nand_mtd *ptr = info->mtds;
421                 int mtdno;
422
423                 for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) {
424                         pr_debug("releasing mtd %d (%p)\n", mtdno, ptr);
425                         nand_release(&ptr->mtd);
426                 }
427
428                 kfree(info->mtds);
429         }
430
431         /* free the common resources */
432
433         if (info->clk != NULL && !IS_ERR(info->clk)) {
434                 if (!allow_clk_stop(info))
435                         clk_disable(info->clk);
436                 clk_put(info->clk);
437         }
438
439         if (info->regs != NULL) {
440                 iounmap(info->regs);
441                 info->regs = NULL;
442         }
443
444         if (info->area != NULL) {
445                 release_resource(info->area);
446                 kfree(info->area);
447                 info->area = NULL;
448         }
449
450         kfree(info);
451
452         return 0;
453 }
454
455 #ifdef CONFIG_MTD_PARTITIONS
456 static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
457                                       struct s3c2410_nand_mtd *mtd,
458                                       struct s3c2410_nand_set *set)
459 {
460         if (set == NULL)
461                 return add_mtd_device(&mtd->mtd);
462
463         if (set->nr_partitions > 0 && set->partitions != NULL) {
464                 return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
465         }
466
467         return add_mtd_device(&mtd->mtd);
468 }
469 #else
470 static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
471                                       struct s3c2410_nand_mtd *mtd,
472                                       struct s3c2410_nand_set *set)
473 {
474         return add_mtd_device(&mtd->mtd);
475 }
476 #endif
477
478 /* s3c2410_nand_init_chip
479  *
480  * init a single instance of an chip
481 */
482
483 static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
484                                    struct s3c2410_nand_mtd *nmtd,
485                                    struct s3c2410_nand_set *set)
486 {
487         struct nand_chip *chip = &nmtd->chip;
488
489         chip->IO_ADDR_R    = info->regs + S3C2410_NFDATA;
490         chip->IO_ADDR_W    = info->regs + S3C2410_NFDATA;
491         chip->cmd_ctrl     = s3c2410_nand_hwcontrol;
492         chip->dev_ready    = s3c2410_nand_devready;
493         chip->write_buf    = s3c2410_nand_write_buf;
494         chip->read_buf     = s3c2410_nand_read_buf;
495         chip->select_chip  = s3c2410_nand_select_chip;
496         chip->chip_delay   = 50;
497         chip->priv         = nmtd;
498         chip->options      = 0;
499         chip->controller   = &info->controller;
500
501         if (info->is_s3c2440) {
502                 chip->IO_ADDR_R  = info->regs + S3C2440_NFDATA;
503                 chip->IO_ADDR_W  = info->regs + S3C2440_NFDATA;
504                 chip->cmd_ctrl   = s3c2440_nand_hwcontrol;
505         }
506
507         nmtd->info         = info;
508         nmtd->mtd.priv     = chip;
509         nmtd->mtd.owner    = THIS_MODULE;
510         nmtd->set          = set;
511
512         if (hardware_ecc) {
513                 chip->ecc.correct   = s3c2410_nand_correct_data;
514                 chip->ecc.hwctl     = s3c2410_nand_enable_hwecc;
515                 chip->ecc.calculate = s3c2410_nand_calculate_ecc;
516                 chip->ecc.mode      = NAND_ECC_HW;
517                 chip->ecc.size      = 512;
518                 chip->ecc.bytes     = 3;
519                 chip->ecc.layout    = &nand_hw_eccoob;
520
521                 if (info->is_s3c2440) {
522                         chip->ecc.hwctl     = s3c2440_nand_enable_hwecc;
523                         chip->ecc.calculate = s3c2440_nand_calculate_ecc;
524                 }
525         } else {
526                 chip->ecc.mode      = NAND_ECC_SOFT;
527         }
528 }
529
530 /* s3c2410_nand_probe
531  *
532  * called by device layer when it finds a device matching
533  * one our driver can handled. This code checks to see if
534  * it can allocate all necessary resources then calls the
535  * nand layer to look for devices
536 */
537
538 static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
539 {
540         struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
541         struct s3c2410_nand_info *info;
542         struct s3c2410_nand_mtd *nmtd;
543         struct s3c2410_nand_set *sets;
544         struct resource *res;
545         int err = 0;
546         int size;
547         int nr_sets;
548         int setno;
549
550         pr_debug("s3c2410_nand_probe(%p)\n", pdev);
551
552         info = kmalloc(sizeof(*info), GFP_KERNEL);
553         if (info == NULL) {
554                 dev_err(&pdev->dev, "no memory for flash info\n");
555                 err = -ENOMEM;
556                 goto exit_error;
557         }
558
559         memzero(info, sizeof(*info));
560         platform_set_drvdata(pdev, info);
561
562         spin_lock_init(&info->controller.lock);
563         init_waitqueue_head(&info->controller.wq);
564
565         /* get the clock source and enable it */
566
567         info->clk = clk_get(&pdev->dev, "nand");
568         if (IS_ERR(info->clk)) {
569                 dev_err(&pdev->dev, "failed to get clock");
570                 err = -ENOENT;
571                 goto exit_error;
572         }
573
574         clk_enable(info->clk);
575
576         /* allocate and map the resource */
577
578         /* currently we assume we have the one resource */
579         res  = pdev->resource;
580         size = res->end - res->start + 1;
581
582         info->area = request_mem_region(res->start, size, pdev->name);
583
584         if (info->area == NULL) {
585                 dev_err(&pdev->dev, "cannot reserve register region\n");
586                 err = -ENOENT;
587                 goto exit_error;
588         }
589
590         info->device     = &pdev->dev;
591         info->platform   = plat;
592         info->regs       = ioremap(res->start, size);
593         info->is_s3c2440 = is_s3c2440;
594
595         if (info->regs == NULL) {
596                 dev_err(&pdev->dev, "cannot reserve register region\n");
597                 err = -EIO;
598                 goto exit_error;
599         }
600
601         dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
602
603         /* initialise the hardware */
604
605         err = s3c2410_nand_inithw(info, pdev);
606         if (err != 0)
607                 goto exit_error;
608
609         sets = (plat != NULL) ? plat->sets : NULL;
610         nr_sets = (plat != NULL) ? plat->nr_sets : 1;
611
612         info->mtd_count = nr_sets;
613
614         /* allocate our information */
615
616         size = nr_sets * sizeof(*info->mtds);
617         info->mtds = kmalloc(size, GFP_KERNEL);
618         if (info->mtds == NULL) {
619                 dev_err(&pdev->dev, "failed to allocate mtd storage\n");
620                 err = -ENOMEM;
621                 goto exit_error;
622         }
623
624         memzero(info->mtds, size);
625
626         /* initialise all possible chips */
627
628         nmtd = info->mtds;
629
630         for (setno = 0; setno < nr_sets; setno++, nmtd++) {
631                 pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
632
633                 s3c2410_nand_init_chip(info, nmtd, sets);
634
635                 nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
636
637                 if (nmtd->scan_res == 0) {
638                         s3c2410_nand_add_partition(info, nmtd, sets);
639                 }
640
641                 if (sets != NULL)
642                         sets++;
643         }
644
645         if (allow_clk_stop(info)) {
646                 dev_info(&pdev->dev, "clock idle support enabled\n");
647                 clk_disable(info->clk);
648         }
649
650         pr_debug("initialised ok\n");
651         return 0;
652
653  exit_error:
654         s3c2410_nand_remove(pdev);
655
656         if (err == 0)
657                 err = -EINVAL;
658         return err;
659 }
660
661 /* PM Support */
662 #ifdef CONFIG_PM
663
664 static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
665 {
666         struct s3c2410_nand_info *info = platform_get_drvdata(dev);
667
668         if (info) {
669                 if (!allow_clk_stop(info))
670                         clk_disable(info->clk);
671         }
672
673         return 0;
674 }
675
676 static int s3c24xx_nand_resume(struct platform_device *dev)
677 {
678         struct s3c2410_nand_info *info = platform_get_drvdata(dev);
679
680         if (info) {
681                 clk_enable(info->clk);
682                 s3c2410_nand_inithw(info, dev);
683
684                 if (allow_clk_stop(info))
685                         clk_disable(info->clk);
686         }
687
688         return 0;
689 }
690
691 #else
692 #define s3c24xx_nand_suspend NULL
693 #define s3c24xx_nand_resume NULL
694 #endif
695
696 /* driver device registration */
697
698 static int s3c2410_nand_probe(struct platform_device *dev)
699 {
700         return s3c24xx_nand_probe(dev, 0);
701 }
702
703 static int s3c2440_nand_probe(struct platform_device *dev)
704 {
705         return s3c24xx_nand_probe(dev, 1);
706 }
707
708 static struct platform_driver s3c2410_nand_driver = {
709         .probe          = s3c2410_nand_probe,
710         .remove         = s3c2410_nand_remove,
711         .suspend        = s3c24xx_nand_suspend,
712         .resume         = s3c24xx_nand_resume,
713         .driver         = {
714                 .name   = "s3c2410-nand",
715                 .owner  = THIS_MODULE,
716         },
717 };
718
719 static struct platform_driver s3c2440_nand_driver = {
720         .probe          = s3c2440_nand_probe,
721         .remove         = s3c2410_nand_remove,
722         .suspend        = s3c24xx_nand_suspend,
723         .resume         = s3c24xx_nand_resume,
724         .driver         = {
725                 .name   = "s3c2440-nand",
726                 .owner  = THIS_MODULE,
727         },
728 };
729
730 static int __init s3c2410_nand_init(void)
731 {
732         printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
733
734         platform_driver_register(&s3c2440_nand_driver);
735         return platform_driver_register(&s3c2410_nand_driver);
736 }
737
738 static void __exit s3c2410_nand_exit(void)
739 {
740         platform_driver_unregister(&s3c2440_nand_driver);
741         platform_driver_unregister(&s3c2410_nand_driver);
742 }
743
744 module_init(s3c2410_nand_init);
745 module_exit(s3c2410_nand_exit);
746
747 MODULE_LICENSE("GPL");
748 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
749 MODULE_DESCRIPTION("S3C24XX MTD NAND driver");