ramips: modify {read,write}_config helpers
[openwrt.git] / target / linux / goldfish / patches-2.6.30 / 0125--ARM-goldfish-NAND-Add-nand-driver-for-goldfish.patch
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.
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=utf-8
7 Content-Transfer-Encoding: 8bit
8
9 Signed-off-by: Mike A. Chan <mikechan@google.com>
10 Signed-off-by: Arve Hjønnevåg <arve@android.com>
11 ---
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
19
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.
25
26 +config MTD_GOLDFISH_NAND
27 + tristate "Goldfish NAND device"
28 + help
29 + none
30 +
31 endmenu
32
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
40 --- /dev/null
41 +++ b/drivers/mtd/devices/goldfish_nand.c
42 @@ -0,0 +1,418 @@
43 +/* drivers/mtd/devices/goldfish_nand.c
44 +**
45 +** Copyright (C) 2007 Google, Inc.
46 +**
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.
50 +**
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.
55 +**
56 +*/
57 +
58 +#include <asm/div64.h>
59 +#include <asm/io.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>
68 +
69 +#include "goldfish_nand_reg.h"
70 +
71 +struct goldfish_nand {
72 + spinlock_t lock;
73 + unsigned char __iomem *base;
74 + size_t mtd_count;
75 + struct mtd_info mtd[0];
76 +};
77 +
78 +static uint32_t goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
79 + uint64_t addr, uint32_t len, void *ptr)
80 +{
81 + struct goldfish_nand *nand = mtd->priv;
82 + uint32_t rv;
83 + unsigned long irq_flags;
84 + unsigned char __iomem *base = nand->base;
85 +
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);
95 + return rv;
96 +}
97 +
98 +static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
99 +{
100 + loff_t ofs = instr->addr;
101 + uint32_t len = instr->len;
102 + uint32_t rem;
103 +
104 + if (ofs + len > mtd->size)
105 + goto invalid_arg;
106 + rem = do_div(ofs, mtd->writesize);
107 + if(rem)
108 + goto invalid_arg;
109 + ofs *= (mtd->writesize + mtd->oobsize);
110 +
111 + if(len % mtd->writesize)
112 + goto invalid_arg;
113 + len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
114 +
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);
118 + return -EIO;
119 + }
120 +
121 + instr->state = MTD_ERASE_DONE;
122 + mtd_erase_callback(instr);
123 +
124 + return 0;
125 +
126 +invalid_arg:
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);
129 + return -EINVAL;
130 +}
131 +
132 +static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
133 + struct mtd_oob_ops *ops)
134 +{
135 + uint32_t rem;
136 +
137 + if(ofs + ops->len > mtd->size)
138 + goto invalid_arg;
139 + if(ops->datbuf && ops->len && ops->len != mtd->writesize)
140 + goto invalid_arg;
141 + if(ops->ooblen + ops->ooboffs > mtd->oobsize)
142 + goto invalid_arg;
143 +
144 + rem = do_div(ofs, mtd->writesize);
145 + if(rem)
146 + goto invalid_arg;
147 + ofs *= (mtd->writesize + mtd->oobsize);
148 +
149 + if(ops->datbuf)
150 + ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
151 + ops->len, ops->datbuf);
152 + ofs += mtd->writesize + ops->ooboffs;
153 + if(ops->oobbuf)
154 + ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
155 + ops->ooblen, ops->oobbuf);
156 + return 0;
157 +
158 +invalid_arg:
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);
162 + return -EINVAL;
163 +}
164 +
165 +static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
166 + struct mtd_oob_ops *ops)
167 +{
168 + uint32_t rem;
169 +
170 + if(ofs + ops->len > mtd->size)
171 + goto invalid_arg;
172 + if(ops->len && ops->len != mtd->writesize)
173 + goto invalid_arg;
174 + if(ops->ooblen + ops->ooboffs > mtd->oobsize)
175 + goto invalid_arg;
176 +
177 + rem = do_div(ofs, mtd->writesize);
178 + if(rem)
179 + goto invalid_arg;
180 + ofs *= (mtd->writesize + mtd->oobsize);
181 +
182 + if(ops->datbuf)
183 + ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
184 + ops->len, ops->datbuf);
185 + ofs += mtd->writesize + ops->ooboffs;
186 + if(ops->oobbuf)
187 + ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
188 + ops->ooblen, ops->oobbuf);
189 + return 0;
190 +
191 +invalid_arg:
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);
195 + return -EINVAL;
196 +}
197 +
198 +static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
199 + size_t *retlen, u_char *buf)
200 +{
201 + uint32_t rem;
202 +
203 + if(from + len > mtd->size)
204 + goto invalid_arg;
205 + if(len != mtd->writesize)
206 + goto invalid_arg;
207 +
208 + rem = do_div(from, mtd->writesize);
209 + if(rem)
210 + goto invalid_arg;
211 + from *= (mtd->writesize + mtd->oobsize);
212 +
213 + *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
214 + return 0;
215 +
216 +invalid_arg:
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);
219 + return -EINVAL;
220 +}
221 +
222 +static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
223 + size_t *retlen, const u_char *buf)
224 +{
225 + uint32_t rem;
226 +
227 + if(to + len > mtd->size)
228 + goto invalid_arg;
229 + if(len != mtd->writesize)
230 + goto invalid_arg;
231 +
232 + rem = do_div(to, mtd->writesize);
233 + if(rem)
234 + goto invalid_arg;
235 + to *= (mtd->writesize + mtd->oobsize);
236 +
237 + *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
238 + return 0;
239 +
240 +invalid_arg:
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);
243 + return -EINVAL;
244 +}
245 +
246 +static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
247 +{
248 + uint32_t rem;
249 +
250 + if(ofs >= mtd->size)
251 + goto invalid_arg;
252 +
253 + rem = do_div(ofs, mtd->erasesize);
254 + if(rem)
255 + goto invalid_arg;
256 + ofs *= mtd->erasesize / mtd->writesize;
257 + ofs *= (mtd->writesize + mtd->oobsize);
258 +
259 + return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
260 +
261 +invalid_arg:
262 + printk("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, "
263 + "write_size %x\n", ofs, mtd->size, mtd->writesize);
264 + return -EINVAL;
265 +}
266 +
267 +static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
268 +{
269 + uint32_t rem;
270 +
271 + if(ofs >= mtd->size)
272 + goto invalid_arg;
273 +
274 + rem = do_div(ofs, mtd->erasesize);
275 + if(rem)
276 + goto invalid_arg;
277 + ofs *= mtd->erasesize / mtd->writesize;
278 + ofs *= (mtd->writesize + mtd->oobsize);
279 +
280 + if(goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
281 + return -EIO;
282 + return 0;
283 +
284 +invalid_arg:
285 + printk("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, "
286 + "write_size %x\n", ofs, mtd->size, mtd->writesize);
287 + return -EINVAL;
288 +}
289 +
290 +static int goldfish_nand_init_device(struct goldfish_nand *nand, int id)
291 +{
292 + uint32_t name_len;
293 + uint32_t result;
294 + uint32_t flags;
295 + unsigned long irq_flags;
296 + unsigned char __iomem *base = nand->base;
297 + struct mtd_info *mtd = &nand->mtd[id];
298 + char *name;
299 +
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);
316 +
317 + mtd->priv = nand;
318 +
319 + mtd->name = name = kmalloc(name_len + 1, GFP_KERNEL);
320 + if(name == NULL)
321 + return -ENOMEM;
322 +
323 + result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, name);
324 + if(result != name_len) {
325 + kfree(mtd->name);
326 + mtd->name = NULL;
327 + printk("goldfish_nand_init_device failed to get dev name %d != %d\n",
328 + result, name_len);
329 + return -ENODEV;
330 + }
331 + ((char *) mtd->name)[name_len] = '\0';
332 +
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;
338 +
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;
347 +
348 + if (add_mtd_device(mtd)) {
349 + kfree(mtd->name);
350 + mtd->name = NULL;
351 + return -EIO;
352 + }
353 +
354 + return 0;
355 +}
356 +
357 +static int goldfish_nand_probe(struct platform_device *pdev)
358 +{
359 + uint32_t num_dev;
360 + int i;
361 + int err;
362 + uint32_t num_dev_working;
363 + uint32_t version;
364 + struct resource *r;
365 + struct goldfish_nand *nand;
366 + unsigned char __iomem *base;
367 +
368 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
369 + if(r == NULL) {
370 + err = -ENODEV;
371 + goto err_no_io_base;
372 + }
373 +
374 + base = ioremap(r->start, PAGE_SIZE);
375 + if(base == NULL) {
376 + err = -ENOMEM;
377 + goto err_ioremap;
378 + }
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);
383 + err = -ENODEV;
384 + goto err_no_dev;
385 + }
386 + num_dev = readl(base + NAND_NUM_DEV);
387 + if(num_dev == 0) {
388 + err = -ENODEV;
389 + goto err_no_dev;
390 + }
391 +
392 + nand = kzalloc(sizeof(*nand) + sizeof(struct mtd_info) * num_dev, GFP_KERNEL);
393 + if(nand == NULL) {
394 + err = -ENOMEM;
395 + goto err_nand_alloc_failed;
396 + }
397 + spin_lock_init(&nand->lock);
398 + nand->base = base;
399 + nand->mtd_count = num_dev;
400 + platform_set_drvdata(pdev, nand);
401 +
402 + num_dev_working = 0;
403 + for(i = 0; i < num_dev; i++) {
404 + err = goldfish_nand_init_device(nand, i);
405 + if(err == 0)
406 + num_dev_working++;
407 + }
408 + if(num_dev_working == 0) {
409 + err = -ENODEV;
410 + goto err_no_working_dev;
411 + }
412 + return 0;
413 +
414 +err_no_working_dev:
415 + kfree(nand);
416 +err_nand_alloc_failed:
417 +err_no_dev:
418 + iounmap(base);
419 +err_ioremap:
420 +err_no_io_base:
421 + return err;
422 +}
423 +
424 +static int goldfish_nand_remove(struct platform_device *pdev)
425 +{
426 + struct goldfish_nand *nand = platform_get_drvdata(pdev);
427 + int i;
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);
432 + }
433 + }
434 + iounmap(nand->base);
435 + kfree(nand);
436 + return 0;
437 +}
438 +
439 +static struct platform_driver goldfish_nand_driver = {
440 + .probe = goldfish_nand_probe,
441 + .remove = goldfish_nand_remove,
442 + .driver = {
443 + .name = "goldfish_nand"
444 + }
445 +};
446 +
447 +static int __init goldfish_nand_init(void)
448 +{
449 + return platform_driver_register(&goldfish_nand_driver);
450 +}
451 +
452 +static void __exit goldfish_nand_exit(void)
453 +{
454 + platform_driver_unregister(&goldfish_nand_driver);
455 +}
456 +
457 +
458 +module_init(goldfish_nand_init);
459 +module_exit(goldfish_nand_exit);
460 +
461 --- /dev/null
462 +++ b/drivers/mtd/devices/goldfish_nand_reg.h
463 @@ -0,0 +1,58 @@
464 +/* drivers/mtd/devices/goldfish_nand_reg.h
465 +**
466 +** Copyright (C) 2007 Google, Inc.
467 +**
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.
471 +**
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.
476 +**
477 +*/
478 +
479 +#ifndef GOLDFISH_NAND_REG_H
480 +#define GOLDFISH_NAND_REG_H
481 +
482 +enum nand_cmd {
483 + NAND_CMD_GET_DEV_NAME, // Write device name for NAND_DEV to NAND_DATA (vaddr)
484 + NAND_CMD_READ,
485 + NAND_CMD_WRITE,
486 + NAND_CMD_ERASE,
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
489 +};
490 +
491 +enum nand_dev_flags {
492 + NAND_DEV_FLAG_READ_ONLY = 0x00000001
493 +};
494 +
495 +#define NAND_VERSION_CURRENT (1)
496 +
497 +enum nand_reg {
498 + // Global
499 + NAND_VERSION = 0x000,
500 + NAND_NUM_DEV = 0x004,
501 + NAND_DEV = 0x008,
502 +
503 + // Dev info
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,
511 +
512 + // Command
513 + NAND_RESULT = 0x040,
514 + NAND_COMMAND = 0x044,
515 + NAND_DATA = 0x048,
516 + NAND_TRANSFER_SIZE = 0x04c,
517 + NAND_ADDR_LOW = 0x050,
518 + NAND_ADDR_HIGH = 0x054,
519 +};
520 +
521 +#endif
This page took 0.066808 seconds and 5 git commands to generate.