]> err.no Git - linux-2.6/blobdiff - drivers/mtd/nand/s3c2410.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6] / drivers / mtd / nand / s3c2410.c
index 338fda87b9e52cf0ca364b47c4872071cd945bf3..2c262fe03d8af60a93bd6cda681246bf4b7d9ce6 100644 (file)
@@ -18,8 +18,9 @@
  *     20-Jun-2005  BJD  Updated s3c2440 support, fixed timing bug
  *     08-Jul-2005  BJD  Fix OOPS when no platform data supplied
  *     20-Oct-2005  BJD  Fix timing calculation bug
+ *     14-Jan-2006  BJD  Allow clock to be stopped when idle
  *
- * $Id: s3c2410.c,v 1.20 2005/11/07 11:14:31 gleixner Exp $
+ * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 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
@@ -36,9 +37,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-#include <config/mtd/nand/s3c2410/hwecc.h>
-#include <config/mtd/nand/s3c2410/debug.h>
-
 #ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
 #define DEBUG
 #endif
@@ -73,11 +71,17 @@ static int hardware_ecc = 1;
 static int hardware_ecc = 0;
 #endif
 
+#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
+static int clock_stop = 1;
+#else
+static const int clock_stop = 0;
+#endif
+
+
 /* new oob placement block for use with hardware ecc generation
  */
 
-static struct nand_oobinfo nand_hw_eccoob = {
-       .useecc = MTD_NANDECC_AUTOPLACE,
+static struct nand_ecclayout nand_hw_eccoob = {
        .eccbytes = 3,
        .eccpos = {0, 1, 2},
        .oobfree = {{8, 8}}
@@ -135,6 +139,11 @@ static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
        return dev->dev.platform_data;
 }
 
+static inline int allow_clk_stop(struct s3c2410_nand_info *info)
+{
+       return clock_stop;
+}
+
 /* timing calculations */
 
 #define NS_IN_KHZ 1000000
@@ -202,6 +211,11 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_d
                cfg = S3C2440_NFCONF_TACLS(tacls - 1);
                cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
                cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
+
+               /* enable the controller and de-assert nFCE */
+
+               writel(S3C2440_NFCONT_ENABLE | S3C2440_NFCONT_ENABLE,
+                      info->regs + S3C2440_NFCONT);
        }
 
        pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
@@ -227,6 +241,9 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
        bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
        reg = info->regs + ((info->is_s3c2440) ? S3C2440_NFCONT : S3C2410_NFCONF);
 
+       if (chip != -1 && allow_clk_stop(info))
+               clk_enable(info->clk);
+
        cur = readl(reg);
 
        if (chip == -1) {
@@ -246,70 +263,44 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
        }
 
        writel(cur, reg);
+
+       if (chip == -1 && allow_clk_stop(info))
+               clk_disable(info->clk);
 }
 
-/* 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.
+/* s3c2410_nand_hwcontrol
  *
+ * Issue command and address cycles to the chip
 */
 
-static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+                                  unsigned int ctrl)
 {
        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:
-               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;
-       }
+       
+       if (cmd == NAND_CMD_NONE)
+               return;
+
+       if (ctrl & NAND_CLE)
+               writeb(cmd, info->regs + S3C2410_NFCMD);
+       else
+               writeb(cmd, info->regs + S3C2410_NFADDR);
 }
 
 /* command and control functions */
 
-static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+                                  unsigned int ctrl)
 {
        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:
-               printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
-               break;
-
-       case NAND_CTL_SETCLE:
-               chip->IO_ADDR_W = info->regs + S3C2440_NFCMD;
-               break;
-
-       case NAND_CTL_SETALE:
-               chip->IO_ADDR_W = info->regs + S3C2440_NFADDR;
-               break;
-
-               /* NAND_CTL_CLRCLE: */
-               /* NAND_CTL_CLRALE: */
-       default:
-               chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
-               break;
-       }
+
+       if (cmd == NAND_CMD_NONE)
+               return;
+
+       if (ctrl & NAND_CLE)
+               writeb(cmd, info->regs + S3C2440_NFCMD);
+       else
+               writeb(cmd, info->regs + S3C2440_NFADDR);
 }
 
 /* s3c2410_nand_devready()
@@ -442,7 +433,8 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
        /* free the common resources */
 
        if (info->clk != NULL && !IS_ERR(info->clk)) {
-               clk_disable(info->clk);
+               if (!allow_clk_stop(info))
+                       clk_disable(info->clk);
                clk_put(info->clk);
        }
 
@@ -498,7 +490,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
 
        chip->IO_ADDR_R    = info->regs + S3C2410_NFDATA;
        chip->IO_ADDR_W    = info->regs + S3C2410_NFDATA;
-       chip->hwcontrol    = s3c2410_nand_hwcontrol;
+       chip->cmd_ctrl     = s3c2410_nand_hwcontrol;
        chip->dev_ready    = s3c2410_nand_devready;
        chip->write_buf    = s3c2410_nand_write_buf;
        chip->read_buf     = s3c2410_nand_read_buf;
@@ -511,26 +503,29 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
        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;
+               chip->cmd_ctrl   = s3c2440_nand_hwcontrol;
        }
 
        nmtd->info         = info;
        nmtd->mtd.priv     = chip;
+       nmtd->mtd.owner    = THIS_MODULE;
        nmtd->set          = set;
 
        if (hardware_ecc) {
-               chip->correct_data  = s3c2410_nand_correct_data;
-               chip->enable_hwecc  = s3c2410_nand_enable_hwecc;
-               chip->calculate_ecc = s3c2410_nand_calculate_ecc;
-               chip->eccmode       = NAND_ECC_HW3_512;
-               chip->autooob       = &nand_hw_eccoob;
+               chip->ecc.correct   = s3c2410_nand_correct_data;
+               chip->ecc.hwctl     = s3c2410_nand_enable_hwecc;
+               chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+               chip->ecc.mode      = NAND_ECC_HW;
+               chip->ecc.size      = 512;
+               chip->ecc.bytes     = 3;
+               chip->ecc.layout    = &nand_hw_eccoob;
 
                if (info->is_s3c2440) {
-                       chip->enable_hwecc  = s3c2440_nand_enable_hwecc;
-                       chip->calculate_ecc = s3c2440_nand_calculate_ecc;
+                       chip->ecc.hwctl     = s3c2440_nand_enable_hwecc;
+                       chip->ecc.calculate = s3c2440_nand_calculate_ecc;
                }
        } else {
-               chip->eccmode       = NAND_ECC_SOFT;
+               chip->ecc.mode      = NAND_ECC_SOFT;
        }
 }
 
@@ -649,6 +644,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
                        sets++;
        }
 
+       if (allow_clk_stop(info)) {
+               dev_info(&pdev->dev, "clock idle support enabled\n");
+               clk_disable(info->clk);
+       }
+
        pr_debug("initialised ok\n");
        return 0;
 
@@ -660,6 +660,41 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
        return err;
 }
 
+/* PM Support */
+#ifdef CONFIG_PM
+
+static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
+{
+       struct s3c2410_nand_info *info = platform_get_drvdata(dev);
+
+       if (info) {
+               if (!allow_clk_stop(info))
+                       clk_disable(info->clk);
+       }
+
+       return 0;
+}
+
+static int s3c24xx_nand_resume(struct platform_device *dev)
+{
+       struct s3c2410_nand_info *info = platform_get_drvdata(dev);
+
+       if (info) {
+               clk_enable(info->clk);
+               s3c2410_nand_inithw(info, dev);
+
+               if (allow_clk_stop(info))
+                       clk_disable(info->clk);
+       }
+
+       return 0;
+}
+
+#else
+#define s3c24xx_nand_suspend NULL
+#define s3c24xx_nand_resume NULL
+#endif
+
 /* driver device registration */
 
 static int s3c2410_nand_probe(struct platform_device *dev)
@@ -675,6 +710,8 @@ static int s3c2440_nand_probe(struct platform_device *dev)
 static struct platform_driver s3c2410_nand_driver = {
        .probe          = s3c2410_nand_probe,
        .remove         = s3c2410_nand_remove,
+       .suspend        = s3c24xx_nand_suspend,
+       .resume         = s3c24xx_nand_resume,
        .driver         = {
                .name   = "s3c2410-nand",
                .owner  = THIS_MODULE,
@@ -684,6 +721,8 @@ static struct platform_driver s3c2410_nand_driver = {
 static struct platform_driver s3c2440_nand_driver = {
        .probe          = s3c2440_nand_probe,
        .remove         = s3c2410_nand_remove,
+       .suspend        = s3c24xx_nand_suspend,
+       .resume         = s3c24xx_nand_resume,
        .driver         = {
                .name   = "s3c2440-nand",
                .owner  = THIS_MODULE,