• nuc900 nand flash mtd 驱动


    nuc900 nand flash mtd 驱动,请参考!

    /*
     * Copyright © 2009 Nuvoton technology corporation.
     *
     * Wan ZongShun <mcuos.com@gmail.com>
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation;version 2 of the License.
     *
     */
    
    #include <linux/slab.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/interrupt.h>
    #include <linux/io.h>
    #include <linux/platform_device.h>
    #include <linux/delay.h>
    #include <linux/clk.h>
    #include <linux/err.h>
    #include <linux/blkdev.h>
    
    #include <linux/mtd/mtd.h>
    #include <linux/mtd/nand.h>
    #include <linux/mtd/partitions.h>
    #include <linux/dma-mapping.h>
    
    
    #include <mach/map.h>
    #include <mach/regs-clock.h>
    #include <mach/regs-fmi.h>
    #include <mach/gnand/GNAND.h>
    #include <mach/nuc900_nand.h>
    #define REG_MFSEL    (W90X900_VA_GCR + 0xC)
    /*
    #define REG_FMICSR       0x00
    #define REG_SMCSR        0xa0
    #define REG_SMISR        0xac
    #define REG_SMCMD        0xb0
    #define REG_SMADDR       0xb4
    #define REG_SMDATA       0xb8
    */
    #define RESET_FMI    0x01
    #define NAND_EN        0x08
    #define READYBUSY    (0x01 << 18)
    
    #define SWRST        0x01
    #define PSIZE        (0x01 << 3)
    #define DMARWEN        (0x03 << 1)
    #define BUSWID        (0x01 << 4)
    #define ECC4EN        (0x01 << 5)
    #define WP        (0x01 << 24)
    #define NANDCS        (0x03 << 25)
    #define ENDADDR        (0x01 << 31)
    
    #define NANDCS1        (0x01 << 25)
    #define ECCBYTE512 3
    #define ECCBYTE2K  3
    
    /* DMAC Control and Status Register (DMACCSR) */
    #define DMACCSR_DMACEN        (1)
    #define DMACCSR_SW_RST        (1<<1)
    #define DMACCSR_SG_EN1        (1<<2)
    #define DMACCSR_SG_EN2        (1<<3)
    #define DMACCSR_ATA_BUSY    (1<<8)
    #define DMACCSR_FMI_BUSY    (1<<9)
    
    /******************************************/
    #define FMI_ERR_ID    0xFFFF0100
    
    #define FMI_TIMEOUT                (FMI_ERR_ID|0x01)
    /* NAND error */
    #define FMI_SM_INIT_ERROR        (FMI_ERR_ID|0x20)
    #define FMI_SM_RB_ERR            (FMI_ERR_ID|0x21)
    #define FMI_SM_STATE_ERROR        (FMI_ERR_ID|0x22)
    #define FMI_SM_ECC_ERROR        (FMI_ERR_ID|0x23)
    #define FMI_SM_STATUS_ERR        (FMI_ERR_ID|0x24)
    #define FMI_SM_ID_ERR            (FMI_ERR_ID|0x25)
    #define FMI_SM_INVALID_BLOCK    (FMI_ERR_ID|0x26)
    
    
    bool volatile  _fmi_bIsSMDataReady=0;
    //#define DEBUG_NAND
    //#define nuc900_nand_debug(fmt,args...) printk(fmt,##args)
    #define nuc900_nand_debug(fmt,args...) 
    
    
    #define read_data_reg(dev)        
        __raw_readl(REG_SMDATA)
    
    #define write_data_reg(dev, val)    
        __raw_writel((val), REG_SMDATA)
    
    #define write_cmd_reg(dev, val)        
        __raw_writel((val), REG_SMCMD)
    
    #define write_addr_reg(dev, val)    
        __raw_writel((val), REG_SMADDR)
    #define nuc900_nand_read(reg)        __raw_readl(reg)
    
    struct nuc900_nand {
        struct mtd_info mtd;
        struct nand_chip chip;
        void __iomem *reg;
        struct clk *clk;
        spinlock_t lock;
    };
    extern struct semaphore  fmi_sem;
    extern struct semaphore dmac_sem;
    
    
    #define NUM_PARTITIONS 4
    
    /*请保证分区的总和数和nandflash的实际大小一致,目前开发板上的nandflash是128M*/
    #define UBOOT_SIZE     SZ_1M*1
    #define KERNEL_SIZE     SZ_1M*5
    #define ROOT_SIZE        SZ_1M*44
    #define USER_SIZE     SZ_1M*78
    
    /*vitual addr and phy addr for dma */
    static unsigned char * nand_vaddr = NULL;
    static unsigned char * nand_phyaddr = NULL;
    
    static const struct mtd_partition partitions[] = {
        { .name = "U-boot",
          .offset = 0,
          .size = UBOOT_SIZE
         },
        { .name = "linux 2.6.35 kernel",
          .offset = UBOOT_SIZE,
          .size = KERNEL_SIZE
        },
        { .name = "root",
           .offset = UBOOT_SIZE+KERNEL_SIZE,
          .size = ROOT_SIZE
        },
        { .name = "user",
          .offset = UBOOT_SIZE+KERNEL_SIZE+ROOT_SIZE,
          .size = USER_SIZE
        }
    };
    
    static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd)
    {
        unsigned char ret;
        struct nuc900_nand *nand;
    
        nand = container_of(mtd, struct nuc900_nand, mtd);
      __raw_writel( NAND_EN,  REG_FMICSR);
        ret = (unsigned char)read_data_reg(nand);
    
        return ret;
    }
    
    static void nuc900_nand_read_buf(struct mtd_info *mtd,
                     unsigned char *buf, int len)
    {
        int i;
        struct nand_chip *chip = mtd->priv;
        //nuc900_nand_debug("nuc900_nand_read_buf: len=%d
    ",len);
        __raw_writel( NAND_EN,  REG_FMICSR);
        if(len==mtd->oobsize){// read oob data
            chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
          if(down_interruptible(&fmi_sem)) //jhe+ 2010.12.21
          {
              printk("nuc900 mtd nand driver read buf sem error
    ");
              return;
          }
          for (i = 0; i < len; i++)
               buf[i] = __raw_readl(REG_SMDATA)& 0xff;
               
    #ifdef DEBUG_NAND
          nuc900_nand_debug("oob read
    ");
          for (i = 0; i < len; i++)
               nuc900_nand_debug(" 0x%02x |",buf[i]);
          nuc900_nand_debug("
    ");    
    #endif      
          
            goto readout1;
        } 
        if(down_interruptible(&fmi_sem)) //jhe+ 2010.12.21
      {
              printk("nuc900 mtd nand driver read buf sem error
    ");
              return;
        }
        
        //normal page read use dma
        if (down_interruptible(&dmac_sem))
          return ;
        while (__raw_readl(REG_DMACCSR)&DMACCSR_FMI_BUSY); //Wait IP finished... for safe
        __raw_writel((unsigned long)nand_phyaddr,REG_DMACSAR2);
        _fmi_bIsSMDataReady = 0;
        __raw_writel(__raw_readl(REG_SMCSR) | 0x02,REG_SMCSR); //enable DMA read
        while (!_fmi_bIsSMDataReady);
      up(&dmac_sem);
    
        memcpy(buf,nand_vaddr,len);
        
    readout1:
        up(&fmi_sem);    
    }
    
    static void nuc900_nand_write_buf(struct mtd_info *mtd,
                      const unsigned char *buf, int len)
    {
        int i;
      struct nand_chip *chip = mtd->priv;
        int length = mtd->oobsize;
        int dmanum = 0;
    
        //nuc900_nand_debug("nuc900nand write buf:len=%d
    ",len);
      __raw_writel( NAND_EN,  REG_FMICSR);
        if(len==length){//  write for oob 
            chip->cmdfunc(mtd, NAND_CMD_RNDIN, mtd->writesize, -1);
            if(down_interruptible(&fmi_sem)) //jhe+ 2010.12.21
          {
               printk("nuc900 mtd nand driver write buf sem error
    ");
               return;
          }
            #ifdef DEBUG_NAND
            nuc900_nand_debug("oobdata:len=%d
    ",len);
          for (i = 0; i < len; i++)
               nuc900_nand_debug(" 0x%02x |",buf[i]);
          nuc900_nand_debug("
    ");    
          #endif
          
            i=0;
            while(i<len){
                __raw_writel(buf[i],REG_SMDATA);
                i=i+1;
            }
            
        goto write_out;
        }
        
          if(down_interruptible(&fmi_sem)) //jhe+ 2010.12.21
          {
               printk("nuc900 mtd nand driver write buf sem error
    ");
               return;
          }
          //normal page write use dma
            while(dmanum < len)//give the first 512 to the dma space
            {
                nand_vaddr[dmanum] = buf[dmanum];
                dmanum++;
             }    
                 
             while(dmanum < 2112)
             {
                  nand_vaddr[dmanum] = 0xff;
                  dmanum++;
             }
             #if 0
             nuc900_nand_debug("
    ");
           nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_0));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_1));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_2));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_3));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_4));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_5));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_6));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_7));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_8));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_9));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_10));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_11));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_12));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_13));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_14));
          nuc900_nand_debug(" 0x%02x |",__raw_readl(REG_SMRA_15));
             nuc900_nand_debug("
    ");
             #endif 
             if (down_interruptible(&dmac_sem))
                return ;
            //normal page write use dma
          while (__raw_readl(REG_DMACCSR)&DMACCSR_FMI_BUSY); //Wait IP finished... for safe
          __raw_writel((unsigned long)nand_phyaddr,REG_DMACSAR2);
          _fmi_bIsSMDataReady = 0;
            __raw_writel(__raw_readl(REG_SMCSR) | 0x04,REG_SMCSR); //enable DMA write
            while (!_fmi_bIsSMDataReady);//wait for dma finished
          //__raw_writel(__raw_readl(REG_SMISR)|0x01,REG_SMISR);  //clear DMA finished flag
         // __raw_writel(0x11223344,REG_SMRA_15);
         up(&dmac_sem);
    write_out:
         up(&fmi_sem);
         return;    
    }
    
    /* select chip */
    static void nuc900_nand_select_chip(struct mtd_info *mtd, int chipnr)
    {
      struct nuc900_nand *nand;
        nand = container_of(mtd, struct nuc900_nand, mtd);
            
        if(down_interruptible(&fmi_sem)) //jhe+ 2010.12.21
        {
            printk("nuc900 mtd nand driver select_chip sem error
    ");
            return ;
        }
      __raw_writel( NAND_EN,  REG_FMICSR);
        switch (chipnr) {
        case -1://no chip selected 
            __raw_writel(__raw_readl(REG_SMCSR) | NANDCS,
                       REG_SMCSR);
            break;
        case 0://select nand chip 0
             __raw_writel(__raw_readl(REG_SMCSR) & ~NANDCS,
                       REG_SMCSR);
            break;
        case 1://select nand chip 1
             __raw_writel((__raw_readl(REG_SMCSR) & (~NANDCS))| NANDCS1,
                       REG_SMCSR);
            break;
        default:
            BUG();
        }
      
      up(&fmi_sem);
    
    }
    static int nuc900_check_rb(struct nuc900_nand *nand)
    {
        unsigned int val;
        spin_lock(&nand->lock);
        __raw_writel( NAND_EN,  REG_FMICSR);
        val = __raw_readl(REG_SMISR );
        val &= READYBUSY;
        spin_unlock(&nand->lock);
    
        return val;
    }
    
    static int nuc900_nand_devready(struct mtd_info *mtd)
    {
        struct nuc900_nand *nand;
        int ready;
    
        nand = container_of(mtd, struct nuc900_nand, mtd);
      __raw_writel( NAND_EN,  REG_FMICSR);
        ready = (nuc900_check_rb(nand)) ? 1 : 0;
        return ready;
    }
    /* functions */
    int fmiSMCheckRB(void)
    {
            __raw_writel( NAND_EN,  REG_FMICSR);
            while (1) {
                    if (__raw_readl(REG_SMISR) & 0x400) {
                            __raw_writel(0x400,REG_SMISR);
                            return 1;
                    }
            }
            return 0;
    }
    static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command,
                       int column, int page_addr)
    {
        register struct nand_chip *chip = mtd->priv;
        struct nuc900_nand *nand;
      //nuc900_nand_debug("command=0x%x,column=0x%x,page_addr=0x%x
    ",command,column,page_addr);
        nand = container_of(mtd, struct nuc900_nand, mtd);
        // enable SM
      __raw_writel( NAND_EN,  REG_FMICSR);
    
        if (command == NAND_CMD_READOOB) {
            column += mtd->writesize;
            command = NAND_CMD_READ0;
        }
        if((command == NAND_CMD_READ0)||(command ==NAND_CMD_SEQIN)){
            //nuc900_nand_debug("clear R/B flag before cmd
    ");
                    /* clear R/B flag */
        while (!(__raw_readl(REG_SMISR) & 0x40000));
        __raw_writel(0x400, REG_SMISR);
        }
    
        write_cmd_reg(nand, command & 0xff);
    
        if (column != -1 || page_addr != -1) {
    
            if (column != -1) {
                if (chip->options & NAND_BUSWIDTH_16)
                    column >>= 1;
                write_addr_reg(nand, column);
                write_addr_reg(nand, column >> 8 | ENDADDR);
            }
            if (page_addr != -1) {
                write_addr_reg(nand, page_addr);
    
                if (chip->chipsize > (128 << 20)) {
                    write_addr_reg(nand, page_addr >> 8);
                    write_addr_reg(nand, page_addr >> 16 | ENDADDR);
                } else {
                    write_addr_reg(nand, page_addr >> 8 | ENDADDR);
                }
            }
        }
    
        switch (command) {
        case NAND_CMD_CACHEDPROG:
        case NAND_CMD_ERASE1:
        case NAND_CMD_ERASE2:
        case NAND_CMD_SEQIN:
        case NAND_CMD_RNDIN:
        
        case NAND_CMD_DEPLETE1:
            //nuc900_nand_debug("command=0x%x
    ",command);
            return;
      case NAND_CMD_PAGEPROG:
          fmiSMCheckRB();
          return;
      case NAND_CMD_STATUS:
        if (__raw_readl(REG_SMDATA) & 0x01) {    // 1:fail; 0:pass
           // printk("Nand Status  error!!
    ");
            return;
        }      
        return;
        case NAND_CMD_STATUS_ERROR:
        case NAND_CMD_STATUS_ERROR0:
        case NAND_CMD_STATUS_ERROR1:
        case NAND_CMD_STATUS_ERROR2:
        case NAND_CMD_STATUS_ERROR3:
            udelay(chip->chip_delay);
            return;
    
        case NAND_CMD_RESET:
            if (chip->dev_ready)
                break;
            udelay(chip->chip_delay);
    
            write_cmd_reg(nand, NAND_CMD_STATUS);
            write_cmd_reg(nand, command);
    
            while (!nuc900_check_rb(nand))
                ;
    
            return;
    
        case NAND_CMD_RNDOUT:
            write_cmd_reg(nand, NAND_CMD_RNDOUTSTART);
            
            return;
    
        case NAND_CMD_READ0:
    
            write_cmd_reg(nand, NAND_CMD_READSTART);
            fmiSMCheckRB();
            break;
            
        default:
    
            if (!chip->dev_ready) {
                udelay(chip->chip_delay);
                return;
            }
        }
    
        /* Apply this short delay always to ensure that we do wait tWB in
         * any case on any machine. */
        //ndelay(100);
    
        while (!chip->dev_ready(mtd))
            ;
    }
    
    // SM functions
    int fmiSM_Reset(void)
    {
    
            u32 volatile i;
            __raw_writel( NAND_EN,  REG_FMICSR);
            __raw_writel(0xff,REG_SMCMD);
            for (i=100; i>0; i--);
    
            if (!fmiSMCheckRB())
                    return -1;
            return 0;
    }
    
    static void nuc900_nand_enable(struct nuc900_nand *nand)
    {
            
        unsigned int val;
    
        if(down_interruptible(&fmi_sem)) //jhe+ 2010.12.21
        {
            printk("nuc900 mtd nand driver nand_enable sem error
    ");
            return;
        }
        spin_lock(&nand->lock);
      
    
        // enable SM
        __raw_writel( 0x3050b, REG_SMTCR );//set timer control
        __raw_writel( NAND_EN,  REG_FMICSR);
        
        /* init SM interface */
        __raw_writel((__raw_readl(REG_SMCSR)&0xf8ffffc0), REG_SMCSR);    // disable ecc4
        fmiSM_Reset();
        __raw_writel((__raw_readl(REG_SMCSR)&0xfffffff0)|0x01000008, REG_SMCSR);    // psize:2048; wp# set 1
    
        //
        __raw_writel(0x01, REG_SMIER);//enable dma interrupter
        spin_unlock(&nand->lock);
        up(&fmi_sem);
    }
    
    static irqreturn_t fmi_interrupt(int irq, void *devid)
    {
            unsigned int volatile isr;
            // SM interrupt status
            isr = __raw_readl(REG_SMISR);
            ///printk("fmi_interrupt
    ");
            //DMA read/write transfer is done
            if (isr & 0x01) {
                    _fmi_bIsSMDataReady = 1;
                    __raw_writel(0x01,REG_SMISR);
                    return IRQ_HANDLED;
            } else {
                    return IRQ_NONE;
            }
    
    }
    
    
    static int __devinit nuc900_nand_probe(struct platform_device *pdev)
    {
        struct nuc900_nand *nuc900_nand;
        struct mtd_info *mtd;
        struct nand_chip *chip;
        int retval;
        struct resource *res;
      nuc900_nand_debug("nuc900_nand_probe in
    ");
      
      nand_vaddr = (unsigned char *) dma_alloc_coherent(NULL,2112, (dma_addr_t *) &nand_phyaddr, GFP_KERNEL);
      if(nand_vaddr == NULL){
          printk(KERN_ERR "NUC900_nand: failed to allocate ram for nand data.
    ");
            return -ENOMEM;
      } 
      
        retval = 0;
      /* Allocate memory for the device structure (and zero it) */
        nuc900_nand = kzalloc(sizeof(struct nuc900_nand), GFP_KERNEL);
        if (!nuc900_nand){
            printk(KERN_ERR "NUC900_nand: failed to allocate device structure.
    ");
            return -ENOMEM;
        }
        mtd=&nuc900_nand->mtd;
        chip = &(nuc900_nand->chip);
        
        chip->priv = nuc900_nand;        /* link the private data structures */
        mtd->priv    = chip;
        mtd->owner    = THIS_MODULE;
        spin_lock_init(&nuc900_nand->lock);
        
        
        __raw_writel(__raw_readl(REG_CLKEN) | 0x30,REG_CLKEN);
        __raw_writel(((__raw_readl(REG_MFSEL) & 0xFFFFFFF3) | 0x00000004),REG_MFSEL); /* select NAND function pins */
        
        if (request_irq(20, fmi_interrupt, IRQF_SHARED, "900_NAND", &pdev->dev)) {
                    printk("NAND: Request IRQ error
    ");
                    retval = -ENOENT;
                        goto fail1;
      }
      if (down_interruptible(&dmac_sem)){
          retval = -ENOENT;
              goto fail1;
        }
        while (__raw_readl(REG_DMACCSR)&DMACCSR_FMI_BUSY); //Wait IP finished... for safe
            // DMAC Initial
      __raw_writel(0x00000003, REG_DMACCSR);
            //Enable DMAC
      __raw_writel(0x00000001, REG_DMACCSR);
            // Enable target abort interrupt generation during DMA transfer.
      __raw_writel(0x00000001, REG_DMACIER);
      up(&dmac_sem);
    
                /* init SM interface */
      __raw_writel((__raw_readl(REG_SMCSR)&0xf8ffffc0),REG_SMCSR);    // disable ecc4
      if (down_interruptible(&dmac_sem)){
          retval = -ENOENT;
              goto fail1;
        }
      while (__raw_readl(REG_DMACCSR)&DMACCSR_FMI_BUSY); //Wait IP finished... for safe
    
       // enable all
       __raw_writel(__raw_readl(REG_DMACCSR) | DMACCSR_DMACEN, REG_DMACCSR); //enable DMAC for FMI
    
       /* enable all interrupt */
       __raw_writel(DMACIER_TABORT_IE, REG_DMACIER); //Enable target abort interrupt generation during DMA transfer
       __raw_writel(FMIIER_DTA_IE, REG_FMIIER); //Enable DMAC READ/WRITE target abort interrupt generation
       up(&dmac_sem);
        
        chip->cmdfunc        = nuc900_nand_command_lp;
        chip->dev_ready        = nuc900_nand_devready;
        chip->read_byte        = nuc900_nand_read_byte;
        chip->write_buf        = nuc900_nand_write_buf;
        chip->read_buf        = nuc900_nand_read_buf;
        chip->select_chip  = nuc900_nand_select_chip;
        chip->chip_delay    = 50;
        chip->options        = 0;
        chip->ecc.mode        = NAND_ECC_SOFT;
        chip->ecc.size = 256;
        chip->ecc.bytes = 3;
    
      platform_set_drvdata(pdev, nuc900_nand);
      
        nuc900_nand_enable(nuc900_nand);
        nuc900_nand_debug("REG_SMCSR=0x%x,REG_SMIER=0x%x,SMTCR=0x%x
    ",__raw_readl(REG_SMCSR),__raw_readl(REG_SMIER),__raw_readl(REG_SMTCR));
    
        
        /* first scan to find the device and get the page size */
        if (nand_scan_ident(mtd, 1, NULL)) {
            retval = -ENXIO;
            goto err_scan_ident;
        }
        
        
        //chip->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
            /* ECC is calculated for the whole page (1 step) */
          //chip->ecc.size = mtd->writesize;
                /* set ECC page size */
            switch (mtd->writesize) {
            case 512:
                __raw_writel( __raw_readl(REG_SMCSR) & ( ~PSIZE ), REG_SMCSR );    // psize:512; wp# set 1
                //chip->ecc.bytes =ECCBYTE512;
                break;
            case 2048:
                __raw_writel( __raw_readl(REG_SMCSR)|PSIZE , REG_SMCSR );    // psize:2048; wp# set 1
                //chip->ecc.bytes =ECCBYTE2K;
                break;
            default:
                /* page size not handled by HW ECC */
                /* switching back to soft ECC */
                chip->ecc.mode = NAND_ECC_SOFT;
                chip->ecc.calculate = NULL;
                chip->ecc.correct = NULL;
                chip->ecc.hwctl = NULL;
                chip->ecc.read_page = NULL;
                chip->ecc.postpad = 0;
                chip->ecc.prepad = 0;
                chip->ecc.bytes = 0;
                break;
            }
        
        /* second phase scan */
        if (nand_scan_tail(mtd)) {
            
            dma_free_coherent(NULL, 2112, nand_vaddr, (dma_addr_t )nand_phyaddr);
            retval = -ENXIO;
            goto fail3;
        }
    
        add_mtd_partitions(mtd, partitions,
                            ARRAY_SIZE(partitions));
    
        return retval;
    
    fail3:    
        nuc900_nand_debug("nuc900_nand_probe fail3
    ");
        //iounmap(nuc900_nand->reg);
    err_scan_ident:    
        platform_set_drvdata(pdev, NULL);
    fail2:
        //nuc900_nand_debug("nuc900_nand_probe fail2
    ");    
        //release_mem_region(res->start, resource_size(res));
    fail1:
        nuc900_nand_debug("nuc900_nand_probe fail1
    ");
        kfree(nuc900_nand);
        return retval;
    }
    
    static int __devexit nuc900_nand_remove(struct platform_device *pdev)
    {
        struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev);
        //struct resource *res;
        struct mtd_info *mtd = &nuc900_nand->mtd;
    
        nand_release(mtd);
        //iounmap(nuc900_nand->reg);
        //res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        //release_mem_region(res->start, resource_size(res));
    
        //clk_disable(nuc900_nand->clk);
        //clk_put(nuc900_nand->clk);
      free_irq(IRQ_FMI, NULL);
        kfree(nuc900_nand);
        /* Free dma space */
        dma_free_coherent(NULL, 2112, nand_vaddr, (dma_addr_t )nand_phyaddr);
    
        platform_set_drvdata(pdev, NULL);
    
        return 0;
    }
    
    static struct platform_driver nuc900_nand_driver = {
        .probe        = nuc900_nand_probe,
        .remove        = __devexit_p(nuc900_nand_remove),
        .driver        = {
            .name    = "nuc900-nand",
            .owner    = THIS_MODULE,
        },
    };
    
    static int __init nuc900_nand_init(void)
    {
        return platform_driver_register(&nuc900_nand_driver);
    }
    
    static void __exit nuc900_nand_exit(void)
    {
        platform_driver_unregister(&nuc900_nand_driver);
    }
    
    module_init(nuc900_nand_init);
    module_exit(nuc900_nand_exit);
    
    MODULE_AUTHOR("Tanshi Li <dolphin96011@gmail.com>");
    MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!");
    MODULE_LICENSE("GPL");
    MODULE_ALIAS("platform:nuc900-nand");
  • 相关阅读:
    腾讯时尚网页精彩专题设计赞赏
    IOS日期转为今天昨天形式
    从12306验证码看人工智能未来发展
    智力题小结(4)
    andriod first app-computer
    java中执行js代码
    Eclipse构建Activiti项目,类导入提示功能不能使用
    在eclipse中安装activiti插件
    windows下多进程加协程并发模式
    python与c#的交互模块pythonnet
  • 原文地址:https://www.cnblogs.com/chuncky/p/4312767.html
Copyright © 2020-2023  润新知