aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorAlessandro Rubini <rubini@unipv.it>2009-07-29 12:51:56 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-09-20 08:59:42 -0400
commit63234717d170d39ee9cc873f29930b0fb142a114 (patch)
tree8a47072187027064f3e0b0ecdb6c65baf7ad0455 /drivers/mtd
parent6469f540ea37d53db089c8fea9c0c77a3d9353d4 (diff)
mtd: nand: driver for Nomadik 8815 SoC (on NHK8815 board)
Signed-off-by: Alessandro Rubini <rubini@unipv.it> Acked-by: Andrea Gallo <andrea.gallo@stericsson.com> Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/Kconfig6
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/nomadik_nand.c250
3 files changed, 257 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2c9a0ed4aed8..2fda0b615246 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -443,6 +443,12 @@ config MTD_NAND_MXC
443 This enables the driver for the NAND flash controller on the 443 This enables the driver for the NAND flash controller on the
444 MXC processors. 444 MXC processors.
445 445
446config MTD_NAND_NOMADIK
447 tristate "ST Nomadik 8815 NAND support"
448 depends on ARCH_NOMADIK
449 help
450 Driver for the NAND flash controller on the Nomadik, with ECC.
451
446config MTD_NAND_SH_FLCTL 452config MTD_NAND_SH_FLCTL
447 tristate "Support for NAND on Renesas SuperH FLCTL" 453 tristate "Support for NAND on Renesas SuperH FLCTL"
448 depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 454 depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 4f7b1890f83c..6950d3dabf10 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -41,5 +41,6 @@ obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
41obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o 41obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
42obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o 42obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
43obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o 43obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o
44obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
44 45
45nand-objs := nand_base.o nand_bbt.o 46nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
new file mode 100644
index 000000000000..7c302d55910e
--- /dev/null
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -0,0 +1,250 @@
1/*
2 * drivers/mtd/nand/nomadik_nand.c
3 *
4 * Overview:
5 * Driver for on-board NAND flash on Nomadik Platforms
6 *
7 * Copyright © 2007 STMicroelectronics Pvt. Ltd.
8 * Author: Sachin Verma <sachin.verma@st.com>
9 *
10 * Copyright © 2009 Alessandro Rubini
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 */
23
24#include <linux/init.h>
25#include <linux/module.h>
26#include <linux/types.h>
27#include <linux/mtd/mtd.h>
28#include <linux/mtd/nand.h>
29#include <linux/mtd/nand_ecc.h>
30#include <linux/platform_device.h>
31#include <linux/mtd/partitions.h>
32#include <linux/io.h>
33#include <mach/nand.h>
34#include <mach/fsmc.h>
35
36#include <mtd/mtd-abi.h>
37
38struct nomadik_nand_host {
39 struct mtd_info mtd;
40 struct nand_chip nand;
41 void __iomem *data_va;
42 void __iomem *cmd_va;
43 void __iomem *addr_va;
44 struct nand_bbt_descr *bbt_desc;
45};
46
47static struct nand_ecclayout nomadik_ecc_layout = {
48 .eccbytes = 3 * 4,
49 .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
50 0x02, 0x03, 0x04,
51 0x12, 0x13, 0x14,
52 0x22, 0x23, 0x24,
53 0x32, 0x33, 0x34},
54 /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */
55 .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
56};
57
58static void nomadik_ecc_control(struct mtd_info *mtd, int mode)
59{
60 /* No need to enable hw ecc, it's on by default */
61}
62
63static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
64{
65 struct nand_chip *nand = mtd->priv;
66 struct nomadik_nand_host *host = nand->priv;
67
68 if (cmd == NAND_CMD_NONE)
69 return;
70
71 if (ctrl & NAND_CLE)
72 writeb(cmd, host->cmd_va);
73 else
74 writeb(cmd, host->addr_va);
75}
76
77static int nomadik_nand_probe(struct platform_device *pdev)
78{
79 struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data;
80 struct nomadik_nand_host *host;
81 struct mtd_info *mtd;
82 struct nand_chip *nand;
83 struct resource *res;
84 int ret = 0;
85
86 /* Allocate memory for the device structure (and zero it) */
87 host = kzalloc(sizeof(struct nomadik_nand_host), GFP_KERNEL);
88 if (!host) {
89 dev_err(&pdev->dev, "Failed to allocate device structure.\n");
90 return -ENOMEM;
91 }
92
93 /* Call the client's init function, if any */
94 if (pdata->init)
95 ret = pdata->init();
96 if (ret < 0) {
97 dev_err(&pdev->dev, "Init function failed\n");
98 goto err;
99 }
100
101 /* ioremap three regions */
102 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
103 if (!res) {
104 ret = -EIO;
105 goto err_unmap;
106 }
107 host->addr_va = ioremap(res->start, res->end - res->start + 1);
108
109 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
110 if (!res) {
111 ret = -EIO;
112 goto err_unmap;
113 }
114 host->data_va = ioremap(res->start, res->end - res->start + 1);
115
116 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
117 if (!res) {
118 ret = -EIO;
119 goto err_unmap;
120 }
121 host->cmd_va = ioremap(res->start, res->end - res->start + 1);
122
123 if (!host->addr_va || !host->data_va || !host->cmd_va) {
124 ret = -ENOMEM;
125 goto err_unmap;
126 }
127
128 /* Link all private pointers */
129 mtd = &host->mtd;
130 nand = &host->nand;
131 mtd->priv = nand;
132 nand->priv = host;
133
134 host->mtd.owner = THIS_MODULE;
135 nand->IO_ADDR_R = host->data_va;
136 nand->IO_ADDR_W = host->data_va;
137 nand->cmd_ctrl = nomadik_cmd_ctrl;
138
139 /*
140 * This stanza declares ECC_HW but uses soft routines. It's because
141 * HW claims to make the calculation but not the correction. However,
142 * I haven't managed to get the desired data out of it until now.
143 */
144 nand->ecc.mode = NAND_ECC_SOFT;
145 nand->ecc.layout = &nomadik_ecc_layout;
146 nand->ecc.hwctl = nomadik_ecc_control;
147 nand->ecc.size = 512;
148 nand->ecc.bytes = 3;
149
150 nand->options = pdata->options;
151
152 /*
153 * Scan to find existance of the device
154 */
155 if (nand_scan(&host->mtd, 1)) {
156 ret = -ENXIO;
157 goto err_unmap;
158 }
159
160#ifdef CONFIG_MTD_PARTITIONS
161 add_mtd_partitions(&host->mtd, pdata->parts, pdata->nparts);
162#else
163 pr_info("Registering %s as whole device\n", mtd->name);
164 add_mtd_device(mtd);
165#endif
166
167 platform_set_drvdata(pdev, host);
168 return 0;
169
170 err_unmap:
171 if (host->cmd_va)
172 iounmap(host->cmd_va);
173 if (host->data_va)
174 iounmap(host->data_va);
175 if (host->addr_va)
176 iounmap(host->addr_va);
177 err:
178 kfree(host);
179 return ret;
180}
181
182/*
183 * Clean up routine
184 */
185static int nomadik_nand_remove(struct platform_device *pdev)
186{
187 struct nomadik_nand_host *host = platform_get_drvdata(pdev);
188 struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data;
189
190 if (pdata->exit)
191 pdata->exit();
192
193 if (host) {
194 iounmap(host->cmd_va);
195 iounmap(host->data_va);
196 iounmap(host->addr_va);
197 kfree(host);
198 }
199 return 0;
200}
201
202static int nomadik_nand_suspend(struct device *dev)
203{
204 struct nomadik_nand_host *host = dev_get_drvdata(dev);
205 int ret = 0;
206 if (host)
207 ret = host->mtd.suspend(&host->mtd);
208 return ret;
209}
210
211static int nomadik_nand_resume(struct device *dev)
212{
213 struct nomadik_nand_host *host = dev_get_drvdata(dev);
214 if (host)
215 host->mtd.resume(&host->mtd);
216 return 0;
217}
218
219static struct dev_pm_ops nomadik_nand_pm_ops = {
220 .suspend = nomadik_nand_suspend,
221 .resume = nomadik_nand_resume,
222};
223
224static struct platform_driver nomadik_nand_driver = {
225 .probe = nomadik_nand_probe,
226 .remove = nomadik_nand_remove,
227 .driver = {
228 .owner = THIS_MODULE,
229 .name = "nomadik_nand",
230 .pm = &nomadik_nand_pm_ops,
231 },
232};
233
234static int __init nand_nomadik_init(void)
235{
236 pr_info("Nomadik NAND driver\n");
237 return platform_driver_register(&nomadik_nand_driver);
238}
239
240static void __exit nand_nomadik_exit(void)
241{
242 platform_driver_unregister(&nomadik_nand_driver);
243}
244
245module_init(nand_nomadik_init);
246module_exit(nand_nomadik_exit);
247
248MODULE_LICENSE("GPL");
249MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)");
250MODULE_DESCRIPTION("NAND driver for Nomadik Platform");