aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/onenand/onenand_sim.c
diff options
context:
space:
mode:
authorKyungmin Park <kyungmin.park@samsung.com>2007-06-30 01:14:43 -0400
committerDavid Woodhouse <dwmw2@infradead.org>2007-06-30 03:25:45 -0400
commit8dab169b8bdea3bcbc08b15fdbd9a21526fdbb77 (patch)
tree2c8d95e67f6af700d872102a5d9e5b83e2fe5031 /drivers/mtd/onenand/onenand_sim.c
parentee9745fcf214272b7cdd9d320d044cf433ee958e (diff)
[MTD] OneNAND Simulator support
This simulate various OneNAND flash chips for the MTD onenand layer. It's simple implementation, only basic operations. It don't support the recent changes in NANDSIM such as lazy block allocation, bitflip, and so on. Note: This passed nand-tests. Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers/mtd/onenand/onenand_sim.c')
-rw-r--r--drivers/mtd/onenand/onenand_sim.c495
1 files changed, 495 insertions, 0 deletions
diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c
new file mode 100644
index 000000000000..dfa39dfcc1b2
--- /dev/null
+++ b/drivers/mtd/onenand/onenand_sim.c
@@ -0,0 +1,495 @@
1/*
2 * linux/drivers/mtd/onenand/onenand_sim.c
3 *
4 * The OneNAND simulator
5 *
6 * Copyright(C) 2005-2007 Samsung Electronics
7 * Kyungmin Park <kyungmin.park@samsung.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/vmalloc.h>
18#include <linux/mtd/mtd.h>
19#include <linux/mtd/partitions.h>
20#include <linux/mtd/onenand.h>
21
22#include <asm/io.h>
23#include <asm/sizes.h>
24
25#ifndef CONFIG_ONENAND_SIM_MANUFACTURER
26#define CONFIG_ONENAND_SIM_MANUFACTURER 0xec
27#endif
28#ifndef CONFIG_ONENAND_SIM_DEVICE_ID
29#define CONFIG_ONENAND_SIM_DEVICE_ID 0x04
30#endif
31#ifndef CONFIG_ONENAND_SIM_VERSION_ID
32#define CONFIG_ONENAND_SIM_VERSION_ID 0x1e
33#endif
34
35static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER;
36static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID;
37static int version_id = CONFIG_ONENAND_SIM_VERSION_ID;
38
39struct onenand_flash {
40 void __iomem *base;
41 void __iomem *data;
42};
43
44#define ONENAND_CORE(flash) (flash->data)
45#define ONENAND_CORE_SPARE(flash, this, offset) \
46 ((flash->data) + (this->chipsize) + (offset >> 5))
47
48#define ONENAND_MAIN_AREA(this, offset) \
49 (this->base + ONENAND_DATARAM + offset)
50
51#define ONENAND_SPARE_AREA(this, offset) \
52 (this->base + ONENAND_SPARERAM + offset)
53
54#define ONENAND_GET_WP_STATUS(this) \
55 (readw(this->base + ONENAND_REG_WP_STATUS))
56
57#define ONENAND_SET_WP_STATUS(v, this) \
58 (writew(v, this->base + ONENAND_REG_WP_STATUS))
59
60/* It has all 0xff chars */
61#define MAX_ONENAND_PAGESIZE (2048 + 64)
62static unsigned char *ffchars;
63
64static struct mtd_partition os_partitions[] = {
65 {
66 .name = "OneNAND simulator partition",
67 .offset = 0,
68 .size = MTDPART_SIZ_FULL,
69 },
70};
71
72/*
73 * OneNAND simulator mtd
74 */
75struct onenand_info {
76 struct mtd_info mtd;
77 struct mtd_partition *parts;
78 struct onenand_chip onenand;
79 struct onenand_flash flash;
80};
81
82struct onenand_info *info;
83
84#define DPRINTK(format, args...) \
85do { \
86 printk("%s[%d]: " format "\n", __func__, __LINE__, ##args); \
87} while (0)
88
89/**
90 * onenand_lock_handle - Handle Lock scheme
91 * @param this OneNAND device structure
92 * @param cmd The command to be sent
93 *
94 * Send lock command to OneNAND device.
95 * The lock scheme is depends on chip type.
96 */
97static void onenand_lock_handle(struct onenand_chip *this, int cmd)
98{
99 int block_lock_scheme;
100 int status;
101
102 status = ONENAND_GET_WP_STATUS(this);
103 block_lock_scheme = !(this->options & ONENAND_HAS_CONT_LOCK);
104
105 switch (cmd) {
106 case ONENAND_CMD_UNLOCK:
107 if (block_lock_scheme)
108 ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
109 else
110 ONENAND_SET_WP_STATUS(status | ONENAND_WP_US, this);
111 break;
112
113 case ONENAND_CMD_LOCK:
114 if (block_lock_scheme)
115 ONENAND_SET_WP_STATUS(ONENAND_WP_LS, this);
116 else
117 ONENAND_SET_WP_STATUS(status | ONENAND_WP_LS, this);
118 break;
119
120 case ONENAND_CMD_LOCK_TIGHT:
121 if (block_lock_scheme)
122 ONENAND_SET_WP_STATUS(ONENAND_WP_LTS, this);
123 else
124 ONENAND_SET_WP_STATUS(status | ONENAND_WP_LTS, this);
125 break;
126
127 default:
128 break;
129 }
130}
131
132/**
133 * onenand_bootram_handle - Handle BootRAM area
134 * @param this OneNAND device structure
135 * @param cmd The command to be sent
136 *
137 * Emulate BootRAM area. It is possible to do basic operation using BootRAM.
138 */
139static void onenand_bootram_handle(struct onenand_chip *this, int cmd)
140{
141 switch (cmd) {
142 case ONENAND_CMD_READID:
143 writew(manuf_id, this->base);
144 writew(device_id, this->base + 2);
145 writew(version_id, this->base + 4);
146 break;
147
148 default:
149 /* REVIST: Handle other commands */
150 break;
151 }
152}
153
154/**
155 * onenand_update_interrupt - Set interrupt register
156 * @param this OneNAND device structure
157 * @param cmd The command to be sent
158 *
159 * Update interrupt register. The status is depends on command.
160 */
161static void onenand_update_interrupt(struct onenand_chip *this, int cmd)
162{
163 int interrupt = ONENAND_INT_MASTER;
164
165 switch (cmd) {
166 case ONENAND_CMD_READ:
167 case ONENAND_CMD_READOOB:
168 interrupt |= ONENAND_INT_READ;
169 break;
170
171 case ONENAND_CMD_PROG:
172 case ONENAND_CMD_PROGOOB:
173 interrupt |= ONENAND_INT_WRITE;
174 break;
175
176 case ONENAND_CMD_ERASE:
177 interrupt |= ONENAND_INT_ERASE;
178 break;
179
180 case ONENAND_CMD_RESET:
181 interrupt |= ONENAND_INT_RESET;
182 break;
183
184 default:
185 break;
186 }
187
188 writew(interrupt, this->base + ONENAND_REG_INTERRUPT);
189}
190
191/**
192 * onenand_check_overwrite - Check over-write if happend
193 * @param dest The destination pointer
194 * @param src The source pointer
195 * @param count The length to be check
196 * @return 0 on same, otherwise 1
197 *
198 * Compare the source with destination
199 */
200static int onenand_check_overwrite(void *dest, void *src, size_t count)
201{
202 unsigned int *s = (unsigned int *) src;
203 unsigned int *d = (unsigned int *) dest;
204 int i;
205
206 count >>= 2;
207 for (i = 0; i < count; i++)
208 if ((*s++ ^ *d++) != 0)
209 return 1;
210
211 return 0;
212}
213
214/**
215 * onenand_data_handle - Handle OneNAND Core and DataRAM
216 * @param this OneNAND device structure
217 * @param cmd The command to be sent
218 * @param dataram Which dataram used
219 * @param offset The offset to OneNAND Core
220 *
221 * Copy data from OneNAND Core to DataRAM (read)
222 * Copy data from DataRAM to OneNAND Core (write)
223 * Erase the OneNAND Core (erase)
224 */
225static void onenand_data_handle(struct onenand_chip *this, int cmd,
226 int dataram, unsigned int offset)
227{
228 struct mtd_info *mtd = &info->mtd;
229 struct onenand_flash *flash = this->priv;
230 int main_offset, spare_offset;
231 void __iomem *src;
232 void __iomem *dest;
233 unsigned int i;
234
235 if (dataram) {
236 main_offset = mtd->writesize;
237 spare_offset = mtd->oobsize;
238 } else {
239 main_offset = 0;
240 spare_offset = 0;
241 }
242
243 switch (cmd) {
244 case ONENAND_CMD_READ:
245 src = ONENAND_CORE(flash) + offset;
246 dest = ONENAND_MAIN_AREA(this, main_offset);
247 memcpy(dest, src, mtd->writesize);
248 /* Fall through */
249
250 case ONENAND_CMD_READOOB:
251 src = ONENAND_CORE_SPARE(flash, this, offset);
252 dest = ONENAND_SPARE_AREA(this, spare_offset);
253 memcpy(dest, src, mtd->oobsize);
254 break;
255
256 case ONENAND_CMD_PROG:
257 src = ONENAND_MAIN_AREA(this, main_offset);
258 dest = ONENAND_CORE(flash) + offset;
259 /* To handle partial write */
260 for (i = 0; i < (1 << mtd->subpage_sft); i++) {
261 int off = i * this->subpagesize;
262 if (!memcmp(src + off, ffchars, this->subpagesize))
263 continue;
264 if (memcmp(dest + off, ffchars, this->subpagesize) &&
265 onenand_check_overwrite(dest + off, src + off, this->subpagesize))
266 printk(KERN_ERR "over-write happend at 0x%08x\n", offset);
267 memcpy(dest + off, src + off, this->subpagesize);
268 }
269 /* Fall through */
270
271 case ONENAND_CMD_PROGOOB:
272 src = ONENAND_SPARE_AREA(this, spare_offset);
273 /* Check all data is 0xff chars */
274 if (!memcmp(src, ffchars, mtd->oobsize))
275 break;
276
277 dest = ONENAND_CORE_SPARE(flash, this, offset);
278 if (memcmp(dest, ffchars, mtd->oobsize) &&
279 onenand_check_overwrite(dest, src, mtd->oobsize))
280 printk(KERN_ERR "OOB: over-write happend at 0x%08x\n",
281 offset);
282 memcpy(dest, src, mtd->oobsize);
283 break;
284
285 case ONENAND_CMD_ERASE:
286 memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
287 memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
288 (mtd->erasesize >> 5));
289 break;
290
291 default:
292 break;
293 }
294}
295
296/**
297 * onenand_command_handle - Handle command
298 * @param this OneNAND device structure
299 * @param cmd The command to be sent
300 *
301 * Emulate OneNAND command.
302 */
303static void onenand_command_handle(struct onenand_chip *this, int cmd)
304{
305 unsigned long offset = 0;
306 int block = -1, page = -1, bufferram = -1;
307 int dataram = 0;
308
309 switch (cmd) {
310 case ONENAND_CMD_UNLOCK:
311 case ONENAND_CMD_LOCK:
312 case ONENAND_CMD_LOCK_TIGHT:
313 case ONENAND_CMD_UNLOCK_ALL:
314 onenand_lock_handle(this, cmd);
315 break;
316
317 case ONENAND_CMD_BUFFERRAM:
318 /* Do nothing */
319 return;
320
321 default:
322 block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1);
323 if (block & (1 << ONENAND_DDP_SHIFT)) {
324 block &= ~(1 << ONENAND_DDP_SHIFT);
325 /* The half of chip block */
326 block += this->chipsize >> (this->erase_shift + 1);
327 }
328 if (cmd == ONENAND_CMD_ERASE)
329 break;
330
331 page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8);
332 page = (page >> ONENAND_FPA_SHIFT);
333 bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER);
334 bufferram >>= ONENAND_BSA_SHIFT;
335 bufferram &= ONENAND_BSA_DATARAM1;
336 dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0;
337 break;
338 }
339
340 if (block != -1)
341 offset += block << this->erase_shift;
342
343 if (page != -1)
344 offset += page << this->page_shift;
345
346 onenand_data_handle(this, cmd, dataram, offset);
347
348 onenand_update_interrupt(this, cmd);
349}
350
351/**
352 * onenand_writew - [OneNAND Interface] Emulate write operation
353 * @param value value to write
354 * @param addr address to write
355 *
356 * Write OneNAND register with value
357 */
358static void onenand_writew(unsigned short value, void __iomem * addr)
359{
360 struct onenand_chip *this = info->mtd.priv;
361
362 /* BootRAM handling */
363 if (addr < this->base + ONENAND_DATARAM) {
364 onenand_bootram_handle(this, value);
365 return;
366 }
367 /* Command handling */
368 if (addr == this->base + ONENAND_REG_COMMAND)
369 onenand_command_handle(this, value);
370
371 writew(value, addr);
372}
373
374/**
375 * flash_init - Initialize OneNAND simulator
376 * @param flash OneNAND simulaotr data strucutres
377 *
378 * Initialize OneNAND simulator.
379 */
380static int __init flash_init(struct onenand_flash *flash)
381{
382 int density, size;
383 int buffer_size;
384
385 flash->base = kzalloc(SZ_128K, GFP_KERNEL);
386 if (!flash->base) {
387 printk(KERN_ERR "Unalbe to allocate base address.\n");
388 return -ENOMEM;
389 }
390
391 density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
392 size = ((16 << 20) << density);
393
394 ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
395 if (!ONENAND_CORE(flash)) {
396 printk(KERN_ERR "Unalbe to allocate nand core address.\n");
397 kfree(flash->base);
398 return -ENOMEM;
399 }
400
401 memset(ONENAND_CORE(flash), 0xff, size + (size >> 5));
402
403 /* Setup registers */
404 writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
405 writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
406 writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
407
408 if (density < 2)
409 buffer_size = 0x0400; /* 1KB page */
410 else
411 buffer_size = 0x0800; /* 2KB page */
412 writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE);
413
414 return 0;
415}
416
417/**
418 * flash_exit - Clean up OneNAND simulator
419 * @param flash OneNAND simulaotr data strucutres
420 *
421 * Clean up OneNAND simulator.
422 */
423static void flash_exit(struct onenand_flash *flash)
424{
425 vfree(ONENAND_CORE(flash));
426 kfree(flash->base);
427 kfree(flash);
428}
429
430static int __init onenand_sim_init(void)
431{
432 /* Allocate all 0xff chars pointer */
433 ffchars = kmalloc(MAX_ONENAND_PAGESIZE, GFP_KERNEL);
434 if (!ffchars) {
435 printk(KERN_ERR "Unable to allocate ff chars.\n");
436 return -ENOMEM;
437 }
438 memset(ffchars, 0xff, MAX_ONENAND_PAGESIZE);
439
440 /* Allocate OneNAND simulator mtd pointer */
441 info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
442 if (!info) {
443 printk(KERN_ERR "Unable to allocate core structures.\n");
444 kfree(ffchars);
445 return -ENOMEM;
446 }
447
448 /* Override write_word function */
449 info->onenand.write_word = onenand_writew;
450
451 if (flash_init(&info->flash)) {
452 printk(KERN_ERR "Unable to allocat flash.\n");
453 kfree(ffchars);
454 kfree(info);
455 return -ENOMEM;
456 }
457
458 info->parts = os_partitions;
459
460 info->onenand.base = info->flash.base;
461 info->onenand.priv = &info->flash;
462
463 info->mtd.name = "OneNAND simulator";
464 info->mtd.priv = &info->onenand;
465 info->mtd.owner = THIS_MODULE;
466
467 if (onenand_scan(&info->mtd, 1)) {
468 flash_exit(&info->flash);
469 kfree(ffchars);
470 kfree(info);
471 return -ENXIO;
472 }
473
474 add_mtd_partitions(&info->mtd, info->parts, ARRAY_SIZE(os_partitions));
475
476 return 0;
477}
478
479static void __exit onenand_sim_exit(void)
480{
481 struct onenand_chip *this = info->mtd.priv;
482 struct onenand_flash *flash = this->priv;
483
484 onenand_release(&info->mtd);
485 flash_exit(flash);
486 kfree(ffchars);
487 kfree(info);
488}
489
490module_init(onenand_sim_init);
491module_exit(onenand_sim_exit);
492
493MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
494MODULE_DESCRIPTION("The OneNAND flash simulator");
495MODULE_LICENSE("GPL");