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