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