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