Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3167308
  • 博文数量: 685
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5303
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-19 14:17
个人简介

文章分类

全部博文(685)

文章存档

2015年(116)

2014年(569)

分类: 嵌入式

2014-09-22 11:02:47

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include

#include
#include
extern int add_mtd_partitions(struct mtd_info *master,
         const struct mtd_partition *parts,
         int nbparts);

enum s3c_cpu_type {
 TYPE_S3C2450, /* including s3c2416 */
 TYPE_S3C6400,
 TYPE_S3C6410, /* including s3c6430/31 */
        TYPE_S5PC100,
};

struct s3c_nand_info {
 /* mtd info */
 struct nand_hw_control  controller;
 struct s3c_nand_mtd_info *mtds;
 struct s3c2410_platform_nand *platform;

 /* device info */
 struct device   *device;
 struct resource   *area;
 struct clk   *clk;
 void __iomem   *regs;
 void __iomem   *sel_reg;
 int    sel_bit;
 int    mtd_count;

 enum s3c_cpu_type  cpu_type;
};
static struct s3c_nand_info s3c_nand;

static struct mtd_info *s3c_mtd = NULL;

/* Nand flash definition values by jsgood */
#define S3C_NAND_TYPE_UNKNOWN 0x0
#define S3C_NAND_TYPE_SLC 0x1//使用单层存储单元的nand flash
#define S3C_NAND_TYPE_MLC 0x2//使用多层存储单元的nand flash

/*
 * Cached progamming disabled for now, Not sure if its worth the
 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
 *
 * if want to use cached program, define next
 * by jsgood (modified to keep prevent rule)
 */
#undef CONFIG_MTD_NAND_S3C_CACHEDPROG

/* Nand flash global values by jsgood */
int cur_ecc_mode = 0;
int nand_type = S3C_NAND_TYPE_UNKNOWN;

#if defined(CONFIG_MTD_NAND_S3C_HWECC)//只有选中CONFIG_MTD_NAND_S3C_HWECC才会定义下面的两个结构体。
/* Nand flash oob definition for SLC 512b page size by jsgood */
static struct nand_ecclayout s3c_nand_oob_16 = {
 .eccbytes = 4,
 .eccpos = {1, 2, 3, 4},
 .oobfree = {
  {.offset = 6,
   .length = 10}}
};

/* Nand flash oob definition for SLC 2k page size by jsgood */
static struct nand_ecclayout s3c_nand_oob_64 = {
 .eccbytes = 16,
 .eccpos = {40, 41, 42, 43, 44, 45, 46, 47,
     48, 49, 50, 51, 52, 53, 54, 55},
 .oobfree = {
  {.offset = 2,
   .length = 38}}
};

/* Nand flash oob definition for MLC 2k page size by jsgood */
static struct nand_ecclayout s3c_nand_oob_mlc_64 = {
 .eccbytes = 32,
 .eccpos = {
     32, 33, 34, 35, 36, 37, 38, 39,
     40, 41, 42, 43, 44, 45, 46, 47,
      48, 49, 50, 51, 52, 53, 54, 55,
        56, 57, 58, 59, 60, 61, 62, 63},
 .oobfree = {
  {.offset = 2,
   .length = 28}}
};

static struct nand_ecclayout s3c_nand_oob_mlc_128 = {
 .eccbytes = 64,
 .eccpos = {
     64,65,66,67,68,69,70,71,72,73,
     74,75,76,77,78,79,80,81,82,83,
     84,85,86,87,88,89,90,91,92,93,
     94,95,96,97,98,99,100,101,102,103,
     104,105,106,107,108,109,110,111,112,113,
     114,115,116,117,118,119,120,121,122,123,
     124,125,126,127},
 .oobfree = {
  {.offset = 2,
   .length = 60}}
};

#endif

#if defined(CONFIG_MTD_NAND_S3C_DEBUG)//选中MTD_NAND_S3C_DEBUG后即可打印出oob区域的数据。
/*
 * Function to print out oob buffer for debugging
 * Written by jsgood
 */
void print_oob(const char *header, struct mtd_info *mtd)
{
 int i;
 struct nand_chip *chip = mtd->priv;

 printk("%s:\t", header);

 for(i = 0; i < 64; i++)
  printk("%02x ", chip->oob_poi[i]);

 printk("\n");
}
EXPORT_SYMBOL(print_oob);
#endif


/*
 * Hardware specific access to control-lines function
 * Written by jsgood
 */
static void s3c_nand_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
 unsigned int cur;
 void __iomem *regs = s3c_nand.regs;

 if (ctrl & NAND_CTRL_CHANGE) {
  if (ctrl & NAND_NCE) {
   if (dat != NAND_CMD_NONE) {
    cur = readl(regs + S3C_NFCONT);
    cur &= ~S3C_NFCONT_nFCE0;
    writel(cur, regs + S3C_NFCONT);
   }
  } else {
   cur = readl(regs + S3C_NFCONT);
   cur |= S3C_NFCONT_nFCE0;
   writel(cur, regs + S3C_NFCONT);
  }
 }

 if (dat != NAND_CMD_NONE) {
  if (ctrl & NAND_CLE)
   writeb(dat, regs + S3C_NFCMMD);
  else if (ctrl & NAND_ALE)
   writeb(dat, regs + S3C_NFADDR);
 }
}

/*
 * Function for checking device ready pin
 * Written by jsgood
 */
static int s3c_nand_device_ready(struct mtd_info *mtd)
{
 void __iomem *regs = s3c_nand.regs;
/* it's to check the RnB nand signal bit and return to device ready condition in nand_base.c */
 return ((readl(regs + S3C_NFSTAT) & S3C_NFSTAT_BUSY));
}

/*
 * We don't use a bad block table
 */
static int s3c_nand_scan_bbt(struct mtd_info *mtdinfo)
{
 return 0;
}

#if defined(CONFIG_MTD_NAND_S3C_HWECC)
#if 0
/*
 * S3C Nand flash chip enable function
 * Written by jsgood
 */
static void s3c_nand_ce_on(struct mtd_info *mtd)
{
 struct nand_chip *chip = mtd->priv;

 chip->cmd_ctrl(mtd, 0x0, NAND_NCE | NAND_CTRL_CHANGE);
 nand_wait_ready(mtd);
}

/*
 * S3C Nand flash chip disable function
 * Written by jsgood
 */
static void s3c_nand_ce_off(struct mtd_info *mtd)
{
 struct nand_chip *chip = mtd->priv;

 chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_CTRL_CHANGE);
 nand_wait_ready(mtd);
}
#endif

/*
 * Function for checking ECCEncDone in NFSTAT
 * Written by jsgood
 */
static void s3c_nand_wait_enc(void)
{
        void __iomem *regs = s3c_nand.regs;
        unsigned long timeo = jiffies;
 
        timeo += 16;    /* when Hz=200,  jiffies interval 1/200=5mS, waiting for 80mS  80/5 = 16 */
 
        /* Apply this short delay always to ensure that we do wait tWB in
         * any case on any machine. */

        while (time_before(jiffies, timeo)) {
  if (readl(regs + S3C_NFSTAT) & S3C_NFSTAT_ECCENCDONE)
    break;
  cond_resched();
 }
}

/*
 * Function for checking ECCDecDone in NFSTAT
 * Written by jsgood
 */
static void s3c_nand_wait_dec(void)
{
 void __iomem *regs = s3c_nand.regs;
        unsigned long timeo = jiffies;
 
        timeo += 16;    /* when Hz=200,  jiffies interval  1/200=5mS, waiting for 80mS  80/5 = 16 */
 
        /* Apply this short delay always to ensure that we do wait tWB in
         * any case on any machine. */

        while (time_before(jiffies, timeo)) {
  if (readl(regs + S3C_NFSTAT) & S3C_NFSTAT_ECCDECDONE)
   break;
  cond_resched();
 }
}

/*
 * Function for checking ECC Busy
 * Written by jsgood
 */
static void s3c_nand_wait_ecc_busy(void)
{
 void __iomem *regs = s3c_nand.regs;
        unsigned long timeo = jiffies;
 
        timeo += 16;    /* when Hz=200,  jiffies interval  1/200=5mS, waiting for 80mS  80/5 = 16 */
 
        /* Apply this short delay always to ensure that we do wait tWB in
         * any case on any machine. */

        while (time_before(jiffies, timeo)) {
  if (!(readl(regs + S3C_NFMECCERR0) & S3C_NFECCERR0_ECCBUSY))
   break;
  cond_resched();
 }
}

/*
 * This function is called before encoding ecc codes to ready ecc engine.
 * Written by jsgood
 */
static void s3c_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
 u_long nfcont;
 u_long nfconf;
 void __iomem *regs = s3c_nand.regs;

 cur_ecc_mode = mode;

 nfconf = readl(regs + S3C_NFCONF);

 if (s3c_nand.cpu_type == TYPE_S3C6400) {
  if (nand_type == S3C_NAND_TYPE_SLC)
   nfconf &= ~S3C_NFCONF_ECC_MLC; /* SLC */
  else
   nfconf |= S3C_NFCONF_ECC_MLC; /* MLC */
 } else {
  nfconf &= ~(0x3 << 23);

  if (nand_type == S3C_NAND_TYPE_SLC)
   nfconf |= S3C_NFCONF_ECC_1BIT;
  else
   nfconf |= S3C_NFCONF_ECC_4BIT;
 }

 writel(nfconf, regs + S3C_NFCONF);

 /* Init main ECC & unlock */
 nfcont = readl(regs + S3C_NFCONT);
 nfcont |= S3C_NFCONT_INITMECC;
 nfcont &= ~S3C_NFCONT_MECCLOCK;

 if (nand_type == S3C_NAND_TYPE_MLC) {
  if (mode == NAND_ECC_WRITE)
   nfcont |= S3C_NFCONT_ECC_ENC;
  else if (mode == NAND_ECC_READ)
   nfcont &= ~S3C_NFCONT_ECC_ENC;
 }

 writel(nfcont, regs + S3C_NFCONT);
}

/*
 * This function is called immediately after encoding ecc codes.
 * This function returns encoded ecc codes.
 * Written by jsgood
 */
static int s3c_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
 u_long nfcont, nfmecc0, nfmecc1;
 void __iomem *regs = s3c_nand.regs;

 /* Lock */
 nfcont = readl(regs + S3C_NFCONT);
 nfcont |= S3C_NFCONT_MECCLOCK;
 writel(nfcont, regs + S3C_NFCONT);

 if (nand_type == S3C_NAND_TYPE_SLC) {
  nfmecc0 = readl(regs + S3C_NFMECC0);

  ecc_code[0] = nfmecc0 & 0xff;
  ecc_code[1] = (nfmecc0 >> 8) & 0xff;
  ecc_code[2] = (nfmecc0 >> 16) & 0xff;
  ecc_code[3] = (nfmecc0 >> 24) & 0xff;
 } else {
  if (cur_ecc_mode == NAND_ECC_READ)
   s3c_nand_wait_dec();
  else {
   s3c_nand_wait_enc();
   
   nfmecc0 = readl(regs + S3C_NFMECC0);
   nfmecc1 = readl(regs + S3C_NFMECC1);

   ecc_code[0] = nfmecc0 & 0xff;
   ecc_code[1] = (nfmecc0 >> 8) & 0xff;
   ecc_code[2] = (nfmecc0 >> 16) & 0xff;
   ecc_code[3] = (nfmecc0 >> 24) & 0xff;   
   ecc_code[4] = nfmecc1 & 0xff;
   ecc_code[5] = (nfmecc1 >> 8) & 0xff;
   ecc_code[6] = (nfmecc1 >> 16) & 0xff;
   ecc_code[7] = (nfmecc1 >> 24) & 0xff;
  }
 }
 
 return 0;
}

/*
 * This function determines whether read data is good or not.
 * If SLC, must write ecc codes to controller before reading status bit.
 * If MLC, status bit is already set, so only reading is needed.
 * If status bit is good, return 0.
 * If correctable errors occured, do that.
 * If uncorrectable errors occured, return -1.
 * Written by jsgood
 */
static int s3c_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
{
 int ret = -1;
 u_long nfestat0, nfestat1, nfmeccdata0, nfmeccdata1, nfmlcbitpt;
 u_char err_type;
 void __iomem *regs = s3c_nand.regs;

 if (!dat) {
  printk("No page data\n");
  return ret;
 }

 if (nand_type == S3C_NAND_TYPE_SLC) {
  /* SLC: Write ECC data to compare */
  nfmeccdata0 = (read_ecc[1] << 16) | read_ecc[0];
  nfmeccdata1 = (read_ecc[3] << 16) | read_ecc[2];
  writel(nfmeccdata0, regs + S3C_NFMECCDATA0);
  writel(nfmeccdata1, regs + S3C_NFMECCDATA1);

  /* Read ECC status */
  nfestat0 = readl(regs + S3C_NFMECCERR0);
  err_type = nfestat0 & 0x3;

  switch (err_type) {
  case 0: /* No error */
   ret = 0;
   break;

  case 1: /* 1 bit error (Correctable)
      (nfestat0 >> 7) & 0x7ff :error byte number
      (nfestat0 >> 4) & 0x7 :error bit number */
   printk("s3c-nand: 1 bit error detected at byte %ld, correcting from "
     "0x%02x ", (nfestat0 >> 7) & 0x7ff, dat[(nfestat0 >> 7) & 0x7ff]);
   dat[(nfestat0 >> 7) & 0x7ff] ^= (1 << ((nfestat0 >> 4) & 0x7));
   printk("to 0x%02x...OK\n", dat[(nfestat0 >> 7) & 0x7ff]);
   ret = 1;
   break;

  case 2: /* Multiple error */
  case 3: /* ECC area error */
   printk("s3c-nand: ECC uncorrectable error detected\n");
   ret = -1;
   break;
  }
 } else {
  /* MLC: */
  s3c_nand_wait_ecc_busy();
  
  nfestat0 = readl(regs + S3C_NFMECCERR0);
  nfestat1 = readl(regs + S3C_NFMECCERR1);
  nfmlcbitpt = readl(regs + S3C_NFMLCBITPT);

  err_type = (nfestat0 >> 26) & 0x7;

  /* No error, If free page (all 0xff) */
  if ((nfestat0 >> 29) & 0x1) {
   err_type = 0;
  } else {
   /* No error, If all 0xff from 17th byte in oob (in case of JFFS2 format) */
   if (dat) {
    if (dat[17] == 0xff && dat[26] == 0xff && dat[35] == 0xff && dat[44] == 0xff && dat[54] == 0xff)
     err_type = 0;
   }
  }

  switch (err_type) {
  case 5: /* Uncorrectable */
   printk("s3c-nand: ECC uncorrectable error detected\n");
   ret = -1;
   break;

  case 4: /* 4 bit error (Correctable) */
   dat[(nfestat1 >> 16) & 0x3ff] ^= ((nfmlcbitpt >> 24) & 0xff);

  case 3: /* 3 bit error (Correctable) */
   dat[nfestat1 & 0x3ff] ^= ((nfmlcbitpt >> 16) & 0xff);

  case 2: /* 2 bit error (Correctable) */
   dat[(nfestat0 >> 16) & 0x3ff] ^= ((nfmlcbitpt >> 8) & 0xff);

  case 1: /* 1 bit error (Correctable) */
   printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
   dat[nfestat0 & 0x3ff] ^= (nfmlcbitpt & 0xff);
   ret = err_type;
   break;

  case 0: /* No error */
   ret = 0;
   break;
  }
 }

 return ret;
}

static int s3c_nand_write_oob_1bit(struct mtd_info *mtd, struct nand_chip *chip,
         int page)
{
 uint8_t *ecc_calc = chip->buffers->ecccalc;
 int status = 0;
 int eccbytes = chip->ecc.bytes;
 int secc_start = mtd->oobsize - eccbytes;
 int i;

 chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);

 /* spare area */
 chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
 chip->write_buf(mtd, chip->oob_poi, secc_start);
 chip->ecc.calculate(mtd, 0, &ecc_calc[chip->ecc.total]);

 for (i = 0; i < eccbytes; i++)
  chip->oob_poi[secc_start + i] = ecc_calc[chip->ecc.total + i];

 chip->write_buf(mtd, chip->oob_poi + secc_start, eccbytes);

 /* Send command to program the OOB data */
 chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);

 status = chip->waitfunc(mtd, chip);

 return status & NAND_STATUS_FAIL ? -EIO : 0;
}

static int s3c_nand_read_oob_1bit(struct mtd_info *mtd, struct nand_chip *chip,
        int page, int sndcmd)
{
 uint8_t *ecc_calc = chip->buffers->ecccalc;
 int eccbytes = chip->ecc.bytes;
 int secc_start = mtd->oobsize - eccbytes;
 
 if (sndcmd) {
  chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
  sndcmd = 0;
 }

 chip->ecc.hwctl(mtd, NAND_ECC_READ);
 chip->read_buf(mtd, chip->oob_poi, secc_start);
 chip->ecc.calculate(mtd, 0, &ecc_calc[chip->ecc.total]);
 chip->read_buf(mtd, chip->oob_poi + secc_start, eccbytes);

 /* jffs2 special case */
 if (!(chip->oob_poi[2] == 0x85 && chip->oob_poi[3] == 0x19))
  chip->ecc.correct(mtd, chip->oob_poi, chip->oob_poi + secc_start, 0);
 
 return sndcmd;
}

static void s3c_nand_write_page_1bit(struct mtd_info *mtd, struct nand_chip *chip,
      const uint8_t *buf)
{
 int i, eccsize = chip->ecc.size;
 int eccbytes = chip->ecc.bytes;
 int eccsteps = chip->ecc.steps;
 int secc_start = mtd->oobsize - eccbytes;
 uint8_t *ecc_calc = chip->buffers->ecccalc;
 const uint8_t *p = buf;
 
 uint32_t *eccpos = chip->ecc.layout->eccpos;

 /* main area */
 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
  chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
  chip->write_buf(mtd, p, eccsize);
  chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 }

 for (i = 0; i < chip->ecc.total; i++)
  chip->oob_poi[eccpos[i]] = ecc_calc[i];

 /* spare area */
 chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
 chip->write_buf(mtd, chip->oob_poi, secc_start);
 chip->ecc.calculate(mtd, p, &ecc_calc[chip->ecc.total]);

 for (i = 0; i < eccbytes; i++)
  chip->oob_poi[secc_start + i] = ecc_calc[chip->ecc.total + i];

 chip->write_buf(mtd, chip->oob_poi + secc_start, eccbytes);
}

static int s3c_nand_read_page_1bit(struct mtd_info *mtd, struct nand_chip *chip,
    uint8_t *buf)
{
 int i, stat, eccsize = chip->ecc.size;
 int eccbytes = chip->ecc.bytes;
 int eccsteps = chip->ecc.steps;
 int secc_start = mtd->oobsize - eccbytes;
 int col = 0;
 uint8_t *p = buf; 
 uint32_t *mecc_pos = chip->ecc.layout->eccpos;
 uint8_t *ecc_calc = chip->buffers->ecccalc;

 col = mtd->writesize;
 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);

 /* spare area */
 chip->ecc.hwctl(mtd, NAND_ECC_READ);
 chip->read_buf(mtd, chip->oob_poi, secc_start);
 chip->ecc.calculate(mtd, p, &ecc_calc[chip->ecc.total]);
 chip->read_buf(mtd, chip->oob_poi + secc_start, eccbytes);

 /* jffs2 special case */
 if (!(chip->oob_poi[2] == 0x85 && chip->oob_poi[3] == 0x19))
  chip->ecc.correct(mtd, chip->oob_poi, chip->oob_poi + secc_start, 0);

 col = 0;

 /* main area */
 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
  chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
  chip->ecc.hwctl(mtd, NAND_ECC_READ);
  chip->read_buf(mtd, p, eccsize);
  chip->ecc.calculate(mtd, p, &ecc_calc[i]);

  stat = chip->ecc.correct(mtd, p, chip->oob_poi + mecc_pos[0] + ((chip->ecc.steps - eccsteps) * eccbytes), 0);
  if (stat == -1)
   mtd->ecc_stats.failed++;

  col = eccsize * (chip->ecc.steps + 1 - eccsteps);
 }
 
 return 0;
}

/*
 * Hardware specific page read function for MLC.
 * Written by jsgood
 */
static int s3c_nand_read_page_4bit(struct mtd_info *mtd, struct nand_chip *chip,
    uint8_t *buf)
{
 int i, stat, eccsize = chip->ecc.size;
 int eccbytes = chip->ecc.bytes;
 int eccsteps = chip->ecc.steps;
 int col = 0;
 uint8_t *p = buf; 
 uint32_t *mecc_pos = chip->ecc.layout->eccpos;

 /* Step1: read whole oob */
 col = mtd->writesize;
 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
 chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);

 col = 0;
 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
  chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
  chip->ecc.hwctl(mtd, NAND_ECC_READ);
  chip->read_buf(mtd, p, eccsize);
  chip->write_buf(mtd, chip->oob_poi + mecc_pos[0] + ((chip->ecc.steps - eccsteps) * eccbytes), eccbytes);
  chip->ecc.calculate(mtd, 0, 0);
  stat = chip->ecc.correct(mtd, p, 0, 0);

  if (stat == -1)
   mtd->ecc_stats.failed++;

  col = eccsize * (chip->ecc.steps + 1 - eccsteps);
 }

 return 0;
}

/*
 * Hardware specific page write function for MLC.
 * Written by jsgood
 */
static void s3c_nand_write_page_4bit(struct mtd_info *mtd, struct nand_chip *chip,
      const uint8_t *buf)
{
 int i, eccsize = chip->ecc.size;
 int eccbytes = chip->ecc.bytes;
 int eccsteps = chip->ecc.steps;
 const uint8_t *p = buf;
 uint8_t *ecc_calc = chip->buffers->ecccalc;
 uint32_t *mecc_pos = chip->ecc.layout->eccpos;

 /* Step1: write main data and encode mecc */
 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
  chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
  chip->write_buf(mtd, p, eccsize);
  chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 }

 /* Step2: save encoded mecc */
 for (i = 0; i < chip->ecc.total; i++)
  chip->oob_poi[mecc_pos[i]] = ecc_calc[i];

 chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
}
#endif

/* s3c_nand_probe
 *
 * called by device layer when it finds a device matching
 * one our driver can handled. This code checks to see if
 * it can allocate all necessary resources then calls the
 * nand layer to look for devices
 */
static int s3c_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type)

 struct s3c_nand_mtd_info *plat_info = pdev->dev.platform_data;
 struct mtd_partition *partition_info ;//= (struct mtd_partition *)plat_info->partition;
 struct nand_chip *nand;
 struct resource *res;
 int err = 0;
 int ret = 0;
 int i, j, size;

partition_info = (struct mtd_partition *)plat_info->partition;
#if defined(CONFIG_MTD_NAND_S3C_HWECC)
 struct nand_flash_dev *type = NULL;
 u_char tmp;
 u_char dev_id;
#endif

 /* get the clock source and enable it */

 s3c_nand.clk = clk_get(&pdev->dev, "nand");
 if (IS_ERR(s3c_nand.clk)) {
  dev_err(&pdev->dev, "failed to get clock");
  err = -ENOENT;
  goto exit_error;
 }

 clk_enable(s3c_nand.clk);

 /* allocate and map the resource */

 /* currently we assume we have the one resource */
 res  = pdev->resource;
 size = res->end - res->start + 1;

 s3c_nand.area = request_mem_region(res->start, size, pdev->name);

 if (s3c_nand.area == NULL) {
  dev_err(&pdev->dev, "cannot reserve register region\n");
  err = -ENOENT;
  goto exit_error;
 }

 s3c_nand.cpu_type   = cpu_type;
 s3c_nand.device     = &pdev->dev;
 s3c_nand.regs       = ioremap(res->start, size);

 if (s3c_nand.regs == NULL) {
  dev_err(&pdev->dev, "cannot reserve register region\n");
  err = -EIO;
  goto exit_error;
 }

 /* allocate memory for MTD device structure and private data */
 s3c_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);

 if (!s3c_mtd) {
  printk("Unable to allocate NAND MTD dev structure.\n");
  return -ENOMEM;
 }

 /* Get pointer to private data */
 nand = (struct nand_chip *) (&s3c_mtd[1]);

 /* Initialize structures */
 memset((char *) s3c_mtd, 0, sizeof(struct mtd_info));
 memset((char *) nand, 0, sizeof(struct nand_chip));

 /* Link the private data with the MTD structure */
 s3c_mtd->priv = nand;

 for (i = 0; i < plat_info->chip_nr; i++) {
  nand->IO_ADDR_R  = (char *)(s3c_nand.regs + S3C_NFDATA);
  nand->IO_ADDR_W  = (char *)(s3c_nand.regs + S3C_NFDATA);
  nand->cmd_ctrl  = s3c_nand_hwcontrol;
  nand->dev_ready  = s3c_nand_device_ready;  
  nand->scan_bbt  = s3c_nand_scan_bbt;
  nand->options  = 0;

#if defined(CONFIG_MTD_NAND_S3C_CACHEDPROG)
  nand->options  |= NAND_CACHEPRG;
#endif

#if defined(CONFIG_MTD_NAND_S3C_HWECC)
  nand->ecc.mode  = NAND_ECC_HW;
  nand->ecc.hwctl  = s3c_nand_enable_hwecc;
  nand->ecc.calculate = s3c_nand_calculate_ecc;
  nand->ecc.correct = s3c_nand_correct_data;
  
  s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
  s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
  s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);
  s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
  s3c_nand_device_ready(0);

  tmp = readb(nand->IO_ADDR_R); /* Maf. ID */
  dev_id = tmp = readb(nand->IO_ADDR_R); /* Device ID */

  for (j = 0; nand_flash_ids[j].name != NULL; j++) {
   if (tmp == nand_flash_ids[j].id) {
    type = &nand_flash_ids[j];
    break;
   }
  }

  if (!type) {
   printk("Unknown NAND Device.\n");
   goto exit_error;
  }
  
  nand->cellinfo = readb(nand->IO_ADDR_R); /* the 3rd byte */
  tmp = readb(nand->IO_ADDR_R);   /* the 4th byte */

  if (!type->pagesize) {
   if (((nand->cellinfo >> 2) & 0x3) == 0) {
    nand_type = S3C_NAND_TYPE_SLC;    
    nand->ecc.size = 512;
    nand->ecc.bytes = 4;

    if ((1024 << (tmp & 0x3)) > 512) {
     nand->ecc.read_page = s3c_nand_read_page_1bit;
     nand->ecc.write_page = s3c_nand_write_page_1bit;
     nand->ecc.read_oob = s3c_nand_read_oob_1bit;
     nand->ecc.write_oob = s3c_nand_write_oob_1bit;
     nand->ecc.layout = &s3c_nand_oob_64;
    } else {
     nand->ecc.layout = &s3c_nand_oob_16;
    }
   } else {
    nand_type = S3C_NAND_TYPE_MLC;
    nand->options |= NAND_NO_SUBPAGE_WRITE; /* NOP = 1 if MLC */
    nand->ecc.read_page = s3c_nand_read_page_4bit;
    nand->ecc.write_page = s3c_nand_write_page_4bit;
    nand->ecc.size = 512;
    nand->ecc.bytes = 8; /* really 7 bytes */
    nand->ecc.layout = &s3c_nand_oob_mlc_64;

    if((1024 << (tmp & 0x3)) > 2048)
     nand->ecc.layout = &s3c_nand_oob_mlc_128;  
   }
  } else {
/*
   nand_type = S3C_NAND_TYPE_SLC;
   nand->ecc.size = 512;
   nand->cellinfo = 0;
   nand->ecc.bytes = 4;
   nand->ecc.layout = &s3c_nand_oob_16;
*/
   nand_type = S3C_NAND_TYPE_MLC;
   nand->options |= NAND_NO_SUBPAGE_WRITE; /* NOP = 1 if MLC */
   nand->ecc.read_page = s3c_nand_read_page_4bit;
   nand->ecc.write_page = s3c_nand_write_page_4bit;
   nand->ecc.size = 512;
   nand->ecc.bytes = 8; /* really 7 bytes */
   nand->ecc.layout = &s3c_nand_oob_mlc_64;

   //jkeqiang   
//   if((1024 << (tmp & 0x3)) > 2048)
   if(dev_id == 0xd5)
   {
    printk("select s3c_nand_oob_mlc_128\n");
    nand->ecc.layout = &s3c_nand_oob_mlc_128;
   }
   else
   {
    printk("select s3c_nand_oob_mlc_64\n");
   }

  }

  printk("S3C NAND Driver is using hardware ECC.\n");
#else
  nand->ecc.mode = NAND_ECC_SOFT;
  printk("S3C NAND Driver is using software ECC.\n");
#endif
  if (nand_scan(s3c_mtd, 1)) {
   ret = -ENXIO;
   goto exit_error;
  }

  /* Register the partitions */
printk("s3c_mtd %x, partition_info %x, plat_info->mtd_part_nr %x",s3c_mtd, partition_info, plat_info->mtd_part_nr);
  add_mtd_partitions(s3c_mtd, partition_info, plat_info->mtd_part_nr);
 }

 pr_debug("initialized ok\n");
 return 0;

exit_error:
 kfree(s3c_mtd);

 return ret;
}

static int s3c2450_nand_probe(struct platform_device *dev)
{
 return s3c_nand_probe(dev, TYPE_S3C2450);
}

static int s3c6400_nand_probe(struct platform_device *dev)
{
 return s3c_nand_probe(dev, TYPE_S3C6400);
}

static int s3c6410_nand_probe(struct platform_device *dev)
{
 return s3c_nand_probe(dev, TYPE_S3C6410);
}

static int s5pc100_nand_probe(struct platform_device *dev)
{
        return s3c_nand_probe(dev, TYPE_S5PC100);
}

/* PM Support */
#if defined(CONFIG_PM)
static int s3c_nand_suspend(struct platform_device *dev, pm_message_t pm)
{
        struct s3c_nand *info = platform_get_drvdata(dev);
        clk_disable(s3c_nand.clk);
 return 0;
}

static int s3c_nand_resume(struct platform_device *dev)
{
        struct s3c_nand *info = platform_get_drvdata(dev);
        clk_enable(s3c_nand.clk);
 return 0;
}

#else
#define s3c_nand_suspend NULL
#define s3c_nand_resume NULL
#endif

/* device management functions */
static int s3c_nand_remove(struct platform_device *dev)
{
 platform_set_drvdata(dev, NULL);

 return 0;
}

static struct platform_driver s3c2450_nand_driver = {
 .probe  = s3c2450_nand_probe,
 .remove  = s3c_nand_remove,
 .suspend = s3c_nand_suspend,
 .resume  = s3c_nand_resume,
 .driver  = {
  .name = "s3c2450-nand",
  .owner = THIS_MODULE,
 },
};

static struct platform_driver s3c6400_nand_driver = {
 .probe  = s3c6400_nand_probe,
 .remove  = s3c_nand_remove,
 .suspend = s3c_nand_suspend,
 .resume  = s3c_nand_resume,
 .driver  = {
  .name = "s3c6400-nand",
  .owner = THIS_MODULE,
 },
};

static struct platform_driver s3c6410_nand_driver = {
 .probe  = s3c6410_nand_probe,
 .remove  = s3c_nand_remove,
 .suspend = s3c_nand_suspend,
 .resume  = s3c_nand_resume,
 .driver  = {
  .name = "s3c6410-nand",
  .owner = THIS_MODULE,
 },
};

static struct platform_driver s5pc100_nand_driver = {
        .probe          = s5pc100_nand_probe,
        .remove         = s3c_nand_remove,
        .suspend        = s3c_nand_suspend,
        .resume         = s3c_nand_resume,
        .driver         = {
                .name   = "s5pc100-nand",
                .owner  = THIS_MODULE,
        },
};

static int __init s3c_nand_init(void)
{
 printk("S3C NAND Driver, (c) 2008 Samsung Electronics\n");

 platform_driver_register(&s3c2450_nand_driver);
 platform_driver_register(&s3c6400_nand_driver);
        platform_driver_register(&s3c6410_nand_driver);
        return platform_driver_register(&s5pc100_nand_driver);
}

static void __exit s3c_nand_exit(void)
{
 platform_driver_unregister(&s3c2450_nand_driver);
 platform_driver_unregister(&s3c6400_nand_driver);
 platform_driver_unregister(&s3c6410_nand_driver);
 platform_driver_unregister(&s5pc100_nand_driver);
}

module_init(s3c_nand_init);
module_exit(s3c_nand_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jinsung Yang <>");
MODULE_DESCRIPTION("S3C MTD NAND driver");

阅读(1155) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~