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 <mach/nand.h>
34 #include <mach/fsmc.h>
36 #include <mtd/mtd-abi.h>
38 struct nomadik_nand_host
{
40 struct nand_chip nand
;
41 void __iomem
*data_va
;
43 void __iomem
*addr_va
;
44 struct nand_bbt_descr
*bbt_desc
;
47 static struct nand_ecclayout nomadik_ecc_layout
= {
49 .eccpos
= { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
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} },
58 static void nomadik_ecc_control(struct mtd_info
*mtd
, int mode
)
60 /* No need to enable hw ecc, it's on by default */
63 static void nomadik_cmd_ctrl(struct mtd_info
*mtd
, int cmd
, unsigned int ctrl
)
65 struct nand_chip
*nand
= mtd
->priv
;
66 struct nomadik_nand_host
*host
= nand
->priv
;
68 if (cmd
== NAND_CMD_NONE
)
72 writeb(cmd
, host
->cmd_va
);
74 writeb(cmd
, host
->addr_va
);
77 static int nomadik_nand_probe(struct platform_device
*pdev
)
79 struct nomadik_nand_platform_data
*pdata
= pdev
->dev
.platform_data
;
80 struct nomadik_nand_host
*host
;
82 struct nand_chip
*nand
;
86 /* Allocate memory for the device structure (and zero it) */
87 host
= kzalloc(sizeof(struct nomadik_nand_host
), GFP_KERNEL
);
89 dev_err(&pdev
->dev
, "Failed to allocate device structure.\n");
93 /* Call the client's init function, if any */
97 dev_err(&pdev
->dev
, "Init function failed\n");
101 /* ioremap three regions */
102 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "nand_addr");
107 host
->addr_va
= ioremap(res
->start
, res
->end
- res
->start
+ 1);
109 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "nand_data");
114 host
->data_va
= ioremap(res
->start
, res
->end
- res
->start
+ 1);
116 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "nand_cmd");
121 host
->cmd_va
= ioremap(res
->start
, res
->end
- res
->start
+ 1);
123 if (!host
->addr_va
|| !host
->data_va
|| !host
->cmd_va
) {
128 /* Link all private pointers */
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
;
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.
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;
150 nand
->options
= pdata
->options
;
153 * Scan to find existance of the device
155 if (nand_scan(&host
->mtd
, 1)) {
160 #ifdef CONFIG_MTD_PARTITIONS
161 add_mtd_partitions(&host
->mtd
, pdata
->parts
, pdata
->nparts
);
163 pr_info("Registering %s as whole device\n", mtd
->name
);
167 platform_set_drvdata(pdev
, host
);
172 iounmap(host
->cmd_va
);
174 iounmap(host
->data_va
);
176 iounmap(host
->addr_va
);
185 static int nomadik_nand_remove(struct platform_device
*pdev
)
187 struct nomadik_nand_host
*host
= platform_get_drvdata(pdev
);
188 struct nomadik_nand_platform_data
*pdata
= pdev
->dev
.platform_data
;
194 iounmap(host
->cmd_va
);
195 iounmap(host
->data_va
);
196 iounmap(host
->addr_va
);
202 static int nomadik_nand_suspend(struct device
*dev
)
204 struct nomadik_nand_host
*host
= dev_get_drvdata(dev
);
207 ret
= host
->mtd
.suspend(&host
->mtd
);
211 static int nomadik_nand_resume(struct device
*dev
)
213 struct nomadik_nand_host
*host
= dev_get_drvdata(dev
);
215 host
->mtd
.resume(&host
->mtd
);
219 static struct dev_pm_ops nomadik_nand_pm_ops
= {
220 .suspend
= nomadik_nand_suspend
,
221 .resume
= nomadik_nand_resume
,
224 static struct platform_driver nomadik_nand_driver
= {
225 .probe
= nomadik_nand_probe
,
226 .remove
= nomadik_nand_remove
,
228 .owner
= THIS_MODULE
,
229 .name
= "nomadik_nand",
230 .pm
= &nomadik_nand_pm_ops
,
234 static int __init
nand_nomadik_init(void)
236 pr_info("Nomadik NAND driver\n");
237 return platform_driver_register(&nomadik_nand_driver
);
240 static void __exit
nand_nomadik_exit(void)
242 platform_driver_unregister(&nomadik_nand_driver
);
245 module_init(nand_nomadik_init
);
246 module_exit(nand_nomadik_exit
);
248 MODULE_LICENSE("GPL");
249 MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)");
250 MODULE_DESCRIPTION("NAND driver for Nomadik Platform");