1 From bed297dad6283a5926962c1c59f95ad641824630 Mon Sep 17 00:00:00 2001
2 From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
3 Date: Fri, 29 Jun 2007 22:20:07 -0700
4 Subject: [PATCH 125/134] [ARM] goldfish: NAND: Add nand driver for goldfish.
6 Content-Type: text/plain; charset=utf-8
7 Content-Transfer-Encoding: 8bit
9 Signed-off-by: Mike A. Chan <mikechan@google.com>
10 Signed-off-by: Arve Hjønnevåg <arve@android.com>
12 drivers/mtd/devices/Kconfig | 5 +
13 drivers/mtd/devices/Makefile | 1 +
14 drivers/mtd/devices/goldfish_nand.c | 418 +++++++++++++++++++++++++++++++
15 drivers/mtd/devices/goldfish_nand_reg.h | 58 +++++
16 4 files changed, 482 insertions(+), 0 deletions(-)
17 create mode 100644 drivers/mtd/devices/goldfish_nand.c
18 create mode 100644 drivers/mtd/devices/goldfish_nand_reg.h
20 --- a/drivers/mtd/devices/Kconfig
21 +++ b/drivers/mtd/devices/Kconfig
22 @@ -297,5 +297,10 @@ config MTD_DOCPROBE_55AA
23 LinuxBIOS or if you need to recover a DiskOnChip Millennium on which
24 you have managed to wipe the first block.
26 +config MTD_GOLDFISH_NAND
27 + tristate "Goldfish NAND device"
33 --- a/drivers/mtd/devices/Makefile
34 +++ b/drivers/mtd/devices/Makefile
35 @@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
36 obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
37 obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
38 obj-$(CONFIG_MTD_M25P80) += m25p80.o
39 +obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
41 +++ b/drivers/mtd/devices/goldfish_nand.c
43 +/* drivers/mtd/devices/goldfish_nand.c
45 +** Copyright (C) 2007 Google, Inc.
47 +** This software is licensed under the terms of the GNU General Public
48 +** License version 2, as published by the Free Software Foundation, and
49 +** may be copied, distributed, and modified under those terms.
51 +** This program is distributed in the hope that it will be useful,
52 +** but WITHOUT ANY WARRANTY; without even the implied warranty of
53 +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54 +** GNU General Public License for more details.
58 +#include <asm/div64.h>
60 +#include <linux/module.h>
61 +#include <linux/slab.h>
62 +#include <linux/ioport.h>
63 +#include <linux/vmalloc.h>
64 +#include <linux/init.h>
65 +#include <linux/mtd/compatmac.h>
66 +#include <linux/mtd/mtd.h>
67 +#include <linux/platform_device.h>
69 +#include "goldfish_nand_reg.h"
71 +struct goldfish_nand {
73 + unsigned char __iomem *base;
75 + struct mtd_info mtd[0];
78 +static uint32_t goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
79 + uint64_t addr, uint32_t len, void *ptr)
81 + struct goldfish_nand *nand = mtd->priv;
83 + unsigned long irq_flags;
84 + unsigned char __iomem *base = nand->base;
86 + spin_lock_irqsave(&nand->lock, irq_flags);
87 + writel(mtd - nand->mtd, base + NAND_DEV);
88 + writel((uint32_t)(addr >> 32), base + NAND_ADDR_HIGH);
89 + writel((uint32_t)addr, base + NAND_ADDR_LOW);
90 + writel(len, base + NAND_TRANSFER_SIZE);
91 + writel(ptr, base + NAND_DATA);
92 + writel(cmd, base + NAND_COMMAND);
93 + rv = readl(base + NAND_RESULT);
94 + spin_unlock_irqrestore(&nand->lock, irq_flags);
98 +static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
100 + loff_t ofs = instr->addr;
101 + uint32_t len = instr->len;
104 + if (ofs + len > mtd->size)
106 + rem = do_div(ofs, mtd->writesize);
109 + ofs *= (mtd->writesize + mtd->oobsize);
111 + if(len % mtd->writesize)
113 + len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
115 + if(goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) {
116 + printk("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size "
117 + "%llx, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize);
121 + instr->state = MTD_ERASE_DONE;
122 + mtd_erase_callback(instr);
127 + printk("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size "
128 + "%llx, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize);
132 +static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
133 + struct mtd_oob_ops *ops)
137 + if(ofs + ops->len > mtd->size)
139 + if(ops->datbuf && ops->len && ops->len != mtd->writesize)
141 + if(ops->ooblen + ops->ooboffs > mtd->oobsize)
144 + rem = do_div(ofs, mtd->writesize);
147 + ofs *= (mtd->writesize + mtd->oobsize);
150 + ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
151 + ops->len, ops->datbuf);
152 + ofs += mtd->writesize + ops->ooboffs;
154 + ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
155 + ops->ooblen, ops->oobbuf);
159 + printk("goldfish_nand_read_oob: invalid read, start %llx, len %x, "
160 + "ooblen %x, dev_size %llx, write_size %x\n",
161 + ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
165 +static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
166 + struct mtd_oob_ops *ops)
170 + if(ofs + ops->len > mtd->size)
172 + if(ops->len && ops->len != mtd->writesize)
174 + if(ops->ooblen + ops->ooboffs > mtd->oobsize)
177 + rem = do_div(ofs, mtd->writesize);
180 + ofs *= (mtd->writesize + mtd->oobsize);
183 + ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
184 + ops->len, ops->datbuf);
185 + ofs += mtd->writesize + ops->ooboffs;
187 + ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
188 + ops->ooblen, ops->oobbuf);
192 + printk("goldfish_nand_write_oob: invalid write, start %llx, len %x, "
193 + "ooblen %x, dev_size %llx, write_size %x\n",
194 + ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
198 +static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
199 + size_t *retlen, u_char *buf)
203 + if(from + len > mtd->size)
205 + if(len != mtd->writesize)
208 + rem = do_div(from, mtd->writesize);
211 + from *= (mtd->writesize + mtd->oobsize);
213 + *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
217 + printk("goldfish_nand_read: invalid read, start %llx, len %x, dev_size %llx"
218 + ", write_size %x\n", from, len, mtd->size, mtd->writesize);
222 +static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
223 + size_t *retlen, const u_char *buf)
227 + if(to + len > mtd->size)
229 + if(len != mtd->writesize)
232 + rem = do_div(to, mtd->writesize);
235 + to *= (mtd->writesize + mtd->oobsize);
237 + *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
241 + printk("goldfish_nand_write: invalid write, start %llx, len %x, dev_size %llx"
242 + ", write_size %x\n", to, len, mtd->size, mtd->writesize);
246 +static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
250 + if(ofs >= mtd->size)
253 + rem = do_div(ofs, mtd->erasesize);
256 + ofs *= mtd->erasesize / mtd->writesize;
257 + ofs *= (mtd->writesize + mtd->oobsize);
259 + return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
262 + printk("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, "
263 + "write_size %x\n", ofs, mtd->size, mtd->writesize);
267 +static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
271 + if(ofs >= mtd->size)
274 + rem = do_div(ofs, mtd->erasesize);
277 + ofs *= mtd->erasesize / mtd->writesize;
278 + ofs *= (mtd->writesize + mtd->oobsize);
280 + if(goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
285 + printk("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, "
286 + "write_size %x\n", ofs, mtd->size, mtd->writesize);
290 +static int goldfish_nand_init_device(struct goldfish_nand *nand, int id)
295 + unsigned long irq_flags;
296 + unsigned char __iomem *base = nand->base;
297 + struct mtd_info *mtd = &nand->mtd[id];
300 + spin_lock_irqsave(&nand->lock, irq_flags);
301 + writel(id, base + NAND_DEV);
302 + flags = readl(base + NAND_DEV_FLAGS);
303 + name_len = readl(base + NAND_DEV_NAME_LEN);
304 + mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE);
305 + mtd->size = readl(base + NAND_DEV_SIZE_LOW);
306 + mtd->size |= (uint64_t)readl(base + NAND_DEV_SIZE_HIGH) << 32;
307 + mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE);
308 + mtd->oobavail = mtd->oobsize;
309 + mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) /
310 + (mtd->writesize + mtd->oobsize) * mtd->writesize;
311 + do_div(mtd->size, mtd->writesize + mtd->oobsize);
312 + mtd->size *= mtd->writesize;
313 + printk("goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
314 + id, mtd->size, mtd->writesize, mtd->oobsize, mtd->erasesize);
315 + spin_unlock_irqrestore(&nand->lock, irq_flags);
319 + mtd->name = name = kmalloc(name_len + 1, GFP_KERNEL);
323 + result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, name);
324 + if(result != name_len) {
327 + printk("goldfish_nand_init_device failed to get dev name %d != %d\n",
331 + ((char *) mtd->name)[name_len] = '\0';
333 + /* Setup the MTD structure */
334 + mtd->type = MTD_NANDFLASH;
335 + mtd->flags = MTD_CAP_NANDFLASH;
336 + if(flags & NAND_DEV_FLAG_READ_ONLY)
337 + mtd->flags &= ~MTD_WRITEABLE;
339 + mtd->owner = THIS_MODULE;
340 + mtd->erase = goldfish_nand_erase;
341 + mtd->read = goldfish_nand_read;
342 + mtd->write = goldfish_nand_write;
343 + mtd->read_oob = goldfish_nand_read_oob;
344 + mtd->write_oob = goldfish_nand_write_oob;
345 + mtd->block_isbad = goldfish_nand_block_isbad;
346 + mtd->block_markbad = goldfish_nand_block_markbad;
348 + if (add_mtd_device(mtd)) {
357 +static int goldfish_nand_probe(struct platform_device *pdev)
362 + uint32_t num_dev_working;
364 + struct resource *r;
365 + struct goldfish_nand *nand;
366 + unsigned char __iomem *base;
368 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
371 + goto err_no_io_base;
374 + base = ioremap(r->start, PAGE_SIZE);
379 + version = readl(base + NAND_VERSION);
380 + if(version != NAND_VERSION_CURRENT) {
381 + printk("goldfish_nand_init: version mismatch, got %d, expected %d\n",
382 + version, NAND_VERSION_CURRENT);
386 + num_dev = readl(base + NAND_NUM_DEV);
392 + nand = kzalloc(sizeof(*nand) + sizeof(struct mtd_info) * num_dev, GFP_KERNEL);
395 + goto err_nand_alloc_failed;
397 + spin_lock_init(&nand->lock);
399 + nand->mtd_count = num_dev;
400 + platform_set_drvdata(pdev, nand);
402 + num_dev_working = 0;
403 + for(i = 0; i < num_dev; i++) {
404 + err = goldfish_nand_init_device(nand, i);
408 + if(num_dev_working == 0) {
410 + goto err_no_working_dev;
416 +err_nand_alloc_failed:
424 +static int goldfish_nand_remove(struct platform_device *pdev)
426 + struct goldfish_nand *nand = platform_get_drvdata(pdev);
428 + for(i = 0; i < nand->mtd_count; i++) {
429 + if(nand->mtd[i].name) {
430 + del_mtd_device(&nand->mtd[i]);
431 + kfree(nand->mtd[i].name);
434 + iounmap(nand->base);
439 +static struct platform_driver goldfish_nand_driver = {
440 + .probe = goldfish_nand_probe,
441 + .remove = goldfish_nand_remove,
443 + .name = "goldfish_nand"
447 +static int __init goldfish_nand_init(void)
449 + return platform_driver_register(&goldfish_nand_driver);
452 +static void __exit goldfish_nand_exit(void)
454 + platform_driver_unregister(&goldfish_nand_driver);
458 +module_init(goldfish_nand_init);
459 +module_exit(goldfish_nand_exit);
462 +++ b/drivers/mtd/devices/goldfish_nand_reg.h
464 +/* drivers/mtd/devices/goldfish_nand_reg.h
466 +** Copyright (C) 2007 Google, Inc.
468 +** This software is licensed under the terms of the GNU General Public
469 +** License version 2, as published by the Free Software Foundation, and
470 +** may be copied, distributed, and modified under those terms.
472 +** This program is distributed in the hope that it will be useful,
473 +** but WITHOUT ANY WARRANTY; without even the implied warranty of
474 +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
475 +** GNU General Public License for more details.
479 +#ifndef GOLDFISH_NAND_REG_H
480 +#define GOLDFISH_NAND_REG_H
483 + NAND_CMD_GET_DEV_NAME, // Write device name for NAND_DEV to NAND_DATA (vaddr)
487 + NAND_CMD_BLOCK_BAD_GET, // NAND_RESULT is 1 if block is bad, 0 if it is not
488 + NAND_CMD_BLOCK_BAD_SET
491 +enum nand_dev_flags {
492 + NAND_DEV_FLAG_READ_ONLY = 0x00000001
495 +#define NAND_VERSION_CURRENT (1)
499 + NAND_VERSION = 0x000,
500 + NAND_NUM_DEV = 0x004,
504 + NAND_DEV_FLAGS = 0x010,
505 + NAND_DEV_NAME_LEN = 0x014,
506 + NAND_DEV_PAGE_SIZE = 0x018,
507 + NAND_DEV_EXTRA_SIZE = 0x01c,
508 + NAND_DEV_ERASE_SIZE = 0x020,
509 + NAND_DEV_SIZE_LOW = 0x028,
510 + NAND_DEV_SIZE_HIGH = 0x02c,
513 + NAND_RESULT = 0x040,
514 + NAND_COMMAND = 0x044,
516 + NAND_TRANSFER_SIZE = 0x04c,
517 + NAND_ADDR_LOW = 0x050,
518 + NAND_ADDR_HIGH = 0x054,