2 * drivers/mtd/nand/nomadik_nand.c
5 * Driver for on-board NAND flash on Nomadik Platforms
7 * Copyright © 2007 STMicroelectronics Pvt. Ltd.
8 * Author: Sachin Verma <sachin.verma@st.com>
10 * Copyright © 2009 Alessandro Rubini
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.
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.
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>
33 #include <linux/slab.h>
34 #include <mach/nand.h>
35 #include <mach/fsmc.h>
37 #include <mtd/mtd-abi.h>
39 struct nomadik_nand_host
{
41 struct nand_chip nand
;
42 void __iomem
*data_va
;
44 void __iomem
*addr_va
;
45 struct nand_bbt_descr
*bbt_desc
;
48 static struct nand_ecclayout nomadik_ecc_layout
= {
50 .eccpos
= { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
55 /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */
56 .oobfree
= { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
59 static void nomadik_ecc_control(struct mtd_info
*mtd
, int mode
)
61 /* No need to enable hw ecc, it's on by default */
64 static void nomadik_cmd_ctrl(struct mtd_info
*mtd
, int cmd
, unsigned int ctrl
)
66 struct nand_chip
*nand
= mtd
->priv
;
67 struct nomadik_nand_host
*host
= nand
->priv
;
69 if (cmd
== NAND_CMD_NONE
)
73 writeb(cmd
, host
->cmd_va
);
75 writeb(cmd
, host
->addr_va
);
78 static int nomadik_nand_probe(struct platform_device
*pdev
)
80 struct nomadik_nand_platform_data
*pdata
= pdev
->dev
.platform_data
;
81 struct nomadik_nand_host
*host
;
83 struct nand_chip
*nand
;
87 /* Allocate memory for the device structure (and zero it) */
88 host
= kzalloc(sizeof(struct nomadik_nand_host
), GFP_KERNEL
);
90 dev_err(&pdev
->dev
, "Failed to allocate device structure.\n");
94 /* Call the client's init function, if any */
98 dev_err(&pdev
->dev
, "Init function failed\n");
102 /* ioremap three regions */
103 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "nand_addr");
108 host
->addr_va
= ioremap(res
->start
, resource_size(res
));
110 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "nand_data");
115 host
->data_va
= ioremap(res
->start
, resource_size(res
));
117 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "nand_cmd");
122 host
->cmd_va
= ioremap(res
->start
, resource_size(res
));
124 if (!host
->addr_va
|| !host
->data_va
|| !host
->cmd_va
) {
129 /* Link all private pointers */
135 host
->mtd
.owner
= THIS_MODULE
;
136 nand
->IO_ADDR_R
= host
->data_va
;
137 nand
->IO_ADDR_W
= host
->data_va
;
138 nand
->cmd_ctrl
= nomadik_cmd_ctrl
;
141 * This stanza declares ECC_HW but uses soft routines. It's because
142 * HW claims to make the calculation but not the correction. However,
143 * I haven't managed to get the desired data out of it until now.
145 nand
->ecc
.mode
= NAND_ECC_SOFT
;
146 nand
->ecc
.layout
= &nomadik_ecc_layout
;
147 nand
->ecc
.hwctl
= nomadik_ecc_control
;
148 nand
->ecc
.size
= 512;
151 nand
->options
= pdata
->options
;
154 * Scan to find existance of the device
156 if (nand_scan(&host
->mtd
, 1)) {
161 #ifdef CONFIG_MTD_PARTITIONS
162 add_mtd_partitions(&host
->mtd
, pdata
->parts
, pdata
->nparts
);
164 pr_info("Registering %s as whole device\n", mtd
->name
);
168 platform_set_drvdata(pdev
, host
);
173 iounmap(host
->cmd_va
);
175 iounmap(host
->data_va
);
177 iounmap(host
->addr_va
);
186 static int nomadik_nand_remove(struct platform_device
*pdev
)
188 struct nomadik_nand_host
*host
= platform_get_drvdata(pdev
);
189 struct nomadik_nand_platform_data
*pdata
= pdev
->dev
.platform_data
;
195 iounmap(host
->cmd_va
);
196 iounmap(host
->data_va
);
197 iounmap(host
->addr_va
);
203 static int nomadik_nand_suspend(struct device
*dev
)
205 struct nomadik_nand_host
*host
= dev_get_drvdata(dev
);
208 ret
= host
->mtd
.suspend(&host
->mtd
);
212 static int nomadik_nand_resume(struct device
*dev
)
214 struct nomadik_nand_host
*host
= dev_get_drvdata(dev
);
216 host
->mtd
.resume(&host
->mtd
);
220 static const struct dev_pm_ops nomadik_nand_pm_ops
= {
221 .suspend
= nomadik_nand_suspend
,
222 .resume
= nomadik_nand_resume
,
225 static struct platform_driver nomadik_nand_driver
= {
226 .probe
= nomadik_nand_probe
,
227 .remove
= nomadik_nand_remove
,
229 .owner
= THIS_MODULE
,
230 .name
= "nomadik_nand",
231 .pm
= &nomadik_nand_pm_ops
,
235 static int __init
nand_nomadik_init(void)
237 pr_info("Nomadik NAND driver\n");
238 return platform_driver_register(&nomadik_nand_driver
);
241 static void __exit
nand_nomadik_exit(void)
243 platform_driver_unregister(&nomadik_nand_driver
);
246 module_init(nand_nomadik_init
);
247 module_exit(nand_nomadik_exit
);
249 MODULE_LICENSE("GPL");
250 MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)");
251 MODULE_DESCRIPTION("NAND driver for Nomadik Platform");