dm9000 mutex with nandc
author刘益星 <lyx@rock-chips.com>
Fri, 28 May 2010 08:56:59 +0000 (08:56 +0000)
committer黄涛 <huangtao@rock-chips.com>
Mon, 21 Jun 2010 05:35:18 +0000 (13:35 +0800)
drivers/mtd/nand/rk2818_nand.c
drivers/net/dm9000.c

index af3cefda8a960d4366098afea7e8f9045946ca82..19e65f0a77bdebdea333c0eccc933953fddf4f52 100644 (file)
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/cpufreq.h>
 #include <linux/err.h>
 #include <linux/io.h>
 
 #include <mach/rk2818_nand.h>
+#include <mach/rk2818_iomap.h>
+#include <mach/iomux.h>
 
 #define PROGRAM_BUSY_COUNT   10000
 #define ERASE_BUSY_COUNT           20000
 /* Define delays in microsec for NAND device operations */
 #define TROP_US_DELAY   2000
 
+static struct mutex  rknand_mutex;
+//static  spinlock_t   rknand_lock;
+
+
 struct rk2818_nand_mtd {
        struct mtd_info         mtd;
        struct nand_chip                nand;
@@ -45,9 +52,15 @@ struct rk2818_nand_mtd {
        struct device           *dev;
        const struct rk2818_nand_flash *flash_info;
 
-       struct clk              *clk;
-       void __iomem            *regs;
-       uint16_t                        col_addr;       
+       struct clk                      *clk;
+       unsigned long                   clk_rate;
+       void __iomem                    *regs;
+       int                                     cs;                     // support muliple nand chip,record current chip select
+       u_char                          accesstime;
+#ifdef CONFIG_CPU_FREQ
+       struct notifier_block   freq_transition;
+#endif
+
 };
 
 /* OOB placement block for use with software ecc generation */
@@ -142,8 +155,17 @@ static void rk2818_nand_select_chip(struct mtd_info *mtd, int chip)
        struct nand_chip *nand_chip = mtd->priv;
        struct rk2818_nand_mtd *master = nand_chip->priv;
        pNANDC pRK28NC=  (pNANDC)(master->regs);
+
+                  
+       if( chip<0 )
+            pRK28NC->FMCTL &=0xffffff00;   // release chip select
+       else
+         {
+              master->cs = chip;
+               pRK28NC->FMCTL &=0xffffff00;
+               pRK28NC ->FMCTL |= 0x1<<chip;  // select chip
+         }
        
-       pRK28NC ->FMCTL |= 0x1<<chip;
 }
 
 /*
@@ -156,8 +178,9 @@ static u_char rk2818_nand_read_byte(struct mtd_info *mtd)
        pNANDC pRK28NC=  (pNANDC)(master->regs);
        
        u_char ret = 0; 
-       pRK28NC->FLCTL &= ~FL_BYPASS;  // bypass mode
-       ret = (u_char)(pRK28NC ->chip[0].data);
+         
+       ret = (u_char)(pRK28NC ->chip[master->cs].data);
+
        return ret;
 }
 
@@ -169,17 +192,16 @@ static u16 rk2818_nand_read_word(struct mtd_info *mtd)
        struct nand_chip *nand_chip = mtd->priv;
        struct rk2818_nand_mtd *master = nand_chip->priv;
        pNANDC pRK28NC=  (pNANDC)(master->regs);
+
        
        u_char tmp1 = 0,tmp2=0;
        u16 ret=0;
-
-       pRK28NC->FLCTL &= ~FL_BYPASS;  // bypass mode
-         
-       tmp1 = (u_char)(pRK28NC ->chip[0].data);
-       tmp2 = (u_char)(pRK28NC ->chip[0].data);
+       
+       tmp1 = (u_char)(pRK28NC ->chip[master->cs].data);
+       tmp2 = (u_char)(pRK28NC ->chip[master->cs].data);
 
        ret =   (tmp2 <<8)|tmp1;
-         
+       
        return ret;
 }
 
@@ -188,9 +210,16 @@ static void rk2818_nand_read_buf(struct mtd_info *mtd, u_char* const buf, int le
        struct nand_chip *nand_chip = mtd->priv;
        struct rk2818_nand_mtd *master = nand_chip->priv;
        pNANDC pRK28NC=  (pNANDC)(master->regs);
-       uint32_t  i;
+       uint32_t  i, chipnr;
+        
+       mutex_lock(&rknand_mutex);
+
+       chipnr = master->cs ;
+          
+       rk2818_nand_select_chip(mtd,chipnr);
+       
+       
        
-   
        if ( len < mtd->writesize )   // read oob
        {
                pRK28NC ->BCHCTL = BCH_RST;
@@ -211,6 +240,13 @@ static void rk2818_nand_read_buf(struct mtd_info *mtd, u_char* const buf, int le
                memcpy(buf+i*0x400,(u_char *)(pRK28NC->buf),0x400);  //  only use nandc sram0
                }
        }
+
+       
+       
+       rk2818_nand_select_chip(mtd,-1);
+
+       mutex_unlock(&rknand_mutex);
+
        
        return; 
 
@@ -222,9 +258,18 @@ static void rk2818_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int l
        struct rk2818_nand_mtd *master = nand_chip->priv;
        pNANDC pRK28NC=  (pNANDC)(master->regs);
      
-       uint32_t  i = 0;
+       uint32_t  i = 0, chipnr;
+          
+        
+        mutex_lock(&rknand_mutex);
+
+        chipnr = master->cs ;
 
+       rk2818_nand_select_chip(mtd,chipnr);
+       
          pRK28NC->FLCTL |= FL_BYPASS;  // dma mode
+         
+          
          for(i=0;i<mtd->writesize/0x400;i++)
            {
               memcpy((u_char *)(pRK28NC->buf),buf+i*0x400,0x400);  //  only use nandc sram0    
@@ -232,6 +277,13 @@ static void rk2818_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int l
                pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|0x1<<5|FL_RDN|FL_BYPASS|FL_START;
                wait_op_done(mtd,TROP_US_DELAY,0);      
            }
+       
+
+         
+       rk2818_nand_select_chip(mtd,-1);
+         
+         mutex_unlock(&rknand_mutex);
+
 
 }
 
@@ -244,12 +296,13 @@ static void rk2818_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int colum
 
        uint32_t timeout = 1000;
        char status,ret;
+
        
        switch (command) {
 
        case NAND_CMD_READID:
-               pRK28NC ->chip[0].cmd = command;
-               pRK28NC ->chip[0].addr = 0x0;
+               pRK28NC ->chip[master->cs].cmd = command;
+               pRK28NC ->chip[master->cs].addr = 0x0;
                while (timeout>0)
                {
                  timeout --;
@@ -262,18 +315,18 @@ static void rk2818_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int colum
                break;
                
        case NAND_CMD_READ0:
-              pRK28NC ->chip[0].cmd = command;
+              pRK28NC ->chip[master->cs].cmd = command;
               if ( column>= 0 )
                 {
-                   pRK28NC ->chip[0].addr = column & 0xff;     
+                   pRK28NC ->chip[master->cs].addr = column & 0xff;    
                    if( mtd->writesize > 512) 
-                        pRK28NC ->chip[0].addr = (column >> 8) & 0xff;
+                        pRK28NC ->chip[master->cs].addr = (column >> 8) & 0xff;
                 }
                if ( page_addr>=0 )
                   {
-                       pRK28NC ->chip[0].addr = page_addr & 0xff;
-                       pRK28NC ->chip[0].addr = (page_addr >> 8) & 0xFF;
-                       pRK28NC ->chip[0].addr = (page_addr >> 16) & 0xff;
+                       pRK28NC ->chip[master->cs].addr = page_addr & 0xff;
+                       pRK28NC ->chip[master->cs].addr = (page_addr >> 8) & 0xFF;
+                       pRK28NC ->chip[master->cs].addr = (page_addr >> 16) & 0xff;
                   }
                if( mtd->writesize > 512)
                    pRK28NC ->chip[0].cmd = NAND_CMD_READSTART;
@@ -283,7 +336,7 @@ static void rk2818_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int colum
                break;
                
        case NAND_CMD_READ1:
-              pRK28NC ->chip[0].cmd = command;
+              pRK28NC ->chip[master->cs].cmd = command;
                break;
                
        case NAND_CMD_READOOB:
@@ -291,26 +344,26 @@ static void rk2818_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int colum
                if( mtd->writesize > 512 )
                        command = NAND_CMD_READ0;  // È«²¿¶Á£¬°üÀ¨¶Áoob
                
-               pRK28NC ->chip[0].cmd = command;  
+               pRK28NC ->chip[master->cs].cmd = command;  
 
               if ( mtd->writesize >512 )
                {
                        if ( column>= 0 )
                         {
-                          pRK28NC ->chip[0].addr = (column + mtd->writesize) & 0xff;   
-                            pRK28NC ->chip[0].addr = ( (column + mtd->writesize)  >> 8) & 0xff;
+                          pRK28NC ->chip[master->cs].addr = (column + mtd->writesize) & 0xff;  
+                            pRK28NC ->chip[master->cs].addr = ( (column + mtd->writesize)  >> 8) & 0xff;
                         }
                        if ( page_addr>=0 )
                           {
-                               pRK28NC ->chip[0].addr = page_addr & 0xff;
-                               pRK28NC ->chip[0].addr = (page_addr >> 8) & 0xFF;
-                               pRK28NC ->chip[0].addr = (page_addr >> 16) & 0xff;
+                               pRK28NC ->chip[master->cs].addr = page_addr & 0xff;
+                               pRK28NC ->chip[master->cs].addr = (page_addr >> 8) & 0xFF;
+                               pRK28NC ->chip[master->cs].addr = (page_addr >> 16) & 0xff;
                           }
-                       pRK28NC ->chip[0].cmd = NAND_CMD_READSTART;
+                       pRK28NC ->chip[master->cs].cmd = NAND_CMD_READSTART;
               }
                else
                {
-                  pRK28NC ->chip[0].addr = column;
+                  pRK28NC ->chip[master->cs].addr = column;
                }
                        
                rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT);
@@ -320,11 +373,11 @@ static void rk2818_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int colum
                
        case NAND_CMD_PAGEPROG:
                pRK28NC ->FMCTL |= FMC_WP;  //½â³ýд±£»¤
-               pRK28NC ->chip[0].cmd = command;
+               pRK28NC ->chip[master->cs].cmd = command;
                rk2818_nand_wait_busy(mtd,PROGRAM_BUSY_COUNT);
                
-               pRK28NC ->chip[0].cmd  = NAND_CMD_STATUS;
-               status = pRK28NC ->chip[0].data;
+               pRK28NC ->chip[master->cs].cmd  = NAND_CMD_STATUS;
+               status = pRK28NC ->chip[master->cs].data;
                
                if(status&0x1)
                        ret = -1;
@@ -336,21 +389,21 @@ static void rk2818_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int colum
        case NAND_CMD_ERASE1:
                pRK28NC ->FMCTL |= FMC_WP;  //½â³ýд±£»¤
                pRK28NC ->BCHCTL = 0x0;
-               pRK28NC ->chip[0].cmd  = command;
+               pRK28NC ->chip[master->cs].cmd  = command;
                if ( page_addr>=0 )
                   {
-                       pRK28NC ->chip[0].addr = page_addr & 0xff;
-                       pRK28NC ->chip[0].addr = (page_addr>>8)&0xff;
-                       pRK28NC ->chip[0].addr = (page_addr>>16)&0xff;
+                       pRK28NC ->chip[master->cs].addr = page_addr & 0xff;
+                       pRK28NC ->chip[master->cs].addr = (page_addr>>8)&0xff;
+                       pRK28NC ->chip[master->cs].addr = (page_addr>>16)&0xff;
                   }  
                break;
                
        case NAND_CMD_ERASE2:
                pRK28NC ->FMCTL |= FMC_WP;  //½â³ýд±£»¤
-               pRK28NC ->chip[0].cmd  = command;              
+               pRK28NC ->chip[master->cs].cmd  = command;             
                rk2818_nand_wait_busy(mtd,ERASE_BUSY_COUNT);
-               pRK28NC ->chip[0].cmd  = NAND_CMD_STATUS;
-               status = pRK28NC ->chip[0].data;
+               pRK28NC ->chip[master->cs].cmd  = NAND_CMD_STATUS;
+               status = pRK28NC ->chip[master->cs].data;
                
                if(status&0x1)
                        ret = -1;
@@ -361,35 +414,35 @@ static void rk2818_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int colum
                
        case NAND_CMD_SEQIN:
                pRK28NC ->FMCTL |= FMC_WP;  //½â³ýд±£»¤
-               pRK28NC ->chip[0].cmd  = command;
+               pRK28NC ->chip[master->cs].cmd  = command;
               udelay(1);
                if ( column>= 0 )
                  {
-                   pRK28NC ->chip[0].addr = column;
+                   pRK28NC ->chip[master->cs].addr = column;
                     if( mtd->writesize > 512) 
-                      pRK28NC ->chip[0].addr = (column >> 8) & 0xff;
+                      pRK28NC ->chip[master->cs].addr = (column >> 8) & 0xff;
                  }
                if( page_addr>=0 )
                  {
-                       pRK28NC ->chip[0].addr = page_addr & 0xff;
-                       pRK28NC ->chip[0].addr = (page_addr>>8)&0xff;
-                       pRK28NC ->chip[0].addr = (page_addr>>16)&0xff;
+                       pRK28NC ->chip[master->cs].addr = page_addr & 0xff;
+                       pRK28NC ->chip[master->cs].addr = (page_addr>>8)&0xff;
+                       pRK28NC ->chip[master->cs].addr = (page_addr>>16)&0xff;
                 }
                
                break;
                
        case NAND_CMD_STATUS:
                pRK28NC ->BCHCTL = 0x0;
-               pRK28NC ->chip[0].cmd = command;
+               pRK28NC ->chip[master->cs].cmd = command;
                break;
 
        case NAND_CMD_RESET:
-               pRK28NC ->chip[0].cmd = command;
+               pRK28NC ->chip[master->cs].cmd = command;
                break;
 
        /* This applies to read commands */
        default:
-              pRK28NC ->chip[0].cmd = command;
+              pRK28NC ->chip[master->cs].cmd = command;
                break;
        }
 
@@ -453,15 +506,21 @@ int rk2818_nand_calculate_ecc(struct mtd_info *mtd,const uint8_t *dat,uint8_t *e
        struct nand_chip *nand_chip = mtd->priv;
        struct rk2818_nand_mtd *master = nand_chip->priv;
        pNANDC pRK28NC=  (pNANDC)(master->regs);
-
          
-       int i;
-       
+       int i,chipnr;
+
+          
+       mutex_lock(&rknand_mutex);
 
+       chipnr = master->cs ;
+       
+       rk2818_nand_select_chip(mtd,chipnr);
        rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT);
           
        pRK28NC->FLCTL |= FL_BYPASS;  // dma mode
-
+         
        for(i=0;i<mtd->writesize/0x400;i++)
        {
               pRK28NC ->BCHCTL = BCH_RST;
@@ -471,7 +530,12 @@ int rk2818_nand_calculate_ecc(struct mtd_info *mtd,const uint8_t *dat,uint8_t *e
           
               memcpy(buf+i*0x400,(u_char *)(pRK28NC->buf),0x400);  //  only use nandc sram0
        }
+       
                
+       rk2818_nand_select_chip(mtd,-1);
+
+       mutex_unlock(&rknand_mutex);
+       
     return 0;
        
  }
@@ -481,11 +545,17 @@ void  rk2818_nand_write_page(struct mtd_info *mtd,struct nand_chip *chip,const u
        struct nand_chip *nand_chip = mtd->priv;
        struct rk2818_nand_mtd *master = nand_chip->priv;
        pNANDC pRK28NC=  (pNANDC)(master->regs);
-       uint32_t  i = 0;
-
+       uint32_t  i = 0, chipnr;
+          
+       mutex_lock(&rknand_mutex);
 
+       chipnr = master->cs ;
+          
+       rk2818_nand_select_chip(mtd,chipnr);
        
        pRK28NC->FLCTL |= FL_BYPASS;  // dma mode
+
+               
          for(i=0;i<mtd->writesize/0x400;i++)
           {
               memcpy((u_char *)(pRK28NC->buf),(buf+i*0x400),0x400);  //  only use nandc sram0          
@@ -498,8 +568,14 @@ void  rk2818_nand_write_page(struct mtd_info *mtd,struct nand_chip *chip,const u
           }
 
          pRK28NC ->chip[0].cmd = NAND_CMD_PAGEPROG;
+        
+
+         
          rk2818_nand_wait_busy(mtd,PROGRAM_BUSY_COUNT);
          
+        rk2818_nand_select_chip(mtd,-1);
+
+     mutex_unlock(&rknand_mutex);
        
     return;
          
@@ -510,28 +586,43 @@ int rk2818_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page,
        struct nand_chip *nand_chip = mtd->priv;
        struct rk2818_nand_mtd *master = nand_chip->priv;
        pNANDC pRK28NC=  (pNANDC)(master->regs);
-       int i;
+       int i,chipnr;
 
+         
        if (sndcmd) {
                chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
                sndcmd = 0;
        }
-               
+
+       mutex_lock(&rknand_mutex);
+
+       chipnr = master->cs ;
+
+       rk2818_nand_select_chip(mtd,chipnr);
+
        rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT);
-          
+
+       
        pRK28NC->FLCTL |= FL_BYPASS;  // dma mode
 
+       
+
+       
        for(i=0;i<mtd->writesize/0x400;i++)
        {
               pRK28NC ->BCHCTL = BCH_RST;
               pRK28NC ->FLCTL = (0<<4)|FL_COR_EN|(0x1<<5)|FL_BYPASS|FL_START ;         
                wait_op_done(mtd,TROP_US_DELAY,0);   
                rk2818_nand_wait_bchdone(mtd,TROP_US_DELAY) ;          
-         //     memcpy(buf+i*0x400,(u_char *)(pRK28NC->buf),0x400);  //  only use nandc sram0
               if(i==0)
                  memcpy((u_char *)(chip->oob_poi+ chip->ops.ooboffs),(u_char *)(pRK28NC->spare),4); 
        }
 
+          
+        rk2818_nand_select_chip(mtd,-1);
+
+        mutex_unlock(&rknand_mutex);
+
 
        return sndcmd;
 }
@@ -542,14 +633,21 @@ int       rk2818_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint
        struct rk2818_nand_mtd *master = nand_chip->priv;
        pNANDC pRK28NC=  (pNANDC)(master->regs);
 
-         
-       int i;
+       int i,chipnr;
+
+       
+    mutex_lock(&rknand_mutex);
+
+       chipnr = master->cs ;
        
+       rk2818_nand_select_chip(mtd,chipnr);
 
        rk2818_nand_wait_busy(mtd,READ_BUSY_COUNT);
           
        pRK28NC->FLCTL |= FL_BYPASS;  // dma mode
 
+       
+          
        for(i=0;i<mtd->writesize/0x400;i++)
        {
               pRK28NC ->BCHCTL = BCH_RST;
@@ -560,10 +658,106 @@ int      rk2818_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint
               if(i==0)
                  memcpy((u_char *)(chip->oob_poi+ chip->ops.ooboffs),(u_char *)(pRK28NC->spare),4); 
        }
-               
+
+        
+        rk2818_nand_select_chip(mtd,-1);
+        mutex_unlock(&rknand_mutex);
+
+        
     return 0;
 }
 
+static int rk2818_nand_setrate(struct rk2818_nand_mtd *info)
+{
+       pNANDC pRK28NC=  (pNANDC)(info->regs);
+       
+       unsigned long clkrate = clk_get_rate(info->clk);
+
+       u_char accesstime,rwpw,csrw,rwcs;
+
+       unsigned int ns=0,timingcfg;
+
+       unsigned long flags; 
+
+       //scan nand flash access time
+       if ( info->accesstime ==0x00 )
+              accesstime=50;
+       else if ( info->accesstime==0x80)
+               accesstime=25;
+       else if ( info->accesstime==0x08)
+               accesstime=20;
+       else
+               accesstime=60;   //60ns
+
+       info->clk_rate = clkrate;
+       clkrate /= 1000000;     /* turn clock into MHz for ease of use */
+       
+       if(clkrate>0 && clkrate<200)
+          ns= 1000/clkrate; // ns
+        else
+          return -1;
+               
+       timingcfg = (accesstime + ns -1)/ns;
+
+       timingcfg = (timingcfg>=3) ? (timingcfg-2) : timingcfg;           //csrw+1, rwcs+1
+
+       rwpw = timingcfg-timingcfg/4;
+       csrw = timingcfg/4;
+       rwcs = (timingcfg/4 >=1)?(timingcfg/4):1;
+
+       mutex_lock(&rknand_mutex);
+
+       pRK28NC ->FMWAIT |=  (rwcs<<FMW_RWCS_OFFSET)|(rwpw<<FMW_RWPW_OFFSET)|(csrw<<FMW_CSRW_OFFSET);
+
+       mutex_unlock(&rknand_mutex);
+
+
+       return 0;
+}
+
+/* cpufreq driver support */
+
+#ifdef CONFIG_CPU_FREQ
+
+static int rk2818_nand_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data)
+{
+       struct rk2818_nand_mtd *info;
+       unsigned long newclk;
+
+       info = container_of(nb, struct rk2818_nand_mtd, freq_transition);
+       newclk = clk_get_rate(info->clk);
+
+       if (val == CPUFREQ_POSTCHANGE && newclk != info->clk_rate) 
+        {
+               rk2818_nand_setrate(info);
+       }
+
+       return 0;
+}
+
+static inline int rk2818_nand_cpufreq_register(struct rk2818_nand_mtd *info)
+{
+       info->freq_transition.notifier_call = rk2818_nand_cpufreq_transition;
+
+       return cpufreq_register_notifier(&info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void rk2818_nand_cpufreq_deregister(struct rk2818_nand_mtd *info)
+{
+       cpufreq_unregister_notifier(&info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int rk2818_nand_cpufreq_register(struct rk2818_nand_mtd *info)
+{
+       return 0;
+}
+
+static inline void rk2818_nand_cpufreq_deregister(struct rk2818_nand_mtd *info)
+{
+}
+#endif
+
 
 static int rk2818_nand_probe(struct platform_device *pdev)
 {
@@ -574,6 +768,7 @@ static int rk2818_nand_probe(struct platform_device *pdev)
        struct resource *res;
        int err = 0;
        pNANDC pRK28NC;
+       u_char  maf_id,dev_id,ext_id3,ext_id4;
 
 #ifdef CONFIG_MTD_PARTITIONS
        struct mtd_partition *partitions = NULL;
@@ -638,10 +833,28 @@ static int rk2818_nand_probe(struct platform_device *pdev)
                this->ecc.mode = NAND_ECC_SOFT;         
        }
 
+
+       mutex_init(&rknand_mutex);
+
+       master->clk = clk_get(NULL, "nandc");
+
+       clk_enable(master->clk);
+       
        pRK28NC =  (pNANDC)(master->regs);
-       pRK28NC ->FMCTL = FMC_WP|FMC_FRDY|(0x1<<0);
+       pRK28NC ->FMCTL = FMC_WP|FMC_FRDY;
        pRK28NC ->FMWAIT |=  (1<<FMW_RWCS_OFFSET)|(4<<FMW_RWPW_OFFSET)|(1<<FMW_CSRW_OFFSET);
        pRK28NC ->BCHCTL = 0x1;
+
+       this->select_chip(mtd, 0);
+       this->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+       maf_id = this->read_byte(mtd);
+       dev_id = this->read_byte(mtd);
+       ext_id3 = this->read_byte(mtd);
+       ext_id4 = this->read_byte(mtd);
+       
+       master->accesstime = ext_id4&0x88;
+       
+       rk2818_nand_setrate(master);
        
        /* Reset NAND */
        this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
@@ -651,14 +864,28 @@ static int rk2818_nand_probe(struct platform_device *pdev)
                this->ecc.layout = &nand_hw_eccoob_16;
        }
 
+      // iomux flash  cs1~cs7
+      rk2818_mux_api_set(GPIOA5_FLASHCS1_SEL_NAME, IOMUXB_FLASH_CS1);
+      rk2818_mux_api_set(GPIOA6_FLASHCS2_SEL_NAME, IOMUXB_FLASH_CS2);
+      rk2818_mux_api_set(GPIOA7_FLASHCS3_SEL_NAME, IOMUXB_FLASH_CS3);
+      rk2818_mux_api_set(GPIOE_SPI1_FLASH_SEL1_NAME, IOMUXA_FLASH_CS45);  
+      rk2818_mux_api_set(GPIOE_SPI1_FLASH_SEL_NAME, IOMUXA_FLASH_CS67);  
+          
        /* Scan to find existence of the device */
-       if (nand_scan(mtd, 1)) {
+          if (nand_scan(mtd, 8)) {     // rk2818 nandc support max 8 cs
+        
                DEBUG(MTD_DEBUG_LEVEL0,
                      "RK2818 NAND: Unable to find any NAND device.\n");
                err = -ENXIO;
                goto outscan;
        }
 
+#if 0
+      // rk281x dma mode bch must  (1k data + 32 oob) bytes align , so cheat system writesize =1024,oobsize=32
+       mtd->writesize = 1024;
+       mtd->oobsize = 32;
+#endif
+
 #ifdef CONFIG_MTD_PARTITIONS
         num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0);
        if (num_partitions > 0) {
@@ -678,6 +905,12 @@ static int rk2818_nand_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, master);
 
+       err =rk2818_nand_cpufreq_register(master);
+       if (err < 0) {
+               printk(KERN_ERR"rk2818 nand failed to init cpufreq support\n");
+               goto outscan;
+       }
+
        return 0;
        
 outres:
@@ -695,8 +928,24 @@ static int rk2818_nand_remove(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, NULL);
 
+       if(master == NULL)
+               return 0;
+
+       rk2818_nand_cpufreq_deregister(master);
+       
+
        nand_release(&master->mtd);
-       iounmap(master->regs);
+
+       if(master->regs!=NULL){
+               iounmap(master->regs);
+               master->regs = NULL;
+       }
+
+       if (master->clk != NULL && !IS_ERR(master->clk)) {
+               clk_disable(master->clk);
+               clk_put(master->clk);
+       }
+       
        kfree(master);
 
        return 0;
@@ -756,6 +1005,26 @@ static void __exit rk2818_nand_exit(void)
        platform_driver_unregister(&rk2818_nand_driver);
 }
 
+
+// nandc dma cs mutex for dm9000 interface
+int rk2818_nand_status_mutex_trylock(void)
+{
+     pNANDC pRK28NC=  (pNANDC)RK2818_NANDC_BASE;
+     if( mutex_trylock(&rknand_mutex))
+       {
+               pRK28NC->FMCTL &=0xffffff00;   // release chip select
+               return 1;      // ready 
+       }
+      else
+               return 0;     // busy
+}
+
+void rk2818_nand_status_mutex_unlock(void)
+{
+     mutex_unlock(&rknand_mutex);
+     return;
+}
+
 module_init(rk2818_nand_init);
 module_exit(rk2818_nand_exit);
 
index 9a7d9e9857974b1b2b68a94ad5906dca75b8d411..1b0bb74a2e660b6fb0a29c16972228a89f89b66a 100644 (file)
@@ -38,6 +38,7 @@
 #include <asm/irq.h>
 #include <asm/io.h>
 #include <mach/gpio.h>
+#include <mach/iomux.h>
 
 #include "dm9000.h"
 
@@ -104,6 +105,12 @@ typedef struct board_info {
        int             debug_level;
 
        enum dm9000_type type;
+       
+#ifdef CONFIG_DM9000_USE_NAND_CONTROL
+    void *dev_id;
+    struct work_struct dm9k_work;
+       struct workqueue_struct *dm9000_wq;
+#endif
 
        void (*inblk)(void __iomem *port, void *data, int length);
        void (*outblk)(void __iomem *port, void *data, int length);
@@ -133,7 +140,6 @@ typedef struct board_info {
 } board_info_t;
 
 /* debug code */
-#define DEBUG_LEVEL 4
 #define dm9000_dbg(db, lev, msg...) do {               \
        if ((lev) < CONFIG_DM9000_DEBUGLEVEL &&         \
            (lev) < db->debug_level) {                  \
@@ -141,6 +147,14 @@ typedef struct board_info {
        }                                               \
 } while (0)
 
+#ifdef CONFIG_DM9000_USE_NAND_CONTROL
+extern int rk2818_nand_status_mutex_trylock(void);
+extern void rk2818_nand_status_mutex_unlock(void);
+#else
+static int rk2818_nand_status_mutex_trylock(void) {return 1;}
+static void rk2818_nand_status_mutex_unlock(void) {return;}
+#endif
+
 static inline board_info_t *to_dm9000_board(struct net_device *dev)
 {
        return netdev_priv(dev);
@@ -307,11 +321,16 @@ dm9000_read_locked(board_info_t *db, int reg)
 {
        unsigned long flags;
        unsigned int ret;
+       
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
 
-       spin_lock_irqsave(&db->lock, flags);
+       spin_lock_irqsave(&db->lock, flags);    
        ret = ior(db, reg);
        spin_unlock_irqrestore(&db->lock, flags);
 
+       rk2818_nand_status_mutex_unlock();
+
        return ret;
 }
 
@@ -363,21 +382,29 @@ dm9000_read_eeprom(board_info_t *db, int offset, u8 *to)
        }
 
        mutex_lock(&db->addr_lock);
+       
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
 
        spin_lock_irqsave(&db->lock, flags);
-
+               
        iow(db, DM9000_EPAR, offset);
        iow(db, DM9000_EPCR, EPCR_ERPRR);
 
        spin_unlock_irqrestore(&db->lock, flags);
 
+       rk2818_nand_status_mutex_unlock();
+
        dm9000_wait_eeprom(db);
 
        /* delay for at-least 150uS */
        msleep(1);
+       
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
 
        spin_lock_irqsave(&db->lock, flags);
-
+       
        iow(db, DM9000_EPCR, 0x0);
 
        to[0] = ior(db, DM9000_EPDRL);
@@ -385,6 +412,8 @@ dm9000_read_eeprom(board_info_t *db, int offset, u8 *to)
 
        spin_unlock_irqrestore(&db->lock, flags);
 
+       rk2818_nand_status_mutex_unlock();
+
        mutex_unlock(&db->addr_lock);
 }
 
@@ -400,22 +429,32 @@ dm9000_write_eeprom(board_info_t *db, int offset, u8 *data)
                return;
 
        mutex_lock(&db->addr_lock);
+       
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
 
-       spin_lock_irqsave(&db->lock, flags);
+       spin_lock_irqsave(&db->lock, flags);    
        iow(db, DM9000_EPAR, offset);
        iow(db, DM9000_EPDRH, data[1]);
        iow(db, DM9000_EPDRL, data[0]);
        iow(db, DM9000_EPCR, EPCR_WEP | EPCR_ERPRW);
        spin_unlock_irqrestore(&db->lock, flags);
 
+       rk2818_nand_status_mutex_unlock();
+
        dm9000_wait_eeprom(db);
 
        mdelay(1);      /* wait at least 150uS to clear */
+       
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
 
        spin_lock_irqsave(&db->lock, flags);
        iow(db, DM9000_EPCR, 0);
        spin_unlock_irqrestore(&db->lock, flags);
 
+       rk2818_nand_status_mutex_unlock();
+
        mutex_unlock(&db->addr_lock);
 }
 
@@ -480,9 +519,13 @@ static int dm9000_set_rx_csum(struct net_device *dev, uint32_t data)
        if (dm->can_csum) {
                dm->rx_csum = data;
 
+               while(!rk2818_nand_status_mutex_trylock())
+                       dev_dbg(dm->dev, "fun:%s, nand busy\n", __func__);
+               
                spin_lock_irqsave(&dm->lock, flags);
                iow(dm, DM9000_RCSR, dm->rx_csum ? RCSR_CSUM : 0);
                spin_unlock_irqrestore(&dm->lock, flags);
+               rk2818_nand_status_mutex_unlock();
 
                return 0;
        }
@@ -680,9 +723,12 @@ dm9000_hash_table(struct net_device *dev)
        unsigned long flags;
 
        dm9000_dbg(db, 1, "entering %s\n", __func__);
+       
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
 
        spin_lock_irqsave(&db->lock, flags);
-
+       
        for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
                iow(db, oft, dev->dev_addr[i]);
 
@@ -713,6 +759,8 @@ dm9000_hash_table(struct net_device *dev)
 
        iow(db, DM9000_RCR, rcr);
        spin_unlock_irqrestore(&db->lock, flags);
+       
+       rk2818_nand_status_mutex_unlock();
 }
 
 /*
@@ -776,6 +824,10 @@ static void dm9000_timeout(struct net_device *dev)
 
        /* Save previous register address */
        reg_save = readb(db->io_addr);
+       
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
+       
        spin_lock_irqsave(&db->lock, flags);
 
        netif_stop_queue(dev);
@@ -788,6 +840,8 @@ static void dm9000_timeout(struct net_device *dev)
        /* Restore previous register address */
        writeb(reg_save, db->io_addr);
        spin_unlock_irqrestore(&db->lock, flags);
+       
+       rk2818_nand_status_mutex_unlock();
 }
 
 static void dm9000_send_packet(struct net_device *dev,
@@ -830,8 +884,12 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
                return NETDEV_TX_BUSY;
        }
 
+       if (!rk2818_nand_status_mutex_trylock()) {
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
+               return NETDEV_TX_BUSY;
+       }
        spin_lock_irqsave(&db->lock, flags);
-
+       
        /* Move data to DM9000 TX RAM */
        writeb(DM9000_MWCMD, db->io_addr);
 
@@ -850,6 +908,8 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        spin_unlock_irqrestore(&db->lock, flags);
+       
+       rk2818_nand_status_mutex_unlock();
 
        /* free this SKB */
        dev_kfree_skb(skb);
@@ -998,6 +1058,82 @@ dm9000_rx(struct net_device *dev)
        } while (rxbyte & DM9000_PKT_RDY);
 }
 
+#ifdef CONFIG_DM9000_USE_NAND_CONTROL
+static void dm9000_interrupt_work(struct work_struct *work)
+{
+       board_info_t *db = container_of(work, board_info_t, dm9k_work); 
+       struct net_device *dev = db->dev_id;
+       int int_status;
+       unsigned long flags;
+       u8 reg_save;
+       
+       //printk("entering %s\n", __FUNCTION__);
+       
+       /* A real interrupt coming */
+
+       /* holders of db->lock must always block IRQs */
+       
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
+       
+       spin_lock_irqsave(&db->lock, flags);
+
+       /* Save previous register address */
+       reg_save = readb(db->io_addr);
+
+       /* Disable all interrupts */
+       iow(db, DM9000_IMR, IMR_PAR);
+
+       /* Got DM9000 interrupt status */
+       int_status = ior(db, DM9000_ISR);       /* Got ISR */
+       iow(db, DM9000_ISR, int_status);        /* Clear ISR status */
+
+       if (netif_msg_intr(db))
+               dev_dbg(db->dev, "interrupt status %02x\n", int_status);
+
+       /* Received the coming packet */
+       if (int_status & ISR_PRS)
+               dm9000_rx(dev);
+
+       /* Trnasmit Interrupt check */
+       if (int_status & ISR_PTS)
+               dm9000_tx_done(dev, db);
+
+       if (db->type != TYPE_DM9000E) {
+               if (int_status & ISR_LNKCHNG) {
+                       /* fire a link-change request */
+                       schedule_delayed_work(&db->phy_poll, 1);
+               }
+       }
+
+       /* Re-enable interrupt mask */
+       iow(db, DM9000_IMR, db->imr_all);
+
+       /* Restore previous register address */
+       writeb(reg_save, db->io_addr);
+
+       spin_unlock_irqrestore(&db->lock, flags);
+       
+       rk2818_nand_status_mutex_unlock();
+
+       enable_irq(dev->irq);
+       
+}
+
+static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       board_info_t *db = netdev_priv(dev);
+
+       //printk("enter : %s\n", __FUNCTION__);
+       
+       db->dev_id = dev_id;
+       disable_irq_nosync(irq);
+       queue_work(db->dm9000_wq, &(db->dm9k_work));
+       
+       return IRQ_HANDLED;
+}
+#else
 static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
 {
        struct net_device *dev = dev_id;
@@ -1010,7 +1146,7 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
 
        /* A real interrupt coming */
 
-       /* holders of db->lock must always block IRQs */
+       /* holders of db->lock must always block IRQs */        
        spin_lock_irqsave(&db->lock, flags);
 
        /* Save previous register address */
@@ -1048,9 +1184,10 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
        writeb(reg_save, db->io_addr);
 
        spin_unlock_irqrestore(&db->lock, flags);
-
+       
        return IRQ_HANDLED;
 }
+#endif
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
 /*
@@ -1074,6 +1211,11 @@ dm9000_open(struct net_device *dev)
        board_info_t *db = netdev_priv(dev);
        unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
 
+       #ifdef CONFIG_DM9000_USE_NAND_CONTROL
+       db->dm9000_wq = create_workqueue("dm9000 wq");
+       INIT_WORK(&(db->dm9k_work), dm9000_interrupt_work);
+       #endif
+       
        if (netif_msg_ifup(db))
                dev_dbg(db->dev, "enabling %s\n", dev->name);
 
@@ -1128,8 +1270,11 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
 
        mutex_lock(&db->addr_lock);
 
-       spin_lock_irqsave(&db->lock,flags);
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
 
+       spin_lock_irqsave(&db->lock,flags);
+       
        /* Save previous register address */
        reg_save = readb(db->io_addr);
 
@@ -1140,10 +1285,16 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
 
        writeb(reg_save, db->io_addr);
        spin_unlock_irqrestore(&db->lock,flags);
+       
+       rk2818_nand_status_mutex_unlock();
 
        dm9000_msleep(db, 1);           /* Wait read complete */
 
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
+
        spin_lock_irqsave(&db->lock,flags);
+       
        reg_save = readb(db->io_addr);
 
        iow(db, DM9000_EPCR, 0x0);      /* Clear phyxcer read command */
@@ -1154,6 +1305,8 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
        /* restore the previous address */
        writeb(reg_save, db->io_addr);
        spin_unlock_irqrestore(&db->lock,flags);
+       
+       rk2818_nand_status_mutex_unlock();
 
        mutex_unlock(&db->addr_lock);
 
@@ -1175,8 +1328,11 @@ dm9000_phy_write(struct net_device *dev,
        dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n", reg, value);
        mutex_lock(&db->addr_lock);
 
-       spin_lock_irqsave(&db->lock,flags);
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
 
+       spin_lock_irqsave(&db->lock,flags);
+       
        /* Save previous register address */
        reg_save = readb(db->io_addr);
 
@@ -1191,10 +1347,16 @@ dm9000_phy_write(struct net_device *dev,
 
        writeb(reg_save, db->io_addr);
        spin_unlock_irqrestore(&db->lock, flags);
+       
+       rk2818_nand_status_mutex_unlock();
 
        dm9000_msleep(db, 1);           /* Wait write complete */
 
+       while(!rk2818_nand_status_mutex_trylock())
+               dev_dbg(db->dev, "fun:%s, nand busy\n", __func__);
+
        spin_lock_irqsave(&db->lock,flags);
+       
        reg_save = readb(db->io_addr);
 
        iow(db, DM9000_EPCR, 0x0);      /* Clear phyxcer write command */
@@ -1203,6 +1365,8 @@ dm9000_phy_write(struct net_device *dev,
        writeb(reg_save, db->io_addr);
 
        spin_unlock_irqrestore(&db->lock, flags);
+       
+       rk2818_nand_status_mutex_unlock();
        mutex_unlock(&db->addr_lock);
 }
 
@@ -1240,6 +1404,10 @@ dm9000_stop(struct net_device *ndev)
 
        dm9000_shutdown(ndev);
 
+       #ifdef CONFIG_DM9000_USE_NAND_CONTROL
+       destroy_workqueue(db->dm9000_wq);
+       #endif
+
        return 0;
 }
 
@@ -1290,8 +1458,6 @@ dm9000_probe(struct platform_device *pdev)
        db->dev = &pdev->dev;
        db->ndev = ndev;
 
-       //db->debug_level = 5;//add by liuyx@20100511
-
        spin_lock_init(&db->lock);
        mutex_init(&db->addr_lock);
 
@@ -1350,6 +1516,7 @@ dm9000_probe(struct platform_device *pdev)
        #if 0
        ndev->irq       = db->irq_res->start;
        #else//modify by liuyx@20100510
+       rk2818_mux_api_set(GPIOE_SPI1_FLASH_SEL1_NAME, IOMUXA_GPIO1_A12);       
        ndev->irq = gpio_to_irq(db->irq_res->start);
        #endif