tree 16b2984a1c648cd2bc5e5cd4ba85bc24ae8fa8b3
parent d7e78d4f2173298c34e88f496c3acea247feec61
author Ben Dooks <[EMAIL PROTECTED]> Mon, 20 Jun 2005 12:48:25 +0100
committer Thomas Gleixner <[EMAIL PROTECTED]> Wed, 29 Jun 2005 14:30:47 +0200

[MTD] NAND: s3c24xx updates

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 <[EMAIL PROTECTED]>
Signed-off-by: Thomas Gleixner <[EMAIL PROTECTED]>

 drivers/mtd/nand/Kconfig   |    7 +
 drivers/mtd/nand/s3c2410.c |  180 +++++++++++++++++++++++++++++++++++++--------
 2 files changed, 152 insertions(+), 35 deletions(-)

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -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. 
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -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 <[EMAIL PROTECTED]>
  *
- * Samsung S3C2410 NAND driver
+ * Samsung S3C2410/S3C240 NAND driver
  *
  * Changelog:
  *     21-Sep-2004  BJD  Initial version
@@ -13,8 +13,11 @@
  *     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 s3
        /* 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 s3
               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(str
        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(str
                                (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 
 {
        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(str
        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(st
        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(st
 }
 
 
-/* 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(struc
        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(struc
                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(struc
  * 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 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 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 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 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
        .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 <[EMAIL PROTECTED]>");
-MODULE_DESCRIPTION("S3C2410 MTD NAND driver");
+MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to