ndfc.c revision 1ff184225b15930ea118ac2130f074c741d34f08
1/*
2 *  drivers/mtd/ndfc.c
3 *
4 *  Overview:
5 *   Platform independend driver for NDFC (NanD Flash Controller)
6 *   integrated into EP440 cores
7 *
8 *  Author: Thomas Gleixner
9 *
10 *  Copyright 2006 IBM
11 *
12 *  This program is free software; you can redistribute	 it and/or modify it
13 *  under  the terms of	 the GNU General  Public License as published by the
14 *  Free Software Foundation;  either version 2 of the	License, or (at your
15 *  option) any later version.
16 *
17 */
18#include <linux/module.h>
19#include <linux/mtd/nand.h>
20#include <linux/mtd/nand_ecc.h>
21#include <linux/mtd/partitions.h>
22#include <linux/mtd/ndfc.h>
23#include <linux/mtd/mtd.h>
24#include <linux/platform_device.h>
25
26#include <asm/io.h>
27#ifdef CONFIG_40x
28#include <asm/ibm405.h>
29#else
30#include <asm/ibm44x.h>
31#endif
32
33struct ndfc_nand_mtd {
34	struct mtd_info			mtd;
35	struct nand_chip		chip;
36	struct platform_nand_chip	*pl_chip;
37};
38
39static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS];
40
41struct ndfc_controller {
42	void __iomem		*ndfcbase;
43	struct nand_hw_control	ndfc_control;
44	atomic_t		childs_active;
45};
46
47static struct ndfc_controller ndfc_ctrl;
48
49static void ndfc_select_chip(struct mtd_info *mtd, int chip)
50{
51	uint32_t ccr;
52	struct ndfc_controller *ndfc = &ndfc_ctrl;
53	struct nand_chip *nandchip = mtd->priv;
54	struct ndfc_nand_mtd *nandmtd = nandchip->priv;
55	struct platform_nand_chip *pchip = nandmtd->pl_chip;
56
57	ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
58	if (chip >= 0) {
59		ccr &= ~NDFC_CCR_BS_MASK;
60		ccr |= NDFC_CCR_BS(chip + pchip->chip_offset);
61	} else
62		ccr |= NDFC_CCR_RESET_CE;
63	__raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
64}
65
66static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
67{
68	struct ndfc_controller *ndfc = &ndfc_ctrl;
69
70	if (cmd == NAND_CMD_NONE)
71		return;
72
73	if (ctrl & NAND_CLE)
74		writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_CMD);
75	else
76		writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_ALE);
77}
78
79static int ndfc_ready(struct mtd_info *mtd)
80{
81	struct ndfc_controller *ndfc = &ndfc_ctrl;
82
83	return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
84}
85
86static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
87{
88	uint32_t ccr;
89	struct ndfc_controller *ndfc = &ndfc_ctrl;
90
91	ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
92	ccr |= NDFC_CCR_RESET_ECC;
93	__raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
94	wmb();
95}
96
97static int ndfc_calculate_ecc(struct mtd_info *mtd,
98			      const u_char *dat, u_char *ecc_code)
99{
100	struct ndfc_controller *ndfc = &ndfc_ctrl;
101	uint32_t ecc;
102	uint8_t *p = (uint8_t *)&ecc;
103
104	wmb();
105	ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC);
106	ecc_code[0] = p[1];
107	ecc_code[1] = p[2];
108	ecc_code[2] = p[3];
109
110	return 0;
111}
112
113/*
114 * Speedups for buffer read/write/verify
115 *
116 * NDFC allows 32bit read/write of data. So we can speed up the buffer
117 * functions. No further checking, as nand_base will always read/write
118 * page aligned.
119 */
120static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
121{
122	struct ndfc_controller *ndfc = &ndfc_ctrl;
123	uint32_t *p = (uint32_t *) buf;
124
125	for(;len > 0; len -= 4)
126		*p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA);
127}
128
129static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
130{
131	struct ndfc_controller *ndfc = &ndfc_ctrl;
132	uint32_t *p = (uint32_t *) buf;
133
134	for(;len > 0; len -= 4)
135		__raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA);
136}
137
138static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
139{
140	struct ndfc_controller *ndfc = &ndfc_ctrl;
141	uint32_t *p = (uint32_t *) buf;
142
143	for(;len > 0; len -= 4)
144		if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA))
145			return -EFAULT;
146	return 0;
147}
148
149/*
150 * Initialize chip structure
151 */
152static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
153{
154	struct ndfc_controller *ndfc = &ndfc_ctrl;
155	struct nand_chip *chip = &mtd->chip;
156
157	chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
158	chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
159	chip->cmd_ctrl = ndfc_hwcontrol;
160	chip->dev_ready = ndfc_ready;
161	chip->select_chip = ndfc_select_chip;
162	chip->chip_delay = 50;
163	chip->priv = mtd;
164	chip->options = mtd->pl_chip->options;
165	chip->controller = &ndfc->ndfc_control;
166	chip->read_buf = ndfc_read_buf;
167	chip->write_buf = ndfc_write_buf;
168	chip->verify_buf = ndfc_verify_buf;
169	chip->ecc.correct = nand_correct_data;
170	chip->ecc.hwctl = ndfc_enable_hwecc;
171	chip->ecc.calculate = ndfc_calculate_ecc;
172	chip->ecc.mode = NAND_ECC_HW;
173	chip->ecc.size = 256;
174	chip->ecc.bytes = 3;
175	chip->ecclayout = chip->ecc.layout = mtd->pl_chip->ecclayout;
176	mtd->mtd.priv = chip;
177	mtd->mtd.owner = THIS_MODULE;
178}
179
180static int ndfc_chip_probe(struct platform_device *pdev)
181{
182	struct platform_nand_chip *nc = pdev->dev.platform_data;
183	struct ndfc_chip_settings *settings = nc->priv;
184	struct ndfc_controller *ndfc = &ndfc_ctrl;
185	struct ndfc_nand_mtd *nandmtd;
186
187	if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS)
188		return -EINVAL;
189
190	/* Set the bank settings */
191	__raw_writel(settings->bank_settings,
192		     ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2));
193
194	nandmtd = &ndfc_mtd[pdev->id];
195	if (nandmtd->pl_chip)
196		return -EBUSY;
197
198	nandmtd->pl_chip = nc;
199	ndfc_chip_init(nandmtd);
200
201	/* Scan for chips */
202	if (nand_scan(&nandmtd->mtd, nc->nr_chips)) {
203		nandmtd->pl_chip = NULL;
204		return -ENODEV;
205	}
206
207#ifdef CONFIG_MTD_PARTITIONS
208	printk("Number of partitions %d\n", nc->nr_partitions);
209	if (nc->nr_partitions) {
210		/* Add the full device, so complete dumps can be made */
211		add_mtd_device(&nandmtd->mtd);
212		add_mtd_partitions(&nandmtd->mtd, nc->partitions,
213				   nc->nr_partitions);
214
215	} else
216#else
217		add_mtd_device(&nandmtd->mtd);
218#endif
219
220	atomic_inc(&ndfc->childs_active);
221	return 0;
222}
223
224static int ndfc_chip_remove(struct platform_device *pdev)
225{
226	return 0;
227}
228
229static int ndfc_nand_probe(struct platform_device *pdev)
230{
231	struct platform_nand_ctrl *nc = pdev->dev.platform_data;
232	struct ndfc_controller_settings *settings = nc->priv;
233	struct resource *res = pdev->resource;
234	struct ndfc_controller *ndfc = &ndfc_ctrl;
235	unsigned long long phys = settings->ndfc_erpn | res->start;
236
237#ifndef CONFIG_PHYS_64BIT
238	ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1);
239#else
240	ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
241#endif
242	if (!ndfc->ndfcbase) {
243		printk(KERN_ERR "NDFC: ioremap failed\n");
244		return -EIO;
245	}
246
247	__raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR);
248
249	spin_lock_init(&ndfc->ndfc_control.lock);
250	init_waitqueue_head(&ndfc->ndfc_control.wq);
251
252	platform_set_drvdata(pdev, ndfc);
253
254	printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n",
255	       __raw_readl(ndfc->ndfcbase + NDFC_REVID));
256
257	return 0;
258}
259
260static int ndfc_nand_remove(struct platform_device *pdev)
261{
262	struct ndfc_controller *ndfc = platform_get_drvdata(pdev);
263
264	if (atomic_read(&ndfc->childs_active))
265		return -EBUSY;
266
267	if (ndfc) {
268		platform_set_drvdata(pdev, NULL);
269		iounmap(ndfc_ctrl.ndfcbase);
270		ndfc_ctrl.ndfcbase = NULL;
271	}
272	return 0;
273}
274
275/* driver device registration */
276
277static struct platform_driver ndfc_chip_driver = {
278	.probe		= ndfc_chip_probe,
279	.remove		= ndfc_chip_remove,
280	.driver		= {
281		.name	= "ndfc-chip",
282		.owner	= THIS_MODULE,
283	},
284};
285
286static struct platform_driver ndfc_nand_driver = {
287	.probe		= ndfc_nand_probe,
288	.remove		= ndfc_nand_remove,
289	.driver		= {
290		.name	= "ndfc-nand",
291		.owner	= THIS_MODULE,
292	},
293};
294
295static int __init ndfc_nand_init(void)
296{
297	int ret;
298
299	spin_lock_init(&ndfc_ctrl.ndfc_control.lock);
300	init_waitqueue_head(&ndfc_ctrl.ndfc_control.wq);
301
302	ret = platform_driver_register(&ndfc_nand_driver);
303	if (!ret)
304		ret = platform_driver_register(&ndfc_chip_driver);
305	return ret;
306}
307
308static void __exit ndfc_nand_exit(void)
309{
310	platform_driver_unregister(&ndfc_chip_driver);
311	platform_driver_unregister(&ndfc_nand_driver);
312}
313
314module_init(ndfc_nand_init);
315module_exit(ndfc_nand_exit);
316
317MODULE_LICENSE("GPL");
318MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
319MODULE_DESCRIPTION("Platform driver for NDFC");
320MODULE_ALIAS("platform:ndfc-chip");
321MODULE_ALIAS("platform:ndfc-nand");
322