linux/drivers/staging/spectra/ffsport.c
<<
>>
Prefs
   1/*
   2 * NAND Flash Controller Device Driver
   3 * Copyright (c) 2009, Intel Corporation and its suppliers.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms and conditions of the GNU General Public License,
   7 * version 2, as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 * You should have received a copy of the GNU General Public License along with
  15 * this program; if not, write to the Free Software Foundation, Inc.,
  16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  17 *
  18 */
  19
  20#include "ffsport.h"
  21#include "flash.h"
  22#include <linux/interrupt.h>
  23#include <linux/delay.h>
  24#include <linux/blkdev.h>
  25#include <linux/wait.h>
  26#include <linux/mutex.h>
  27#include <linux/kthread.h>
  28#include <linux/log2.h>
  29#include <linux/init.h>
  30#include <linux/slab.h>
  31#include <linux/async.h>
  32
  33/**** Helper functions used for Div, Remainder operation on u64 ****/
  34
  35/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
  36* Function:     GLOB_Calc_Used_Bits
  37* Inputs:       Power of 2 number
  38* Outputs:      Number of Used Bits
  39*               0, if the argument is 0
  40* Description:  Calculate the number of bits used by a given power of 2 number
  41*               Number can be upto 32 bit
  42*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
  43int GLOB_Calc_Used_Bits(u32 n)
  44{
  45        int tot_bits = 0;
  46
  47        if (n >= 1 << 16) {
  48                n >>= 16;
  49                tot_bits += 16;
  50        }
  51
  52        if (n >= 1 << 8) {
  53                n >>=  8;
  54                tot_bits +=  8;
  55        }
  56
  57        if (n >= 1 << 4) {
  58                n >>=  4;
  59                tot_bits +=  4;
  60        }
  61
  62        if (n >= 1 << 2) {
  63                n >>=  2;
  64                tot_bits +=  2;
  65        }
  66
  67        if (n >= 1 << 1)
  68                tot_bits +=  1;
  69
  70        return ((n == 0) ? (0) : tot_bits);
  71}
  72
  73/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
  74* Function:     GLOB_u64_Div
  75* Inputs:       Number of u64
  76*               A power of 2 number as Division
  77* Outputs:      Quotient of the Divisor operation
  78* Description:  It divides the address by divisor by using bit shift operation
  79*               (essentially without explicitely using "/").
  80*               Divisor is a power of 2 number and Divided is of u64
  81*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
  82u64 GLOB_u64_Div(u64 addr, u32 divisor)
  83{
  84        return  (u64)(addr >> GLOB_Calc_Used_Bits(divisor));
  85}
  86
  87/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
  88* Function:     GLOB_u64_Remainder
  89* Inputs:       Number of u64
  90*               Divisor Type (1 -PageAddress, 2- BlockAddress)
  91* Outputs:      Remainder of the Division operation
  92* Description:  It calculates the remainder of a number (of u64) by
  93*               divisor(power of 2 number ) by using bit shifting and multiply
  94*               operation(essentially without explicitely using "/").
  95*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
  96u64 GLOB_u64_Remainder(u64 addr, u32 divisor_type)
  97{
  98        u64 result = 0;
  99
 100        if (divisor_type == 1) { /* Remainder -- Page */
 101                result = (addr >> DeviceInfo.nBitsInPageDataSize);
 102                result = result * DeviceInfo.wPageDataSize;
 103        } else if (divisor_type == 2) { /* Remainder -- Block */
 104                result = (addr >> DeviceInfo.nBitsInBlockDataSize);
 105                result = result * DeviceInfo.wBlockDataSize;
 106        }
 107
 108        result = addr - result;
 109
 110        return result;
 111}
 112
 113#define NUM_DEVICES             1
 114#define PARTITIONS              8
 115
 116#define GLOB_SBD_NAME          "nd"
 117#define GLOB_SBD_IRQ_NUM       (29)
 118
 119#define GLOB_SBD_IOCTL_GC                        (0x7701)
 120#define GLOB_SBD_IOCTL_WL                        (0x7702)
 121#define GLOB_SBD_IOCTL_FORMAT                    (0x7703)
 122#define GLOB_SBD_IOCTL_ERASE_FLASH               (0x7704)
 123#define GLOB_SBD_IOCTL_FLUSH_CACHE               (0x7705)
 124#define GLOB_SBD_IOCTL_COPY_BLK_TABLE            (0x7706)
 125#define GLOB_SBD_IOCTL_COPY_WEAR_LEVELING_TABLE  (0x7707)
 126#define GLOB_SBD_IOCTL_GET_NAND_INFO             (0x7708)
 127#define GLOB_SBD_IOCTL_WRITE_DATA                (0x7709)
 128#define GLOB_SBD_IOCTL_READ_DATA                 (0x770A)
 129
 130static int reserved_mb = 0;
 131module_param(reserved_mb, int, 0);
 132MODULE_PARM_DESC(reserved_mb, "Reserved space for OS image, in MiB (default 25 MiB)");
 133
 134int nand_debug_level;
 135module_param(nand_debug_level, int, 0644);
 136MODULE_PARM_DESC(nand_debug_level, "debug level value: 1-3");
 137
 138MODULE_LICENSE("GPL");
 139
 140struct spectra_nand_dev {
 141        struct pci_dev *dev;
 142        u64 size;
 143        u16 users;
 144        spinlock_t qlock;
 145        void __iomem *ioaddr;  /* Mapped address */
 146        struct request_queue *queue;
 147        struct task_struct *thread;
 148        struct gendisk *gd;
 149        u8 *tmp_buf;
 150};
 151
 152
 153static int GLOB_SBD_majornum;
 154
 155static char *GLOB_version = GLOB_VERSION;
 156
 157static struct spectra_nand_dev nand_device[NUM_DEVICES];
 158
 159static struct mutex spectra_lock;
 160
 161static int res_blks_os = 1;
 162
 163struct spectra_indentfy_dev_tag IdentifyDeviceData;
 164
 165static int force_flush_cache(void)
 166{
 167        nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n",
 168                __FILE__, __LINE__, __func__);
 169
 170        if (ERR == GLOB_FTL_Flush_Cache()) {
 171                printk(KERN_ERR "Fail to Flush FTL Cache!\n");
 172                return -EFAULT;
 173        }
 174#if CMD_DMA
 175                if (glob_ftl_execute_cmds())
 176                        return -EIO;
 177                else
 178                        return 0;
 179#endif
 180        return 0;
 181}
 182
 183struct ioctl_rw_page_info {
 184        u8 *data;
 185        unsigned int page;
 186};
 187
 188static int ioctl_read_page_data(unsigned long arg)
 189{
 190        u8 *buf;
 191        struct ioctl_rw_page_info info;
 192        int result = PASS;
 193
 194        if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
 195                return -EFAULT;
 196
 197        buf = kmalloc(IdentifyDeviceData.PageDataSize, GFP_ATOMIC);
 198        if (!buf) {
 199                printk(KERN_ERR "ioctl_read_page_data: "
 200                       "failed to allocate memory\n");
 201                return -ENOMEM;
 202        }
 203
 204        mutex_lock(&spectra_lock);
 205        result = GLOB_FTL_Page_Read(buf,
 206                (u64)info.page * IdentifyDeviceData.PageDataSize);
 207        mutex_unlock(&spectra_lock);
 208
 209        if (copy_to_user((void __user *)info.data, buf,
 210                           IdentifyDeviceData.PageDataSize)) {
 211                printk(KERN_ERR "ioctl_read_page_data: "
 212                       "failed to copy user data\n");
 213                kfree(buf);
 214                return -EFAULT;
 215        }
 216
 217        kfree(buf);
 218        return result;
 219}
 220
 221static int ioctl_write_page_data(unsigned long arg)
 222{
 223        u8 *buf;
 224        struct ioctl_rw_page_info info;
 225        int result = PASS;
 226
 227        if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
 228                return -EFAULT;
 229
 230        buf = kmalloc(IdentifyDeviceData.PageDataSize, GFP_ATOMIC);
 231        if (!buf) {
 232                printk(KERN_ERR "ioctl_write_page_data: "
 233                       "failed to allocate memory\n");
 234                return -ENOMEM;
 235        }
 236
 237        if (copy_from_user(buf, (void __user *)info.data,
 238                           IdentifyDeviceData.PageDataSize)) {
 239                printk(KERN_ERR "ioctl_write_page_data: "
 240                       "failed to copy user data\n");
 241                kfree(buf);
 242                return -EFAULT;
 243        }
 244
 245        mutex_lock(&spectra_lock);
 246        result = GLOB_FTL_Page_Write(buf,
 247                (u64)info.page * IdentifyDeviceData.PageDataSize);
 248        mutex_unlock(&spectra_lock);
 249
 250        kfree(buf);
 251        return result;
 252}
 253
 254/* Return how many blocks should be reserved for bad block replacement */
 255static int get_res_blk_num_bad_blk(void)
 256{
 257        return IdentifyDeviceData.wDataBlockNum / 10;
 258}
 259
 260/* Return how many blocks should be reserved for OS image */
 261static int get_res_blk_num_os(void)
 262{
 263        u32 res_blks, blk_size;
 264
 265        blk_size = IdentifyDeviceData.PageDataSize *
 266                IdentifyDeviceData.PagesPerBlock;
 267
 268        res_blks = (reserved_mb * 1024 * 1024) / blk_size;
 269
 270        if ((res_blks < 1) || (res_blks >= IdentifyDeviceData.wDataBlockNum))
 271                res_blks = 1; /* Reserved 1 block for block table */
 272
 273        return res_blks;
 274}
 275
 276/* Transfer a full request. */
 277static int do_transfer(struct spectra_nand_dev *tr, struct request *req)
 278{
 279        u64 start_addr, addr;
 280        u32 logical_start_sect, hd_start_sect;
 281        u32 nsect, hd_sects;
 282        u32 rsect, tsect = 0;
 283        char *buf;
 284        u32 ratio = IdentifyDeviceData.PageDataSize >> 9;
 285
 286        start_addr = (u64)(blk_rq_pos(req)) << 9;
 287        /* Add a big enough offset to prevent the OS Image from
 288        *  being accessed or damaged by file system */
 289        start_addr += IdentifyDeviceData.PageDataSize *
 290                        IdentifyDeviceData.PagesPerBlock *
 291                        res_blks_os;
 292
 293        if (req->cmd_type & REQ_FLUSH) {
 294                if (force_flush_cache()) /* Fail to flush cache */
 295                        return -EIO;
 296                else
 297                        return 0;
 298        }
 299
 300        if (req->cmd_type != REQ_TYPE_FS)
 301                return -EIO;
 302
 303        if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > get_capacity(tr->gd)) {
 304                printk(KERN_ERR "Spectra error: request over the NAND "
 305                        "capacity!sector %d, current_nr_sectors %d, "
 306                        "while capacity is %d\n",
 307                        (int)blk_rq_pos(req),
 308                        blk_rq_cur_sectors(req),
 309                        (int)get_capacity(tr->gd));
 310                return -EIO;
 311        }
 312
 313        logical_start_sect = start_addr >> 9;
 314        hd_start_sect = logical_start_sect / ratio;
 315        rsect = logical_start_sect - hd_start_sect * ratio;
 316
 317        addr = (u64)hd_start_sect * ratio * 512;
 318        buf = req->buffer;
 319        nsect = blk_rq_cur_sectors(req);
 320
 321        if (rsect)
 322                tsect =  (ratio - rsect) < nsect ? (ratio - rsect) : nsect;
 323
 324        switch (rq_data_dir(req)) {
 325        case READ:
 326                /* Read the first NAND page */
 327                if (rsect) {
 328                        if (GLOB_FTL_Page_Read(tr->tmp_buf, addr)) {
 329                                printk(KERN_ERR "Error in %s, Line %d\n",
 330                                        __FILE__, __LINE__);
 331                                return -EIO;
 332                        }
 333                        memcpy(buf, tr->tmp_buf + (rsect << 9), tsect << 9);
 334                        addr += IdentifyDeviceData.PageDataSize;
 335                        buf += tsect << 9;
 336                        nsect -= tsect;
 337                }
 338
 339                /* Read the other NAND pages */
 340                for (hd_sects = nsect / ratio; hd_sects > 0; hd_sects--) {
 341                        if (GLOB_FTL_Page_Read(buf, addr)) {
 342                                printk(KERN_ERR "Error in %s, Line %d\n",
 343                                        __FILE__, __LINE__);
 344                                return -EIO;
 345                        }
 346                        addr += IdentifyDeviceData.PageDataSize;
 347                        buf += IdentifyDeviceData.PageDataSize;
 348                }
 349
 350                /* Read the last NAND pages */
 351                if (nsect % ratio) {
 352                        if (GLOB_FTL_Page_Read(tr->tmp_buf, addr)) {
 353                                printk(KERN_ERR "Error in %s, Line %d\n",
 354                                        __FILE__, __LINE__);
 355                                return -EIO;
 356                        }
 357                        memcpy(buf, tr->tmp_buf, (nsect % ratio) << 9);
 358                }
 359#if CMD_DMA
 360                if (glob_ftl_execute_cmds())
 361                        return -EIO;
 362                else
 363                        return 0;
 364#endif
 365                return 0;
 366
 367        case WRITE:
 368                /* Write the first NAND page */
 369                if (rsect) {
 370                        if (GLOB_FTL_Page_Read(tr->tmp_buf, addr)) {
 371                                printk(KERN_ERR "Error in %s, Line %d\n",
 372                                        __FILE__, __LINE__);
 373                                return -EIO;
 374                        }
 375                        memcpy(tr->tmp_buf + (rsect << 9), buf, tsect << 9);
 376                        if (GLOB_FTL_Page_Write(tr->tmp_buf, addr)) {
 377                                printk(KERN_ERR "Error in %s, Line %d\n",
 378                                        __FILE__, __LINE__);
 379                                return -EIO;
 380                        }
 381                        addr += IdentifyDeviceData.PageDataSize;
 382                        buf += tsect << 9;
 383                        nsect -= tsect;
 384                }
 385
 386                /* Write the other NAND pages */
 387                for (hd_sects = nsect / ratio; hd_sects > 0; hd_sects--) {
 388                        if (GLOB_FTL_Page_Write(buf, addr)) {
 389                                printk(KERN_ERR "Error in %s, Line %d\n",
 390                                        __FILE__, __LINE__);
 391                                return -EIO;
 392                        }
 393                        addr += IdentifyDeviceData.PageDataSize;
 394                        buf += IdentifyDeviceData.PageDataSize;
 395                }
 396
 397                /* Write the last NAND pages */
 398                if (nsect % ratio) {
 399                        if (GLOB_FTL_Page_Read(tr->tmp_buf, addr)) {
 400                                printk(KERN_ERR "Error in %s, Line %d\n",
 401                                        __FILE__, __LINE__);
 402                                return -EIO;
 403                        }
 404                        memcpy(tr->tmp_buf, buf, (nsect % ratio) << 9);
 405                        if (GLOB_FTL_Page_Write(tr->tmp_buf, addr)) {
 406                                printk(KERN_ERR "Error in %s, Line %d\n",
 407                                        __FILE__, __LINE__);
 408                                return -EIO;
 409                        }
 410                }
 411#if CMD_DMA
 412                if (glob_ftl_execute_cmds())
 413                        return -EIO;
 414                else
 415                        return 0;
 416#endif
 417                return 0;
 418
 419        default:
 420                printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
 421                return -EIO;
 422        }
 423}
 424
 425/* This function is copied from drivers/mtd/mtd_blkdevs.c */
 426static int spectra_trans_thread(void *arg)
 427{
 428        struct spectra_nand_dev *tr = arg;
 429        struct request_queue *rq = tr->queue;
 430        struct request *req = NULL;
 431
 432        /* we might get involved when memory gets low, so use PF_MEMALLOC */
 433        current->flags |= PF_MEMALLOC;
 434
 435        spin_lock_irq(rq->queue_lock);
 436        while (!kthread_should_stop()) {
 437                int res;
 438
 439                if (!req) {
 440                        req = blk_fetch_request(rq);
 441                        if (!req) {
 442                                set_current_state(TASK_INTERRUPTIBLE);
 443                                spin_unlock_irq(rq->queue_lock);
 444                                schedule();
 445                                spin_lock_irq(rq->queue_lock);
 446                                continue;
 447                        }
 448                }
 449
 450                spin_unlock_irq(rq->queue_lock);
 451
 452                mutex_lock(&spectra_lock);
 453                res = do_transfer(tr, req);
 454                mutex_unlock(&spectra_lock);
 455
 456                spin_lock_irq(rq->queue_lock);
 457
 458                if (!__blk_end_request_cur(req, res))
 459                        req = NULL;
 460        }
 461
 462        if (req)
 463                __blk_end_request_all(req, -EIO);
 464
 465        spin_unlock_irq(rq->queue_lock);
 466
 467        return 0;
 468}
 469
 470
 471/* Request function that "handles clustering". */
 472static void GLOB_SBD_request(struct request_queue *rq)
 473{
 474        struct spectra_nand_dev *pdev = rq->queuedata;
 475        wake_up_process(pdev->thread);
 476}
 477
 478static int GLOB_SBD_open(struct block_device *bdev, fmode_t mode)
 479
 480{
 481        nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n",
 482                       __FILE__, __LINE__, __func__);
 483        return 0;
 484}
 485
 486static int GLOB_SBD_release(struct gendisk *disk, fmode_t mode)
 487{
 488        int ret;
 489
 490        nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n",
 491                       __FILE__, __LINE__, __func__);
 492
 493        mutex_lock(&spectra_lock);
 494        ret = force_flush_cache();
 495        mutex_unlock(&spectra_lock);
 496
 497        return 0;
 498}
 499
 500static int GLOB_SBD_getgeo(struct block_device *bdev, struct hd_geometry *geo)
 501{
 502        geo->heads = 4;
 503        geo->sectors = 16;
 504        geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16);
 505
 506        nand_dbg_print(NAND_DBG_DEBUG,
 507                "heads: %d, sectors: %d, cylinders: %d\n",
 508                geo->heads, geo->sectors, geo->cylinders);
 509
 510        return 0;
 511}
 512
 513int GLOB_SBD_ioctl(struct block_device *bdev, fmode_t mode,
 514                unsigned int cmd, unsigned long arg)
 515{
 516        int ret;
 517
 518        nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n",
 519                       __FILE__, __LINE__, __func__);
 520
 521        switch (cmd) {
 522        case GLOB_SBD_IOCTL_GC:
 523                nand_dbg_print(NAND_DBG_DEBUG,
 524                               "Spectra IOCTL: Garbage Collection "
 525                               "being performed\n");
 526                if (PASS != GLOB_FTL_Garbage_Collection())
 527                        return -EFAULT;
 528                return 0;
 529
 530        case GLOB_SBD_IOCTL_WL:
 531                nand_dbg_print(NAND_DBG_DEBUG,
 532                               "Spectra IOCTL: Static Wear Leveling "
 533                               "being performed\n");
 534                if (PASS != GLOB_FTL_Wear_Leveling())
 535                        return -EFAULT;
 536                return 0;
 537
 538        case GLOB_SBD_IOCTL_FORMAT:
 539                nand_dbg_print(NAND_DBG_DEBUG, "Spectra IOCTL: Flash format "
 540                               "being performed\n");
 541                if (PASS != GLOB_FTL_Flash_Format())
 542                        return -EFAULT;
 543                return 0;
 544
 545        case GLOB_SBD_IOCTL_FLUSH_CACHE:
 546                nand_dbg_print(NAND_DBG_DEBUG, "Spectra IOCTL: Cache flush "
 547                               "being performed\n");
 548                mutex_lock(&spectra_lock);
 549                ret = force_flush_cache();
 550                mutex_unlock(&spectra_lock);
 551                return ret;
 552
 553        case GLOB_SBD_IOCTL_COPY_BLK_TABLE:
 554                nand_dbg_print(NAND_DBG_DEBUG, "Spectra IOCTL: "
 555                               "Copy block table\n");
 556                if (copy_to_user((void __user *)arg,
 557                        get_blk_table_start_addr(),
 558                        get_blk_table_len()))
 559                        return -EFAULT;
 560                return 0;
 561
 562        case GLOB_SBD_IOCTL_COPY_WEAR_LEVELING_TABLE:
 563                nand_dbg_print(NAND_DBG_DEBUG, "Spectra IOCTL: "
 564                               "Copy wear leveling table\n");
 565                if (copy_to_user((void __user *)arg,
 566                        get_wear_leveling_table_start_addr(),
 567                        get_wear_leveling_table_len()))
 568                        return -EFAULT;
 569                return 0;
 570
 571        case GLOB_SBD_IOCTL_GET_NAND_INFO:
 572                nand_dbg_print(NAND_DBG_DEBUG, "Spectra IOCTL: "
 573                               "Get NAND info\n");
 574                if (copy_to_user((void __user *)arg, &IdentifyDeviceData,
 575                        sizeof(IdentifyDeviceData)))
 576                        return -EFAULT;
 577                return 0;
 578
 579        case GLOB_SBD_IOCTL_WRITE_DATA:
 580                nand_dbg_print(NAND_DBG_DEBUG, "Spectra IOCTL: "
 581                               "Write one page data\n");
 582                return ioctl_write_page_data(arg);
 583
 584        case GLOB_SBD_IOCTL_READ_DATA:
 585                nand_dbg_print(NAND_DBG_DEBUG, "Spectra IOCTL: "
 586                               "Read one page data\n");
 587                return ioctl_read_page_data(arg);
 588        }
 589
 590        return -ENOTTY;
 591}
 592
 593static DEFINE_MUTEX(ffsport_mutex);
 594
 595int GLOB_SBD_unlocked_ioctl(struct block_device *bdev, fmode_t mode,
 596                unsigned int cmd, unsigned long arg)
 597{
 598        int ret;
 599
 600        mutex_lock(&ffsport_mutex);
 601        ret = GLOB_SBD_ioctl(bdev, mode, cmd, arg);
 602        mutex_unlock(&ffsport_mutex);
 603
 604        return ret;
 605}
 606
 607static struct block_device_operations GLOB_SBD_ops = {
 608        .owner = THIS_MODULE,
 609        .open = GLOB_SBD_open,
 610        .release = GLOB_SBD_release,
 611        .ioctl = GLOB_SBD_unlocked_ioctl,
 612        .getgeo = GLOB_SBD_getgeo,
 613};
 614
 615static int SBD_setup_device(struct spectra_nand_dev *dev, int which)
 616{
 617        int res_blks;
 618        u32 sects;
 619
 620        nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n",
 621                       __FILE__, __LINE__, __func__);
 622
 623        memset(dev, 0, sizeof(struct spectra_nand_dev));
 624
 625        nand_dbg_print(NAND_DBG_WARN, "Reserved %d blocks "
 626                "for OS image, %d blocks for bad block replacement.\n",
 627                get_res_blk_num_os(),
 628                get_res_blk_num_bad_blk());
 629
 630        res_blks = get_res_blk_num_bad_blk() + get_res_blk_num_os();
 631
 632        dev->size = (u64)IdentifyDeviceData.PageDataSize *
 633                IdentifyDeviceData.PagesPerBlock *
 634                (IdentifyDeviceData.wDataBlockNum - res_blks);
 635
 636        res_blks_os = get_res_blk_num_os();
 637
 638        spin_lock_init(&dev->qlock);
 639
 640        dev->tmp_buf = kmalloc(IdentifyDeviceData.PageDataSize, GFP_ATOMIC);
 641        if (!dev->tmp_buf) {
 642                printk(KERN_ERR "Failed to kmalloc memory in %s Line %d, exit.\n",
 643                        __FILE__, __LINE__);
 644                goto out_vfree;
 645        }
 646
 647        dev->queue = blk_init_queue(GLOB_SBD_request, &dev->qlock);
 648        if (dev->queue == NULL) {
 649                printk(KERN_ERR
 650                       "Spectra: Request queue could not be initialized."
 651                        " Aborting\n ");
 652                goto out_vfree;
 653        }
 654        dev->queue->queuedata = dev;
 655
 656        /* As Linux block layer doens't support >4KB hardware sector,  */
 657        /* Here we force report 512 byte hardware sector size to Kernel */
 658        blk_queue_logical_block_size(dev->queue, 512);
 659
 660        blk_queue_flush(dev->queue, REQ_FLUSH);
 661
 662        dev->thread = kthread_run(spectra_trans_thread, dev, "nand_thd");
 663        if (IS_ERR(dev->thread)) {
 664                blk_cleanup_queue(dev->queue);
 665                unregister_blkdev(GLOB_SBD_majornum, GLOB_SBD_NAME);
 666                return PTR_ERR(dev->thread);
 667        }
 668
 669        dev->gd = alloc_disk(PARTITIONS);
 670        if (!dev->gd) {
 671                printk(KERN_ERR
 672                       "Spectra: Could not allocate disk. Aborting \n ");
 673                goto out_vfree;
 674        }
 675        dev->gd->major = GLOB_SBD_majornum;
 676        dev->gd->first_minor = which * PARTITIONS;
 677        dev->gd->fops = &GLOB_SBD_ops;
 678        dev->gd->queue = dev->queue;
 679        dev->gd->private_data = dev;
 680        snprintf(dev->gd->disk_name, 32, "%s%c", GLOB_SBD_NAME, which + 'a');
 681
 682        sects = dev->size >> 9;
 683        nand_dbg_print(NAND_DBG_WARN, "Capacity sects: %d\n", sects);
 684        set_capacity(dev->gd, sects);
 685
 686        add_disk(dev->gd);
 687
 688        return 0;
 689out_vfree:
 690        return -ENOMEM;
 691}
 692
 693/*
 694static ssize_t show_nand_block_num(struct device *dev,
 695        struct device_attribute *attr, char *buf)
 696{
 697        return snprintf(buf, PAGE_SIZE, "%d\n",
 698                (int)IdentifyDeviceData.wDataBlockNum);
 699}
 700
 701static ssize_t show_nand_pages_per_block(struct device *dev,
 702        struct device_attribute *attr, char *buf)
 703{
 704        return snprintf(buf, PAGE_SIZE, "%d\n",
 705                (int)IdentifyDeviceData.PagesPerBlock);
 706}
 707
 708static ssize_t show_nand_page_size(struct device *dev,
 709        struct device_attribute *attr, char *buf)
 710{
 711        return snprintf(buf, PAGE_SIZE, "%d\n",
 712                (int)IdentifyDeviceData.PageDataSize);
 713}
 714
 715static DEVICE_ATTR(nand_block_num, 0444, show_nand_block_num, NULL);
 716static DEVICE_ATTR(nand_pages_per_block, 0444, show_nand_pages_per_block, NULL);
 717static DEVICE_ATTR(nand_page_size, 0444, show_nand_page_size, NULL);
 718
 719static void create_sysfs_entry(struct device *dev)
 720{
 721        if (device_create_file(dev, &dev_attr_nand_block_num))
 722                printk(KERN_ERR "Spectra: "
 723                        "failed to create sysfs entry nand_block_num.\n");
 724        if (device_create_file(dev, &dev_attr_nand_pages_per_block))
 725                printk(KERN_ERR "Spectra: "
 726                "failed to create sysfs entry nand_pages_per_block.\n");
 727        if (device_create_file(dev, &dev_attr_nand_page_size))
 728                printk(KERN_ERR "Spectra: "
 729                "failed to create sysfs entry nand_page_size.\n");
 730}
 731*/
 732
 733static void register_spectra_ftl_async(void *unused, async_cookie_t cookie)
 734{
 735        int i;
 736
 737        /* create_sysfs_entry(&dev->dev); */
 738
 739        if (PASS != GLOB_FTL_IdentifyDevice(&IdentifyDeviceData)) {
 740                printk(KERN_ERR "Spectra: Unable to Read Flash Device. "
 741                       "Aborting\n");
 742                return;
 743        } else {
 744                nand_dbg_print(NAND_DBG_WARN, "In GLOB_SBD_init: "
 745                               "Num blocks=%d, pagesperblock=%d, "
 746                               "pagedatasize=%d, ECCBytesPerSector=%d\n",
 747                       (int)IdentifyDeviceData.NumBlocks,
 748                       (int)IdentifyDeviceData.PagesPerBlock,
 749                       (int)IdentifyDeviceData.PageDataSize,
 750                       (int)IdentifyDeviceData.wECCBytesPerSector);
 751        }
 752
 753        printk(KERN_ALERT "Spectra: searching block table, please wait ...\n");
 754        if (GLOB_FTL_Init() != PASS) {
 755                printk(KERN_ERR "Spectra: Unable to Initialize FTL Layer. "
 756                       "Aborting\n");
 757                goto out_ftl_flash_register;
 758        }
 759        printk(KERN_ALERT "Spectra: block table has been found.\n");
 760
 761        GLOB_SBD_majornum = register_blkdev(0, GLOB_SBD_NAME);
 762        if (GLOB_SBD_majornum <= 0) {
 763                printk(KERN_ERR "Unable to get the major %d for Spectra",
 764                       GLOB_SBD_majornum);
 765                goto out_ftl_flash_register;
 766        }
 767
 768        for (i = 0; i < NUM_DEVICES; i++)
 769                if (SBD_setup_device(&nand_device[i], i) == -ENOMEM)
 770                        goto out_blk_register;
 771
 772        nand_dbg_print(NAND_DBG_DEBUG,
 773                       "Spectra: module loaded with major number %d\n",
 774                       GLOB_SBD_majornum);
 775
 776        return;
 777
 778out_blk_register:
 779        unregister_blkdev(GLOB_SBD_majornum, GLOB_SBD_NAME);
 780out_ftl_flash_register:
 781        GLOB_FTL_Cache_Release();
 782        printk(KERN_ERR "Spectra: Module load failed.\n");
 783}
 784
 785int register_spectra_ftl()
 786{
 787        async_schedule(register_spectra_ftl_async, NULL);
 788        return 0;
 789}
 790EXPORT_SYMBOL_GPL(register_spectra_ftl);
 791
 792static int GLOB_SBD_init(void)
 793{
 794        /* Set debug output level (0~3) here. 3 is most verbose */
 795        printk(KERN_ALERT "Spectra: %s\n", GLOB_version);
 796
 797        mutex_init(&spectra_lock);
 798
 799        if (PASS != GLOB_FTL_Flash_Init()) {
 800                printk(KERN_ERR "Spectra: Unable to Initialize Flash Device. "
 801                       "Aborting\n");
 802                return -ENODEV;
 803        }
 804        return 0;
 805}
 806
 807static void __exit GLOB_SBD_exit(void)
 808{
 809        int i;
 810
 811        nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n",
 812                       __FILE__, __LINE__, __func__);
 813
 814        for (i = 0; i < NUM_DEVICES; i++) {
 815                struct spectra_nand_dev *dev = &nand_device[i];
 816                if (dev->gd) {
 817                        del_gendisk(dev->gd);
 818                        put_disk(dev->gd);
 819                }
 820                if (dev->queue)
 821                        blk_cleanup_queue(dev->queue);
 822                kfree(dev->tmp_buf);
 823        }
 824
 825        unregister_blkdev(GLOB_SBD_majornum, GLOB_SBD_NAME);
 826
 827        mutex_lock(&spectra_lock);
 828        force_flush_cache();
 829        mutex_unlock(&spectra_lock);
 830
 831        GLOB_FTL_Cache_Release();
 832
 833        GLOB_FTL_Flash_Release();
 834
 835        nand_dbg_print(NAND_DBG_DEBUG,
 836                       "Spectra FTL module (major number %d) unloaded.\n",
 837                       GLOB_SBD_majornum);
 838}
 839
 840module_init(GLOB_SBD_init);
 841module_exit(GLOB_SBD_exit);
 842