• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* drivers/mtd/devices/goldfish_nand.c
2 **
3 ** Copyright (C) 2007 Google, Inc.
4 **
5 ** This software is licensed under the terms of the GNU General Public
6 ** License version 2, as published by the Free Software Foundation, and
7 ** may be copied, distributed, and modified under those terms.
8 **
9 ** This program is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 ** GNU General Public License for more details.
13 **
14 */
15 
16 #include <asm/div64.h>
17 #include <asm/io.h>
18 #include <linux/module.h>
19 #include <linux/slab.h>
20 #include <linux/ioport.h>
21 #include <linux/vmalloc.h>
22 #include <linux/init.h>
23 #include <linux/mtd/compatmac.h>
24 #include <linux/mtd/mtd.h>
25 #include <linux/platform_device.h>
26 
27 #include "goldfish_nand_reg.h"
28 
29 struct goldfish_nand {
30 	spinlock_t              lock;
31 	unsigned char __iomem  *base;
32 	struct cmd_params       *cmd_params;
33 	size_t                  mtd_count;
34 	struct mtd_info         mtd[0];
35 };
36 
goldfish_nand_cmd_with_params(struct mtd_info * mtd,enum nand_cmd cmd,uint64_t addr,uint32_t len,void * ptr,uint32_t * rv)37 static uint32_t goldfish_nand_cmd_with_params(struct mtd_info *mtd,
38 			enum nand_cmd cmd, uint64_t addr, uint32_t len,
39 			void *ptr, uint32_t *rv)
40 {
41 	uint32_t cmdp;
42 	struct goldfish_nand *nand = mtd->priv;
43 	struct cmd_params *cps = nand->cmd_params;
44 	unsigned char __iomem  *base = nand->base;
45 
46 	if (cps == NULL)
47 	    return -1;
48 
49 	switch(cmd) {
50 	    case NAND_CMD_ERASE:
51 		cmdp = NAND_CMD_ERASE_WITH_PARAMS;
52 		break;
53 	    case NAND_CMD_READ:
54 		cmdp = NAND_CMD_READ_WITH_PARAMS;
55 		break;
56 	    case NAND_CMD_WRITE:
57 		cmdp = NAND_CMD_WRITE_WITH_PARAMS;
58 		break;
59 	    default:
60 		return -1;
61 	}
62 	cps->dev = mtd - nand->mtd;
63 	cps->addr_high = (uint32_t)(addr >> 32);
64 	cps->addr_low = (uint32_t)addr;
65 	cps->transfer_size = len;
66 	cps->data = (uint32_t)ptr;
67 	writel(cmdp, base + NAND_COMMAND);
68 	*rv = cps->result;
69 	return 0;
70 }
71 
goldfish_nand_cmd(struct mtd_info * mtd,enum nand_cmd cmd,uint64_t addr,uint32_t len,void * ptr)72 static uint32_t goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
73                               uint64_t addr, uint32_t len, void *ptr)
74 {
75 	struct goldfish_nand *nand = mtd->priv;
76 	uint32_t rv;
77 	unsigned long irq_flags;
78 	unsigned char __iomem  *base = nand->base;
79 
80 	spin_lock_irqsave(&nand->lock, irq_flags);
81 	if (goldfish_nand_cmd_with_params(mtd, cmd, addr, len, ptr, &rv))
82 	{
83 	    writel(mtd - nand->mtd, base + NAND_DEV);
84 	    writel((uint32_t)(addr >> 32), base + NAND_ADDR_HIGH);
85 	    writel((uint32_t)addr, base + NAND_ADDR_LOW);
86 	    writel(len, base + NAND_TRANSFER_SIZE);
87 	    writel((unsigned long)ptr, base + NAND_DATA);
88 	    writel(cmd, base + NAND_COMMAND);
89 	    rv = readl(base + NAND_RESULT);
90 	}
91 	spin_unlock_irqrestore(&nand->lock, irq_flags);
92 	return rv;
93 }
94 
goldfish_nand_erase(struct mtd_info * mtd,struct erase_info * instr)95 static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
96 {
97 	loff_t ofs = instr->addr;
98 	uint32_t len = instr->len;
99 	uint32_t rem;
100 
101 	if (ofs + len > mtd->size)
102 		goto invalid_arg;
103 	rem = do_div(ofs, mtd->writesize);
104 	if(rem)
105 		goto invalid_arg;
106 	ofs *= (mtd->writesize + mtd->oobsize);
107 
108 	if(len % mtd->writesize)
109 		goto invalid_arg;
110 	len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
111 
112 	if(goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) {
113 		printk("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size "
114 		       "%llx, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize);
115 		return -EIO;
116 	}
117 
118 	instr->state = MTD_ERASE_DONE;
119 	mtd_erase_callback(instr);
120 
121 	return 0;
122 
123 invalid_arg:
124 	printk("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size "
125 	       "%llx, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize);
126 	return -EINVAL;
127 }
128 
goldfish_nand_read_oob(struct mtd_info * mtd,loff_t ofs,struct mtd_oob_ops * ops)129 static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
130                               struct mtd_oob_ops *ops)
131 {
132 	uint32_t rem;
133 
134 	if(ofs + ops->len > mtd->size)
135 		goto invalid_arg;
136 	if(ops->datbuf && ops->len && ops->len != mtd->writesize)
137 		goto invalid_arg;
138 	if(ops->ooblen + ops->ooboffs > mtd->oobsize)
139 		goto invalid_arg;
140 
141 	rem = do_div(ofs, mtd->writesize);
142 	if(rem)
143 		goto invalid_arg;
144 	ofs *= (mtd->writesize + mtd->oobsize);
145 
146 	if(ops->datbuf)
147 		ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
148 		                            ops->len, ops->datbuf);
149 	ofs += mtd->writesize + ops->ooboffs;
150 	if(ops->oobbuf)
151 		ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
152 		                               ops->ooblen, ops->oobbuf);
153 	return 0;
154 
155 invalid_arg:
156 	printk("goldfish_nand_read_oob: invalid read, start %llx, len %x, "
157 	       "ooblen %x, dev_size %llx, write_size %x\n",
158 	       ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
159 	return -EINVAL;
160 }
161 
goldfish_nand_write_oob(struct mtd_info * mtd,loff_t ofs,struct mtd_oob_ops * ops)162 static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
163                                struct mtd_oob_ops *ops)
164 {
165 	uint32_t rem;
166 
167 	if(ofs + ops->len > mtd->size)
168 		goto invalid_arg;
169 	if(ops->len && ops->len != mtd->writesize)
170 		goto invalid_arg;
171 	if(ops->ooblen + ops->ooboffs > mtd->oobsize)
172 		goto invalid_arg;
173 
174 	rem = do_div(ofs, mtd->writesize);
175 	if(rem)
176 		goto invalid_arg;
177 	ofs *= (mtd->writesize + mtd->oobsize);
178 
179 	if(ops->datbuf)
180 		ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
181 		                            ops->len, ops->datbuf);
182 	ofs += mtd->writesize + ops->ooboffs;
183 	if(ops->oobbuf)
184 		ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
185 		                               ops->ooblen, ops->oobbuf);
186 	return 0;
187 
188 invalid_arg:
189 	printk("goldfish_nand_write_oob: invalid write, start %llx, len %x, "
190 	       "ooblen %x, dev_size %llx, write_size %x\n",
191 	       ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
192 	return -EINVAL;
193 }
194 
goldfish_nand_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)195 static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
196                           size_t *retlen, u_char *buf)
197 {
198 	uint32_t rem;
199 
200 	if(from + len > mtd->size)
201 		goto invalid_arg;
202 	if(len != mtd->writesize)
203 		goto invalid_arg;
204 
205 	rem = do_div(from, mtd->writesize);
206 	if(rem)
207 		goto invalid_arg;
208 	from *= (mtd->writesize + mtd->oobsize);
209 
210 	*retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
211 	return 0;
212 
213 invalid_arg:
214 	printk("goldfish_nand_read: invalid read, start %llx, len %x, dev_size %llx"
215 	       ", write_size %x\n", from, len, mtd->size, mtd->writesize);
216 	return -EINVAL;
217 }
218 
goldfish_nand_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)219 static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
220                            size_t *retlen, const u_char *buf)
221 {
222 	uint32_t rem;
223 
224 	if(to + len > mtd->size)
225 		goto invalid_arg;
226 	if(len != mtd->writesize)
227 		goto invalid_arg;
228 
229 	rem = do_div(to, mtd->writesize);
230 	if(rem)
231 		goto invalid_arg;
232 	to *= (mtd->writesize + mtd->oobsize);
233 
234 	*retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
235 	return 0;
236 
237 invalid_arg:
238 	printk("goldfish_nand_write: invalid write, start %llx, len %x, dev_size %llx"
239 	       ", write_size %x\n", to, len, mtd->size, mtd->writesize);
240 	return -EINVAL;
241 }
242 
goldfish_nand_block_isbad(struct mtd_info * mtd,loff_t ofs)243 static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
244 {
245 	uint32_t rem;
246 
247 	if(ofs >= mtd->size)
248 		goto invalid_arg;
249 
250 	rem = do_div(ofs, mtd->erasesize);
251 	if(rem)
252 		goto invalid_arg;
253 	ofs *= mtd->erasesize / mtd->writesize;
254 	ofs *= (mtd->writesize + mtd->oobsize);
255 
256 	return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
257 
258 invalid_arg:
259 	printk("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, "
260 	       "write_size %x\n", ofs, mtd->size, mtd->writesize);
261 	return -EINVAL;
262 }
263 
goldfish_nand_block_markbad(struct mtd_info * mtd,loff_t ofs)264 static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
265 {
266 	uint32_t rem;
267 
268 	if(ofs >= mtd->size)
269 		goto invalid_arg;
270 
271 	rem = do_div(ofs, mtd->erasesize);
272 	if(rem)
273 		goto invalid_arg;
274 	ofs *= mtd->erasesize / mtd->writesize;
275 	ofs *= (mtd->writesize + mtd->oobsize);
276 
277 	if(goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
278 		return -EIO;
279 	return 0;
280 
281 invalid_arg:
282 	printk("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, "
283 	       "write_size %x\n", ofs, mtd->size, mtd->writesize);
284 	return -EINVAL;
285 }
286 
nand_setup_cmd_params(struct goldfish_nand * nand)287 static int nand_setup_cmd_params(struct goldfish_nand *nand)
288 {
289 	uint64_t paddr;
290 	unsigned char __iomem  *base = nand->base;
291 
292 	nand->cmd_params = kmalloc(sizeof(struct cmd_params), GFP_KERNEL);
293 	if (!nand->cmd_params)
294 	    return -1;
295 
296 	paddr = __pa(nand->cmd_params);
297 	writel((uint32_t)(paddr >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH);
298 	writel((uint32_t)paddr, base + NAND_CMD_PARAMS_ADDR_LOW);
299 	return 0;
300 }
301 
goldfish_nand_init_device(struct goldfish_nand * nand,int id)302 static int goldfish_nand_init_device(struct goldfish_nand *nand, int id)
303 {
304 	uint32_t name_len;
305 	uint32_t result;
306 	uint32_t flags;
307 	unsigned long irq_flags;
308 	unsigned char __iomem  *base = nand->base;
309 	struct mtd_info *mtd = &nand->mtd[id];
310 	char *name;
311 
312 	spin_lock_irqsave(&nand->lock, irq_flags);
313 	writel(id, base + NAND_DEV);
314 	flags = readl(base + NAND_DEV_FLAGS);
315 	name_len = readl(base + NAND_DEV_NAME_LEN);
316 	mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE);
317 	mtd->size = readl(base + NAND_DEV_SIZE_LOW);
318 	mtd->size |= (uint64_t)readl(base + NAND_DEV_SIZE_HIGH) << 32;
319 	mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE);
320 	mtd->oobavail = mtd->oobsize;
321 	mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) /
322 	                 (mtd->writesize + mtd->oobsize) * mtd->writesize;
323 	do_div(mtd->size, mtd->writesize + mtd->oobsize);
324 	mtd->size *= mtd->writesize;
325 	printk("goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
326 	       id, mtd->size, mtd->writesize, mtd->oobsize, mtd->erasesize);
327 	spin_unlock_irqrestore(&nand->lock, irq_flags);
328 
329 	mtd->priv = nand;
330 
331 	mtd->name = name = kmalloc(name_len + 1, GFP_KERNEL);
332 	if(name == NULL)
333 		return -ENOMEM;
334 
335 	result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, name);
336 	if(result != name_len) {
337 		kfree(mtd->name);
338 		mtd->name = NULL;
339 		printk("goldfish_nand_init_device failed to get dev name %d != %d\n",
340 		       result, name_len);
341 		return -ENODEV;
342 	}
343 	((char *) mtd->name)[name_len] = '\0';
344 
345 	/* Setup the MTD structure */
346 	mtd->type = MTD_NANDFLASH;
347 	mtd->flags = MTD_CAP_NANDFLASH;
348 	if(flags & NAND_DEV_FLAG_READ_ONLY)
349 		mtd->flags &= ~MTD_WRITEABLE;
350 	if(flags & NAND_DEV_FLAG_CMD_PARAMS_CAP)
351 	    nand_setup_cmd_params(nand);
352 
353 	mtd->owner = THIS_MODULE;
354 	mtd->erase = goldfish_nand_erase;
355 	mtd->read = goldfish_nand_read;
356 	mtd->write = goldfish_nand_write;
357 	mtd->read_oob = goldfish_nand_read_oob;
358 	mtd->write_oob = goldfish_nand_write_oob;
359 	mtd->block_isbad = goldfish_nand_block_isbad;
360 	mtd->block_markbad = goldfish_nand_block_markbad;
361 
362 	if (add_mtd_device(mtd)) {
363 		kfree(mtd->name);
364 		mtd->name = NULL;
365 		return -EIO;
366 	}
367 
368 	return 0;
369 }
370 
goldfish_nand_probe(struct platform_device * pdev)371 static int goldfish_nand_probe(struct platform_device *pdev)
372 {
373 	uint32_t num_dev;
374 	int i;
375 	int err;
376 	uint32_t num_dev_working;
377 	uint32_t version;
378 	struct resource *r;
379 	struct goldfish_nand *nand;
380 	unsigned char __iomem  *base;
381 
382 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
383 	if(r == NULL) {
384 		err = -ENODEV;
385 		goto err_no_io_base;
386 	}
387 
388 	base = ioremap(r->start, PAGE_SIZE);
389 	if(base == NULL) {
390 		err = -ENOMEM;
391 		goto err_ioremap;
392 	}
393 	version = readl(base + NAND_VERSION);
394 	if(version != NAND_VERSION_CURRENT) {
395 		printk("goldfish_nand_init: version mismatch, got %d, expected %d\n",
396 		       version, NAND_VERSION_CURRENT);
397 		err = -ENODEV;
398 		goto err_no_dev;
399 	}
400 	num_dev = readl(base + NAND_NUM_DEV);
401 	if(num_dev == 0) {
402 		err = -ENODEV;
403 		goto err_no_dev;
404 	}
405 
406 	nand = kzalloc(sizeof(*nand) + sizeof(struct mtd_info) * num_dev, GFP_KERNEL);
407 	if(nand == NULL) {
408 		err = -ENOMEM;
409 		goto err_nand_alloc_failed;
410 	}
411 	spin_lock_init(&nand->lock);
412 	nand->base = base;
413 	nand->mtd_count = num_dev;
414 	platform_set_drvdata(pdev, nand);
415 
416 	num_dev_working = 0;
417 	for(i = 0; i < num_dev; i++) {
418 		err = goldfish_nand_init_device(nand, i);
419 		if(err == 0)
420 			num_dev_working++;
421 	}
422 	if(num_dev_working == 0) {
423 		err = -ENODEV;
424 		goto err_no_working_dev;
425 	}
426 	return 0;
427 
428 err_no_working_dev:
429 	kfree(nand);
430 err_nand_alloc_failed:
431 err_no_dev:
432 	iounmap(base);
433 err_ioremap:
434 err_no_io_base:
435 	return err;
436 }
437 
goldfish_nand_remove(struct platform_device * pdev)438 static int goldfish_nand_remove(struct platform_device *pdev)
439 {
440 	struct goldfish_nand *nand = platform_get_drvdata(pdev);
441 	int i;
442 	for(i = 0; i < nand->mtd_count; i++) {
443 		if(nand->mtd[i].name) {
444 			del_mtd_device(&nand->mtd[i]);
445 			kfree(nand->mtd[i].name);
446 		}
447 	}
448 	if (nand->cmd_params)
449 	    kfree(nand->cmd_params);
450 	iounmap(nand->base);
451 	kfree(nand);
452 	return 0;
453 }
454 
455 static struct platform_driver goldfish_nand_driver = {
456 	.probe		= goldfish_nand_probe,
457 	.remove		= goldfish_nand_remove,
458 	.driver = {
459 		.name = "goldfish_nand"
460 	}
461 };
462 
goldfish_nand_init(void)463 static int __init goldfish_nand_init(void)
464 {
465 	return platform_driver_register(&goldfish_nand_driver);
466 }
467 
goldfish_nand_exit(void)468 static void __exit goldfish_nand_exit(void)
469 {
470 	platform_driver_unregister(&goldfish_nand_driver);
471 }
472 
473 
474 module_init(goldfish_nand_init);
475 module_exit(goldfish_nand_exit);
476 
477