summary refs log tree commit diff
path: root/drivers/mtd/nand/s3c2410.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/s3c2410.c')
-rw-r--r--drivers/mtd/nand/s3c2410.c164
1 files changed, 115 insertions, 49 deletions
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 2c262fe03d8a..ff5cef24d5bb 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -63,8 +63,6 @@
 #include <asm/arch/regs-nand.h>
 #include <asm/arch/nand.h>
 
-#define PFX "s3c2410-nand: "
-
 #ifdef CONFIG_MTD_NAND_S3C2410_HWECC
 static int hardware_ecc = 1;
 #else
@@ -99,6 +97,12 @@ struct s3c2410_nand_mtd {
 	int				scan_res;
 };
 
+enum s3c_cpu_type {
+	TYPE_S3C2410,
+	TYPE_S3C2412,
+	TYPE_S3C2440,
+};
+
 /* overview of the s3c2410 nand state */
 
 struct s3c2410_nand_info {
@@ -112,9 +116,11 @@ struct s3c2410_nand_info {
 	struct resource			*area;
 	struct clk			*clk;
 	void __iomem			*regs;
+	void __iomem			*sel_reg;
+	int				sel_bit;
 	int				mtd_count;
 
-	unsigned char			is_s3c2440;
+	enum s3c_cpu_type		cpu_type;
 };
 
 /* conversion functions */
@@ -148,7 +154,7 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info)
 
 #define NS_IN_KHZ 1000000
 
-static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
+static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
 {
 	int result;
 
@@ -172,53 +178,58 @@ static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
 
 /* controller setup */
 
-static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_device *pdev)
+static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
+			       struct platform_device *pdev)
 {
 	struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
 	unsigned long clkrate = clk_get_rate(info->clk);
+	int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
 	int tacls, twrph0, twrph1;
-	unsigned long cfg;
+	unsigned long cfg = 0;
 
 	/* calculate the timing information for the controller */
 
 	clkrate /= 1000;	/* turn clock into kHz for ease of use */
 
 	if (plat != NULL) {
-		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);
+		tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);
+		twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);
+		twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);
 	} else {
 		/* default timings */
-		tacls = 4;
+		tacls = tacls_max;
 		twrph0 = 8;
 		twrph1 = 8;
 	}
 
 	if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
-		printk(KERN_ERR PFX "cannot get timings suitable for board\n");
+		dev_err(info->device, "cannot get suitable timings\n");
 		return -EINVAL;
 	}
 
-	printk(KERN_INFO PFX "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
+	dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
 	       tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
 
-	if (!info->is_s3c2440) {
+ 	switch (info->cpu_type) {
+ 	case TYPE_S3C2410:
 		cfg = S3C2410_NFCONF_EN;
 		cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
 		cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
 		cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
-	} else {
+		break;
+
+ 	case TYPE_S3C2440:
+ 	case TYPE_S3C2412:
 		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);
+		writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
 	}
 
-	pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
+	dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
 
 	writel(cfg, info->regs + S3C2410_NFCONF);
 	return 0;
@@ -231,26 +242,21 @@ 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;
 
-	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);
+	cur = readl(info->sel_reg);
 
 	if (chip == -1) {
-		cur |= bit;
+		cur |= info->sel_bit;
 	} else {
 		if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
-			printk(KERN_ERR PFX "chip %d out of range\n", chip);
+			dev_err(info->device, "invalid chip %d\n", chip);
 			return;
 		}
 
@@ -259,10 +265,10 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
 				(info->platform->select_chip) (nmtd->set, chip);
 		}
 
-		cur &= ~bit;
+		cur &= ~info->sel_bit;
 	}
 
-	writel(cur, reg);
+	writel(cur, info->sel_reg);
 
 	if (chip == -1 && allow_clk_stop(info))
 		clk_disable(info->clk);
@@ -311,15 +317,25 @@ static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
 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;
 }
 
+static int s3c2440_nand_devready(struct mtd_info *mtd)
+{
+	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+	return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
+}
+
+static int s3c2412_nand_devready(struct mtd_info *mtd)
+{
+	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+	return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY;
+}
+
 /* ECC handling functions */
 
-static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+				     u_char *read_ecc, u_char *calc_ecc)
 {
 	pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
 
@@ -487,11 +503,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
 				   struct s3c2410_nand_set *set)
 {
 	struct nand_chip *chip = &nmtd->chip;
+	void __iomem *regs = info->regs;
 
-	chip->IO_ADDR_R	   = info->regs + S3C2410_NFDATA;
-	chip->IO_ADDR_W    = info->regs + S3C2410_NFDATA;
-	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;
 	chip->select_chip  = s3c2410_nand_select_chip;
@@ -500,11 +513,37 @@ 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->cmd_ctrl   = s3c2440_nand_hwcontrol;
-	}
+	switch (info->cpu_type) {
+	case TYPE_S3C2410:
+		chip->IO_ADDR_W = regs + S3C2410_NFDATA;
+		info->sel_reg   = regs + S3C2410_NFCONF;
+		info->sel_bit	= S3C2410_NFCONF_nFCE;
+		chip->cmd_ctrl  = s3c2410_nand_hwcontrol;
+		chip->dev_ready = s3c2410_nand_devready;
+		break;
+
+	case TYPE_S3C2440:
+		chip->IO_ADDR_W = regs + S3C2440_NFDATA;
+		info->sel_reg   = regs + S3C2440_NFCONT;
+		info->sel_bit	= S3C2440_NFCONT_nFCE;
+		chip->cmd_ctrl  = s3c2440_nand_hwcontrol;
+		chip->dev_ready = s3c2440_nand_devready;
+		break;
+
+	case TYPE_S3C2412:
+		chip->IO_ADDR_W = regs + S3C2440_NFDATA;
+		info->sel_reg   = regs + S3C2440_NFCONT;
+		info->sel_bit	= S3C2412_NFCONT_nFCE0;
+		chip->cmd_ctrl  = s3c2440_nand_hwcontrol;
+		chip->dev_ready = s3c2412_nand_devready;
+
+		if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT)
+			dev_info(info->device, "System booted from NAND\n");
+
+		break;
+  	}
+
+	chip->IO_ADDR_R = chip->IO_ADDR_W;
 
 	nmtd->info	   = info;
 	nmtd->mtd.priv	   = chip;
@@ -512,17 +551,25 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
 	nmtd->set	   = set;
 
 	if (hardware_ecc) {
-		chip->ecc.correct   = s3c2410_nand_correct_data;
-		chip->ecc.hwctl	    = s3c2410_nand_enable_hwecc;
 		chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+		chip->ecc.correct   = s3c2410_nand_correct_data;
 		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->ecc.hwctl     = s3c2440_nand_enable_hwecc;
-			chip->ecc.calculate = s3c2440_nand_calculate_ecc;
+		switch (info->cpu_type) {
+		case TYPE_S3C2410:
+			chip->ecc.hwctl	    = s3c2410_nand_enable_hwecc;
+			chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+			break;
+
+		case TYPE_S3C2412:
+		case TYPE_S3C2440:
+  			chip->ecc.hwctl     = s3c2440_nand_enable_hwecc;
+  			chip->ecc.calculate = s3c2440_nand_calculate_ecc;
+			break;
+
 		}
 	} else {
 		chip->ecc.mode	    = NAND_ECC_SOFT;
@@ -537,7 +584,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
  * nand layer to look for devices
 */
 
-static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
+static int s3c24xx_nand_probe(struct platform_device *pdev,
+			      enum s3c_cpu_type cpu_type)
 {
 	struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
 	struct s3c2410_nand_info *info;
@@ -592,7 +640,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
 	info->device     = &pdev->dev;
 	info->platform   = plat;
 	info->regs       = ioremap(res->start, size);
-	info->is_s3c2440 = is_s3c2440;
+	info->cpu_type   = cpu_type;
 
 	if (info->regs == NULL) {
 		dev_err(&pdev->dev, "cannot reserve register region\n");
@@ -699,12 +747,17 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
 
 static int s3c2410_nand_probe(struct platform_device *dev)
 {
-	return s3c24xx_nand_probe(dev, 0);
+	return s3c24xx_nand_probe(dev, TYPE_S3C2410);
 }
 
 static int s3c2440_nand_probe(struct platform_device *dev)
 {
-	return s3c24xx_nand_probe(dev, 1);
+	return s3c24xx_nand_probe(dev, TYPE_S3C2440);
+}
+
+static int s3c2412_nand_probe(struct platform_device *dev)
+{
+	return s3c24xx_nand_probe(dev, TYPE_S3C2412);
 }
 
 static struct platform_driver s3c2410_nand_driver = {
@@ -729,16 +782,29 @@ static struct platform_driver s3c2440_nand_driver = {
 	},
 };
 
+static struct platform_driver s3c2412_nand_driver = {
+	.probe		= s3c2412_nand_probe,
+	.remove		= s3c2410_nand_remove,
+	.suspend	= s3c24xx_nand_suspend,
+	.resume		= s3c24xx_nand_resume,
+	.driver		= {
+		.name	= "s3c2412-nand",
+		.owner	= THIS_MODULE,
+	},
+};
+
 static int __init s3c2410_nand_init(void)
 {
 	printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
 
+	platform_driver_register(&s3c2412_nand_driver);
 	platform_driver_register(&s3c2440_nand_driver);
 	return platform_driver_register(&s3c2410_nand_driver);
 }
 
 static void __exit s3c2410_nand_exit(void)
 {
+	platform_driver_unregister(&s3c2412_nand_driver);
 	platform_driver_unregister(&s3c2440_nand_driver);
 	platform_driver_unregister(&s3c2410_nand_driver);
 }