[MTD] NAND: s3c24xx updates
authorBen Dooks <ben@simtec.co.uk>
Mon, 20 Jun 2005 11:48:25 +0000 (12:48 +0100)
committerThomas Gleixner <tglx@mtd.linutronix.de>
Wed, 29 Jun 2005 12:30:47 +0000 (14:30 +0200)
Fix error in timing generation, Tacls is only in the range 0..3

Add proper support for the s3c2440 NAND controller, which has now
been tested on several s3c2440 implementations.

Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
drivers/mtd/nand/Kconfig
drivers/mtd/nand/s3c2410.c

index f7801eb730cede7b0c844fc892f9cc30fe51a13d..94b1d0e3ec856080d6844eb320e5ff49c0f5137b 100644 (file)
@@ -1,5 +1,5 @@
 # drivers/mtd/nand/Kconfig
-# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $
+# $Id: Kconfig,v 1.31 2005/06/20 12:03:21 bjd Exp $
 
 menu "NAND Flash Device Drivers"
        depends on MTD!=n
@@ -95,10 +95,11 @@ config MTD_NAND_PPCHAMELEONEVB
          This enables the NAND flash driver on the PPChameleon EVB Board.
 
 config MTD_NAND_S3C2410
-       tristate "NAND Flash support for S3C2410 SoC"
+       tristate "NAND Flash support for S3C2410/S3C2440 SoC"
        depends on ARCH_S3C2410 && MTD_NAND
        help
-         This enables the NAND flash controller on the S3C2410.
+         This enables the NAND flash controller on the S3C2410 and S3C2440
+         SoCs
 
          No board specfic support is done by this driver, each board
          must advertise a platform_device for the driver to attach. 
index 64b1d95e3e37c7de348bc265cc1876590f41094a..630a9c0edf3147e4eafba84186379a57614f5283 100644 (file)
@@ -1,10 +1,10 @@
 /* linux/drivers/mtd/nand/s3c2410.c
  *
- * Copyright (c) 2004 Simtec Electronics
+ * Copyright (c) 2004,2005 Simtec Electronics
  *     http://www.simtec.co.uk/products/SWLINUX/
  *     Ben Dooks <ben@simtec.co.uk>
  *
- * Samsung S3C2410 NAND driver
+ * Samsung S3C2410/S3C240 NAND driver
  *
  * Changelog:
  *     21-Sep-2004  BJD  Initial version
  *     12-Oct-2004  BJD  Fixed errors in use of platform data
  *     18-Feb-2005  BJD  Fix sparse errors
  *     14-Mar-2005  BJD  Applied tglx's code reduction patch
+ *     02-May-2005  BJD  Fixed s3c2440 support
+ *     02-May-2005  BJD  Reduced hwcontrol decode
+ *     20-Jun-2005  BJD  Updated s3c2440 support, fixed timing bug
  *
- * $Id: s3c2410.c,v 1.12 2005/03/17 11:31:26 bjd Exp $
+ * $Id: s3c2410.c,v 1.13 2005/06/20 11:48:21 bjd Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -104,6 +107,8 @@ struct s3c2410_nand_info {
        struct clk                      *clk;
        void __iomem                    *regs;
        int                             mtd_count;
+
+       unsigned char                   is_s3c2440;
 };
 
 /* conversion functions */
@@ -168,12 +173,12 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
        /* calculate the timing information for the controller */
 
        if (plat != NULL) {
-               tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8);
+               tacls  = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
                twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
                twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
        } else {
                /* default timings */
-               tacls = 8;
+               tacls = 4;
                twrph0 = 8;
                twrph1 = 8;
        }
@@ -188,10 +193,16 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
               to_ns(twrph0, clkrate),
               to_ns(twrph1, clkrate));
 
-       cfg  = S3C2410_NFCONF_EN;
-       cfg |= S3C2410_NFCONF_TACLS(tacls-1);
-       cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
-       cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
+       if (!info->is_s3c2440) {
+               cfg  = S3C2410_NFCONF_EN;
+               cfg |= S3C2410_NFCONF_TACLS(tacls-1);
+               cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
+               cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
+       } else {
+               cfg   = S3C2440_NFCONF_TACLS(tacls-1);
+               cfg  |= S3C2440_NFCONF_TWRPH0(twrph0-1);
+               cfg  |= S3C2440_NFCONF_TWRPH1(twrph1-1);
+       }
 
        pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
 
@@ -206,15 +217,20 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
        struct s3c2410_nand_info *info;
        struct s3c2410_nand_mtd *nmtd; 
        struct nand_chip *this = mtd->priv;
+       void __iomem *reg;
        unsigned long cur;
+       unsigned long bit;
 
        nmtd = this->priv;
        info = nmtd->info;
 
-       cur = readl(info->regs + S3C2410_NFCONF);
+       bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
+       reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF);
+
+       cur = readl(reg);
 
        if (chip == -1) {
-               cur |= S3C2410_NFCONF_nFCE;
+               cur |= bit;
        } else {
                if (chip > nmtd->set->nr_chips) {
                        printk(KERN_ERR PFX "chip %d out of range\n", chip);
@@ -226,45 +242,72 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
                                (info->platform->select_chip)(nmtd->set, chip);
                }
 
-               cur &= ~S3C2410_NFCONF_nFCE;
+               cur &= ~bit;
        }
 
-       writel(cur, info->regs + S3C2410_NFCONF);
+       writel(cur, reg);
 }
 
-/* command and control functions */
+/* command and control functions 
+ *
+ * Note, these all use tglx's method of changing the IO_ADDR_W field
+ * to make the code simpler, and use the nand layer's code to issue the
+ * command and address sequences via the proper IO ports.
+ *
+*/
 
 static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
 {
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
        struct nand_chip *chip = mtd->priv;
-       unsigned long cur;
 
        switch (cmd) {
        case NAND_CTL_SETNCE:
-               cur = readl(info->regs + S3C2410_NFCONF);
-               cur &= ~S3C2410_NFCONF_nFCE;
-               writel(cur, info->regs + S3C2410_NFCONF);
+       case NAND_CTL_CLRNCE:
+               printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
+               break;
+
+       case NAND_CTL_SETCLE:
+               chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;
+               break;
+
+       case NAND_CTL_SETALE:
+               chip->IO_ADDR_W = info->regs + S3C2410_NFADDR;
+               break;
+
+               /* NAND_CTL_CLRCLE: */
+               /* NAND_CTL_CLRALE: */
+       default:
+               chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
                break;
+       }
+}
+
+/* command and control functions */
+
+static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       struct nand_chip *chip = mtd->priv;
 
+       switch (cmd) {
+       case NAND_CTL_SETNCE:
        case NAND_CTL_CLRNCE:
-               cur = readl(info->regs + S3C2410_NFCONF);
-               cur |= S3C2410_NFCONF_nFCE;
-               writel(cur, info->regs + S3C2410_NFCONF);
+               printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
                break;
 
        case NAND_CTL_SETCLE:
-               chip->IO_ADDR_W    = info->regs + S3C2410_NFCMD;
+               chip->IO_ADDR_W = info->regs + S3C2440_NFCMD;
                break;
 
        case NAND_CTL_SETALE:
-               chip->IO_ADDR_W    = info->regs + S3C2410_NFADDR;
+               chip->IO_ADDR_W = info->regs + S3C2440_NFADDR;
                break;
 
                /* NAND_CTL_CLRCLE: */
                /* NAND_CTL_CLRALE: */
        default:
-               chip->IO_ADDR_W    = info->regs + S3C2410_NFDATA;
+               chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
                break;
        }
 }
@@ -278,9 +321,12 @@ static int s3c2410_nand_devready(struct mtd_info *mtd)
 {
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
        
+       if (info->is_s3c2440)
+               return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
        return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
 }
 
+
 /* ECC handling functions */
 
 static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
@@ -303,6 +349,12 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
        return -1;
 }
 
+/* ECC functions
+ *
+ * These allow the s3c2410 and s3c2440 to use the controller's ECC
+ * generator block to ECC the data as it passes through]
+*/
+
 static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
 {
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
@@ -313,6 +365,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
        writel(ctrl, info->regs + S3C2410_NFCONF);
 }
 
+static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       unsigned long ctrl;
+
+       ctrl = readl(info->regs + S3C2440_NFCONT);
+       writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
+}
+
 static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
                                      const u_char *dat, u_char *ecc_code)
 {
@@ -329,7 +390,26 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
 }
 
 
-/* over-ride the standard functions for a little more speed? */
+static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd,
+                                     const u_char *dat, u_char *ecc_code)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
+
+       ecc_code[0] = ecc;
+       ecc_code[1] = ecc >> 8;
+       ecc_code[2] = ecc >> 16;
+
+       pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
+                ecc_code[0], ecc_code[1], ecc_code[2]);
+
+       return 0;
+}
+
+
+/* over-ride the standard functions for a little more speed. We can
+ * use read/write block to move the data buffers to/from the controller
+*/
 
 static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 {
@@ -444,6 +524,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
        chip->options      = 0;
        chip->controller   = &info->controller;
 
+       if (info->is_s3c2440) {
+               chip->IO_ADDR_R  = info->regs + S3C2440_NFDATA;
+               chip->IO_ADDR_W  = info->regs + S3C2440_NFDATA;
+               chip->hwcontrol  = s3c2440_nand_hwcontrol;
+       }
+
        nmtd->info         = info;
        nmtd->mtd.priv     = chip;
        nmtd->set          = set;
@@ -454,6 +540,11 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
                chip->calculate_ecc = s3c2410_nand_calculate_ecc;
                chip->eccmode       = NAND_ECC_HW3_512;
                chip->autooob       = &nand_hw_eccoob;
+
+               if (info->is_s3c2440) {
+                       chip->enable_hwecc  = s3c2440_nand_enable_hwecc;
+                       chip->calculate_ecc = s3c2440_nand_calculate_ecc;
+               }
        } else {
                chip->eccmode       = NAND_ECC_SOFT;
        }
@@ -467,7 +558,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
  * nand layer to look for devices
 */
 
-static int s3c2410_nand_probe(struct device *dev)
+static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct s3c2410_platform_nand *plat = to_nand_plat(dev);
@@ -493,6 +584,7 @@ static int s3c2410_nand_probe(struct device *dev)
        dev_set_drvdata(dev, info);
 
        spin_lock_init(&info->controller.lock);
+       init_waitqueue_head(&info->controller.wq);
 
        /* get the clock source and enable it */
 
@@ -508,7 +600,8 @@ static int s3c2410_nand_probe(struct device *dev)
 
        /* allocate and map the resource */
 
-       res = pdev->resource;  /* assume that the flash has one resource */
+       /* currently we assume we have the one resource */
+       res  = pdev->resource;
        size = res->end - res->start + 1;
 
        info->area = request_mem_region(res->start, size, pdev->name);
@@ -519,9 +612,10 @@ static int s3c2410_nand_probe(struct device *dev)
                goto exit_error;
        }
 
-       info->device = dev;
-       info->platform = plat;
-       info->regs = ioremap(res->start, size);
+       info->device     = dev;
+       info->platform   = plat;
+       info->regs       = ioremap(res->start, size);
+       info->is_s3c2440 = is_s3c2440;
 
        if (info->regs == NULL) {
                printk(KERN_ERR PFX "cannot reserve register region\n");
@@ -586,6 +680,18 @@ static int s3c2410_nand_probe(struct device *dev)
        return err;
 }
 
+/* driver device registration */
+
+static int s3c2410_nand_probe(struct device *dev)
+{
+       return s3c24xx_nand_probe(dev, 0);
+}
+
+static int s3c2440_nand_probe(struct device *dev)
+{
+       return s3c24xx_nand_probe(dev, 1);
+}
+
 static struct device_driver s3c2410_nand_driver = {
        .name           = "s3c2410-nand",
        .bus            = &platform_bus_type,
@@ -593,14 +699,24 @@ static struct device_driver s3c2410_nand_driver = {
        .remove         = s3c2410_nand_remove,
 };
 
+static struct device_driver s3c2440_nand_driver = {
+       .name           = "s3c2440-nand",
+       .bus            = &platform_bus_type,
+       .probe          = s3c2440_nand_probe,
+       .remove         = s3c2410_nand_remove,
+};
+
 static int __init s3c2410_nand_init(void)
 {
-       printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n");
+       printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
+
+       driver_register(&s3c2440_nand_driver);
        return driver_register(&s3c2410_nand_driver);
 }
 
 static void __exit s3c2410_nand_exit(void)
 {
+       driver_unregister(&s3c2440_nand_driver);
        driver_unregister(&s3c2410_nand_driver);
 }
 
@@ -609,4 +725,4 @@ module_exit(s3c2410_nand_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("S3C2410 MTD NAND driver");
+MODULE_DESCRIPTION("S3C24XX MTD NAND driver");