OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [mtd/] [onenand/] [onenand_sim.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 *  linux/drivers/mtd/onenand/onenand_sim.c
3
 *
4
 *  The OneNAND simulator
5
 *
6
 *  Copyright © 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 <linux/io.h>
23
 
24
#ifndef CONFIG_ONENAND_SIM_MANUFACTURER
25
#define CONFIG_ONENAND_SIM_MANUFACTURER         0xec
26
#endif
27
#ifndef CONFIG_ONENAND_SIM_DEVICE_ID
28
#define CONFIG_ONENAND_SIM_DEVICE_ID            0x04
29
#endif
30
#ifndef CONFIG_ONENAND_SIM_VERSION_ID
31
#define CONFIG_ONENAND_SIM_VERSION_ID           0x1e
32
#endif
33
 
34
static int manuf_id     = CONFIG_ONENAND_SIM_MANUFACTURER;
35
static int device_id    = CONFIG_ONENAND_SIM_DEVICE_ID;
36
static int version_id   = CONFIG_ONENAND_SIM_VERSION_ID;
37
 
38
struct onenand_flash {
39
        void __iomem *base;
40
        void __iomem *data;
41
};
42
 
43
#define ONENAND_CORE(flash)             (flash->data)
44
#define ONENAND_CORE_SPARE(flash, this, offset)                         \
45
        ((flash->data) + (this->chipsize) + (offset >> 5))
46
 
47
#define ONENAND_MAIN_AREA(this, offset)                                 \
48
        (this->base + ONENAND_DATARAM + offset)
49
 
50
#define ONENAND_SPARE_AREA(this, offset)                                \
51
        (this->base + ONENAND_SPARERAM + offset)
52
 
53
#define ONENAND_GET_WP_STATUS(this)                                     \
54
        (readw(this->base + ONENAND_REG_WP_STATUS))
55
 
56
#define ONENAND_SET_WP_STATUS(v, this)                                  \
57
        (writew(v, this->base + ONENAND_REG_WP_STATUS))
58
 
59
/* It has all 0xff chars */
60
#define MAX_ONENAND_PAGESIZE            (2048 + 64)
61
static unsigned char *ffchars;
62
 
63
static struct mtd_partition os_partitions[] = {
64
        {
65
                .name           = "OneNAND simulator partition",
66
                .offset         = 0,
67
                .size           = MTDPART_SIZ_FULL,
68
        },
69
};
70
 
71
/*
72
 * OneNAND simulator mtd
73
 */
74
struct onenand_info {
75
        struct mtd_info         mtd;
76
        struct mtd_partition    *parts;
77
        struct onenand_chip     onenand;
78
        struct onenand_flash    flash;
79
};
80
 
81
static struct onenand_info *info;
82
 
83
#define DPRINTK(format, args...)                                        \
84
do {                                                                    \
85
        printk(KERN_DEBUG "%s[%d]: " format "\n", __func__,             \
86
                           __LINE__, ##args);                           \
87
} while (0)
88
 
89
/**
90
 * onenand_lock_handle - Handle Lock scheme
91
 * @this:               OneNAND device structure
92
 * @cmd:                The command to be sent
93
 *
94
 * Send lock command to OneNAND device.
95
 * The lock scheme depends on chip type.
96
 */
97
static 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
 * @this:               OneNAND device structure
135
 * @cmd:                The command to be sent
136
 *
137
 * Emulate BootRAM area. It is possible to do basic operation using BootRAM.
138
 */
139
static 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
 * @this:         OneNAND device structure
157
 * @cmd:          The command to be sent
158
 *
159
 * Update interrupt register. The status depends on command.
160
 */
161
static 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 if over-write happened
193
 * @dest:               The destination pointer
194
 * @src:                The source pointer
195
 * @count:              The length to be check
196
 *
197
 * Returns:             0 on same, otherwise 1
198
 *
199
 * Compare the source with destination
200
 */
201
static int onenand_check_overwrite(void *dest, void *src, size_t count)
202
{
203
        unsigned int *s = (unsigned int *) src;
204
        unsigned int *d = (unsigned int *) dest;
205
        int i;
206
 
207
        count >>= 2;
208
        for (i = 0; i < count; i++)
209
                if ((*s++ ^ *d++) != 0)
210
                        return 1;
211
 
212
        return 0;
213
}
214
 
215
/**
216
 * onenand_data_handle - Handle OneNAND Core and DataRAM
217
 * @this:               OneNAND device structure
218
 * @cmd:                The command to be sent
219
 * @dataram:            Which dataram used
220
 * @offset:             The offset to OneNAND Core
221
 *
222
 * Copy data from OneNAND Core to DataRAM (read)
223
 * Copy data from DataRAM to OneNAND Core (write)
224
 * Erase the OneNAND Core (erase)
225
 */
226
static void onenand_data_handle(struct onenand_chip *this, int cmd,
227
                                int dataram, unsigned int offset)
228
{
229
        struct mtd_info *mtd = &info->mtd;
230
        struct onenand_flash *flash = this->priv;
231
        int main_offset, spare_offset;
232
        void __iomem *src;
233
        void __iomem *dest;
234
        unsigned int i;
235
 
236
        if (dataram) {
237
                main_offset = mtd->writesize;
238
                spare_offset = mtd->oobsize;
239
        } else {
240
                main_offset = 0;
241
                spare_offset = 0;
242
        }
243
 
244
        switch (cmd) {
245
        case ONENAND_CMD_READ:
246
                src = ONENAND_CORE(flash) + offset;
247
                dest = ONENAND_MAIN_AREA(this, main_offset);
248
                memcpy(dest, src, mtd->writesize);
249
                /* Fall through */
250
 
251
        case ONENAND_CMD_READOOB:
252
                src = ONENAND_CORE_SPARE(flash, this, offset);
253
                dest = ONENAND_SPARE_AREA(this, spare_offset);
254
                memcpy(dest, src, mtd->oobsize);
255
                break;
256
 
257
        case ONENAND_CMD_PROG:
258
                src = ONENAND_MAIN_AREA(this, main_offset);
259
                dest = ONENAND_CORE(flash) + offset;
260
                /* To handle partial write */
261
                for (i = 0; i < (1 << mtd->subpage_sft); i++) {
262
                        int off = i * this->subpagesize;
263
                        if (!memcmp(src + off, ffchars, this->subpagesize))
264
                                continue;
265
                        if (memcmp(dest + off, ffchars, this->subpagesize) &&
266
                            onenand_check_overwrite(dest + off, src + off, this->subpagesize))
267
                                printk(KERN_ERR "over-write happend at 0x%08x\n", offset);
268
                        memcpy(dest + off, src + off, this->subpagesize);
269
                }
270
                /* Fall through */
271
 
272
        case ONENAND_CMD_PROGOOB:
273
                src = ONENAND_SPARE_AREA(this, spare_offset);
274
                /* Check all data is 0xff chars */
275
                if (!memcmp(src, ffchars, mtd->oobsize))
276
                        break;
277
 
278
                dest = ONENAND_CORE_SPARE(flash, this, offset);
279
                if (memcmp(dest, ffchars, mtd->oobsize) &&
280
                    onenand_check_overwrite(dest, src, mtd->oobsize))
281
                        printk(KERN_ERR "OOB: over-write happend at 0x%08x\n",
282
                               offset);
283
                memcpy(dest, src, mtd->oobsize);
284
                break;
285
 
286
        case ONENAND_CMD_ERASE:
287
                memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
288
                memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
289
                       (mtd->erasesize >> 5));
290
                break;
291
 
292
        default:
293
                break;
294
        }
295
}
296
 
297
/**
298
 * onenand_command_handle - Handle command
299
 * @this:               OneNAND device structure
300
 * @cmd:                The command to be sent
301
 *
302
 * Emulate OneNAND command.
303
 */
304
static void onenand_command_handle(struct onenand_chip *this, int cmd)
305
{
306
        unsigned long offset = 0;
307
        int block = -1, page = -1, bufferram = -1;
308
        int dataram = 0;
309
 
310
        switch (cmd) {
311
        case ONENAND_CMD_UNLOCK:
312
        case ONENAND_CMD_LOCK:
313
        case ONENAND_CMD_LOCK_TIGHT:
314
        case ONENAND_CMD_UNLOCK_ALL:
315
                onenand_lock_handle(this, cmd);
316
                break;
317
 
318
        case ONENAND_CMD_BUFFERRAM:
319
                /* Do nothing */
320
                return;
321
 
322
        default:
323
                block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1);
324
                if (block & (1 << ONENAND_DDP_SHIFT)) {
325
                        block &= ~(1 << ONENAND_DDP_SHIFT);
326
                        /* The half of chip block */
327
                        block += this->chipsize >> (this->erase_shift + 1);
328
                }
329
                if (cmd == ONENAND_CMD_ERASE)
330
                        break;
331
 
332
                page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8);
333
                page = (page >> ONENAND_FPA_SHIFT);
334
                bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER);
335
                bufferram >>= ONENAND_BSA_SHIFT;
336
                bufferram &= ONENAND_BSA_DATARAM1;
337
                dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0;
338
                break;
339
        }
340
 
341
        if (block != -1)
342
                offset += block << this->erase_shift;
343
 
344
        if (page != -1)
345
                offset += page << this->page_shift;
346
 
347
        onenand_data_handle(this, cmd, dataram, offset);
348
 
349
        onenand_update_interrupt(this, cmd);
350
}
351
 
352
/**
353
 * onenand_writew - [OneNAND Interface] Emulate write operation
354
 * @value:              value to write
355
 * @addr:               address to write
356
 *
357
 * Write OneNAND register with value
358
 */
359
static void onenand_writew(unsigned short value, void __iomem * addr)
360
{
361
        struct onenand_chip *this = info->mtd.priv;
362
 
363
        /* BootRAM handling */
364
        if (addr < this->base + ONENAND_DATARAM) {
365
                onenand_bootram_handle(this, value);
366
                return;
367
        }
368
        /* Command handling */
369
        if (addr == this->base + ONENAND_REG_COMMAND)
370
                onenand_command_handle(this, value);
371
 
372
        writew(value, addr);
373
}
374
 
375
/**
376
 * flash_init - Initialize OneNAND simulator
377
 * @flash:              OneNAND simulator data strucutres
378
 *
379
 * Initialize OneNAND simulator.
380
 */
381
static int __init flash_init(struct onenand_flash *flash)
382
{
383
        int density, size;
384
        int buffer_size;
385
 
386
        flash->base = kzalloc(131072, GFP_KERNEL);
387
        if (!flash->base) {
388
                printk(KERN_ERR "Unable to allocate base address.\n");
389
                return -ENOMEM;
390
        }
391
 
392
        density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
393
        size = ((16 << 20) << density);
394
 
395
        ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
396
        if (!ONENAND_CORE(flash)) {
397
                printk(KERN_ERR "Unable to allocate nand core address.\n");
398
                kfree(flash->base);
399
                return -ENOMEM;
400
        }
401
 
402
        memset(ONENAND_CORE(flash), 0xff, size + (size >> 5));
403
 
404
        /* Setup registers */
405
        writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
406
        writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
407
        writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
408
 
409
        if (density < 2)
410
                buffer_size = 0x0400;   /* 1KiB page */
411
        else
412
                buffer_size = 0x0800;   /* 2KiB page */
413
        writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE);
414
 
415
        return 0;
416
}
417
 
418
/**
419
 * flash_exit - Clean up OneNAND simulator
420
 * @flash:              OneNAND simulator data structures
421
 *
422
 * Clean up OneNAND simulator.
423
 */
424
static void flash_exit(struct onenand_flash *flash)
425
{
426
        vfree(ONENAND_CORE(flash));
427
        kfree(flash->base);
428
}
429
 
430
static 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 allocate 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
 
479
static 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
 
490
module_init(onenand_sim_init);
491
module_exit(onenand_sim_exit);
492
 
493
MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
494
MODULE_DESCRIPTION("The OneNAND flash simulator");
495
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.