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