ps3: R.I.P.
[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