mtd: nand: fix S3C NAND clock stop
authorJiri Pinkava <jiri.pinkava@vscht.cz>
Wed, 13 Apr 2011 09:59:30 +0000 (11:59 +0200)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Wed, 25 May 2011 00:53:10 +0000 (01:53 +0100)
Current implementation of s3c2410_nand_select_chip call
clk_disable every time when chip = -1 (de-select). This happend
multiple times even if chip was already de-selected. This causes
disabling clock even if they are already disabled and due to
nature of clock subsytem implementation this causes nand clock
to be disabled and newer enabled again.

Signed-off-by: Jiri Pinkava <jiri.pinkava@vscht.cz>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
drivers/mtd/nand/s3c2410.c

index 33d832d..cea775a 100644 (file)
@@ -55,7 +55,7 @@ static int hardware_ecc = 0;
 #endif
 
 #ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
-static int clock_stop = 1;
+static const int clock_stop = 1;
 #else
 static const int clock_stop = 0;
 #endif
@@ -96,6 +96,12 @@ enum s3c_cpu_type {
        TYPE_S3C2440,
 };
 
+enum s3c_nand_clk_state {
+       CLOCK_DISABLE   = 0,
+       CLOCK_ENABLE,
+       CLOCK_SUSPEND,
+};
+
 /* overview of the s3c2410 nand state */
 
 /**
@@ -111,6 +117,7 @@ enum s3c_cpu_type {
  * @mtd_count: The number of MTDs created from this controller.
  * @save_sel: The contents of @sel_reg to be saved over suspend.
  * @clk_rate: The clock rate from @clk.
+ * @clk_state: The current clock state.
  * @cpu_type: The exact type of this controller.
  */
 struct s3c2410_nand_info {
@@ -129,6 +136,7 @@ struct s3c2410_nand_info {
        int                             mtd_count;
        unsigned long                   save_sel;
        unsigned long                   clk_rate;
+       enum s3c_nand_clk_state         clk_state;
 
        enum s3c_cpu_type               cpu_type;
 
@@ -159,11 +167,33 @@ 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)
+static inline int allow_clk_suspend(struct s3c2410_nand_info *info)
 {
        return clock_stop;
 }
 
+/**
+ * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock.
+ * @info: The controller instance.
+ * @new_state: State to which clock should be set.
+ */
+static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info,
+               enum s3c_nand_clk_state new_state)
+{
+       if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND)
+               return;
+
+       if (info->clk_state == CLOCK_ENABLE) {
+               if (new_state != CLOCK_ENABLE)
+                       clk_disable(info->clk);
+       } else {
+               if (new_state == CLOCK_ENABLE)
+                       clk_enable(info->clk);
+       }
+
+       info->clk_state = new_state;
+}
+
 /* timing calculations */
 
 #define NS_IN_KHZ 1000000
@@ -333,8 +363,8 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
        nmtd = this->priv;
        info = nmtd->info;
 
-       if (chip != -1 && allow_clk_stop(info))
-               clk_enable(info->clk);
+       if (chip != -1)
+               s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
 
        cur = readl(info->sel_reg);
 
@@ -356,8 +386,8 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
 
        writel(cur, info->sel_reg);
 
-       if (chip == -1 && allow_clk_stop(info))
-               clk_disable(info->clk);
+       if (chip == -1)
+               s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
 }
 
 /* s3c2410_nand_hwcontrol
@@ -694,8 +724,7 @@ static int s3c24xx_nand_remove(struct platform_device *pdev)
        /* free the common resources */
 
        if (info->clk != NULL && !IS_ERR(info->clk)) {
-               if (!allow_clk_stop(info))
-                       clk_disable(info->clk);
+               s3c2410_nand_clk_set_state(info, CLOCK_DISABLE);
                clk_put(info->clk);
        }
 
@@ -947,7 +976,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
                goto exit_error;
        }
 
-       clk_enable(info->clk);
+       s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
 
        /* allocate and map the resource */
 
@@ -1026,9 +1055,9 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
                goto exit_error;
        }
 
-       if (allow_clk_stop(info)) {
+       if (allow_clk_suspend(info)) {
                dev_info(&pdev->dev, "clock idle support enabled\n");
-               clk_disable(info->clk);
+               s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
        }
 
        pr_debug("initialised ok\n");
@@ -1059,8 +1088,7 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
 
                writel(info->save_sel | info->sel_bit, info->sel_reg);
 
-               if (!allow_clk_stop(info))
-                       clk_disable(info->clk);
+               s3c2410_nand_clk_set_state(info, CLOCK_DISABLE);
        }
 
        return 0;
@@ -1072,7 +1100,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
        unsigned long sel;
 
        if (info) {
-               clk_enable(info->clk);
+               s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
                s3c2410_nand_inithw(info);
 
                /* Restore the state of the nFCE line. */
@@ -1082,8 +1110,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
                sel |= info->save_sel & info->sel_bit;
                writel(sel, info->sel_reg);
 
-               if (allow_clk_stop(info))
-                       clk_disable(info->clk);
+               s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
        }
 
        return 0;