Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
authorDavid Woodhouse <dwmw2@infradead.org>
Fri, 1 Dec 2006 09:56:43 +0000 (09:56 +0000)
committerDavid Woodhouse <dwmw2@infradead.org>
Fri, 1 Dec 2006 09:56:43 +0000 (09:56 +0000)
1  2 
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/nand/nand_base.c
include/linux/mtd/nand.h

index 11de545355266d9a0fff04248e6dbd23d7b30ec0,296159ec5189eafc0e6c68d118bde8772725ac95..f69184a92eb2389d7370d726a346602c117d51dd
@@@ -337,11 -337,12 +337,11 @@@ struct mtd_info *cfi_cmdset_0001(struc
        struct mtd_info *mtd;
        int i;
  
 -      mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
 +      mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
        if (!mtd) {
                printk(KERN_ERR "Failed to allocate memory for MTD device\n");
                return NULL;
        }
 -      memset(mtd, 0, sizeof(*mtd));
        mtd->priv = map;
        mtd->type = MTD_NORFLASH;
  
@@@ -1086,7 -1087,7 +1086,7 @@@ static int inval_cache_and_wait_for_ope
                }
                spin_lock(chip->mutex);
  
-               if (chip->state != chip_state) {
+               while (chip->state != chip_state) {
                        /* Someone's suspended the operation: sleep */
                        DECLARE_WAITQUEUE(wait, current);
                        set_current_state(TASK_UNINTERRUPTIBLE);
@@@ -2223,8 -2224,6 +2223,8 @@@ static int cfi_intelext_suspend(struct 
                case FL_CFI_QUERY:
                case FL_JEDEC_QUERY:
                        if (chip->oldstate == FL_READY) {
 +                              /* place the chip in a known state before suspend */
 +                              map_write(map, CMD(0xFF), cfi->chips[i].start);
                                chip->oldstate = chip->state;
                                chip->state = FL_PM_SUSPENDED;
                                /* No need to wake_up() on this state change -
index eed3271b99cc2e7ccfc81b091011194089b8fb00,41bfcae1fbf46ebbfb8aa2457efaf9f7d1723529..dfe56e03e48b0bf9ca9bfa89fc4b11e5e15d4dc6
@@@ -362,7 -362,7 +362,7 @@@ static int nand_default_block_markbad(s
                 * access
                 */
                ofs += mtd->oobsize;
 -              chip->ops.len = 2;
 +              chip->ops.len = chip->ops.ooblen = 2;
                chip->ops.datbuf = NULL;
                chip->ops.oobbuf = buf;
                chip->ops.ooboffs = chip->badblockpos & ~0x01;
@@@ -755,7 -755,7 +755,7 @@@ static int nand_read_page_raw(struct mt
  }
  
  /**
 - * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
 + * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
   * @mtd:      mtd info structure
   * @chip:     nand chip info structure
   * @buf:      buffer to store read data
@@@ -795,7 -795,7 +795,7 @@@ static int nand_read_page_swecc(struct 
  }
  
  /**
 - * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function
 + * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
   * @mtd:      mtd info structure
   * @chip:     nand chip info structure
   * @buf:      buffer to store read data
@@@ -839,7 -839,7 +839,7 @@@ static int nand_read_page_hwecc(struct 
  }
  
  /**
 - * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
 + * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
   * @mtd:      mtd info structure
   * @chip:     nand chip info structure
   * @buf:      buffer to store read data
@@@ -897,11 -897,12 +897,11 @@@ static int nand_read_page_syndrome(stru
   * @chip:     nand chip structure
   * @oob:      oob destination address
   * @ops:      oob ops structure
 + * @len:      size of oob to transfer
   */
  static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
 -                                struct mtd_oob_ops *ops)
 +                                struct mtd_oob_ops *ops, size_t len)
  {
 -      size_t len = ops->ooblen;
 -
        switch(ops->mode) {
  
        case MTD_OOB_PLACE:
@@@ -959,7 -960,6 +959,7 @@@ static int nand_do_read_ops(struct mtd_
        int sndcmd = 1;
        int ret = 0;
        uint32_t readlen = ops->len;
 +      uint32_t oobreadlen = ops->ooblen;
        uint8_t *bufpoi, *oob, *buf;
  
        stats = mtd->ecc_stats;
        page = realpage & chip->pagemask;
  
        col = (int)(from & (mtd->writesize - 1));
 -      chip->oob_poi = chip->buffers->oobrbuf;
  
        buf = ops->datbuf;
        oob = ops->oobbuf;
  
                        if (unlikely(oob)) {
                                /* Raw mode does data:oob:data:oob */
 -                              if (ops->mode != MTD_OOB_RAW)
 -                                      oob = nand_transfer_oob(chip, oob, ops);
 -                              else
 -                                      buf = nand_transfer_oob(chip, buf, ops);
 +                              if (ops->mode != MTD_OOB_RAW) {
 +                                      int toread = min(oobreadlen,
 +                                              chip->ecc.layout->oobavail);
 +                                      if (toread) {
 +                                              oob = nand_transfer_oob(chip,
 +                                                      oob, ops, toread);
 +                                              oobreadlen -= toread;
 +                                      }
 +                              } else
 +                                      buf = nand_transfer_oob(chip,
 +                                              buf, ops, mtd->oobsize);
                        }
  
                        if (!(chip->options & NAND_NO_READRDY)) {
        }
  
        ops->retlen = ops->len - (size_t) readlen;
 +      if (oob)
 +              ops->oobretlen = ops->ooblen - oobreadlen;
  
        if (ret)
                return ret;
@@@ -1265,18 -1257,12 +1265,18 @@@ static int nand_do_read_oob(struct mtd_
        int page, realpage, chipnr, sndcmd = 1;
        struct nand_chip *chip = mtd->priv;
        int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
 -      int readlen = ops->len;
 +      int readlen = ops->ooblen;
 +      int len;
        uint8_t *buf = ops->oobbuf;
  
        DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
              (unsigned long long)from, readlen);
  
 +      if (ops->mode == MTD_OOB_RAW)
 +              len = mtd->oobsize;
 +      else
 +              len = chip->ecc.layout->oobavail;
 +
        chipnr = (int)(from >> chip->chip_shift);
        chip->select_chip(mtd, chipnr);
  
        realpage = (int)(from >> chip->page_shift);
        page = realpage & chip->pagemask;
  
 -      chip->oob_poi = chip->buffers->oobrbuf;
 -
        while(1) {
                sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
 -              buf = nand_transfer_oob(chip, buf, ops);
 +
 +              len = min(len, readlen);
 +              buf = nand_transfer_oob(chip, buf, ops, len);
  
                if (!(chip->options & NAND_NO_READRDY)) {
                        /*
                                nand_wait_ready(mtd);
                }
  
 -              readlen -= ops->ooblen;
 +              readlen -= len;
                if (!readlen)
                        break;
  
                        sndcmd = 1;
        }
  
 -      ops->retlen = ops->len;
 +      ops->oobretlen = ops->ooblen;
        return 0;
  }
  
@@@ -1346,7 -1332,7 +1346,7 @@@ static int nand_read_oob(struct mtd_inf
        ops->retlen = 0;
  
        /* Do not allow reads past end of device */
 -      if ((from + ops->len) > mtd->size) {
 +      if (ops->datbuf && (from + ops->len) > mtd->size) {
                DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
                      "Attempt read beyond end of device\n");
                return -EINVAL;
@@@ -1389,7 -1375,7 +1389,7 @@@ static void nand_write_page_raw(struct 
  }
  
  /**
 - * nand_write_page_swecc - {REPLACABLE] software ecc based page write function
 + * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
   * @mtd:      mtd info structure
   * @chip:     nand chip info structure
   * @buf:      data buffer
@@@ -1415,7 -1401,7 +1415,7 @@@ static void nand_write_page_swecc(struc
  }
  
  /**
 - * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function
 + * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
   * @mtd:      mtd info structure
   * @chip:     nand chip info structure
   * @buf:      data buffer
@@@ -1443,7 -1429,7 +1443,7 @@@ static void nand_write_page_hwecc(struc
  }
  
  /**
 - * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write
 + * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
   * @mtd:      mtd info structure
   * @chip:     nand chip info structure
   * @buf:      data buffer
@@@ -1493,6 -1479,7 +1493,7 @@@ static void nand_write_page_syndrome(st
   * @buf:      the data to write
   * @page:     page number to write
   * @cached:   cached programming
+  * @raw:      use _raw version of write_page
   */
  static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
                           const uint8_t *buf, int page, int cached, int raw)
@@@ -1590,7 -1577,7 +1591,7 @@@ static uint8_t *nand_fill_oob(struct na
        return NULL;
  }
  
 -#define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
 +#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
  
  /**
   * nand_do_write_ops - [Internal] NAND write with ECC
  static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                             struct mtd_oob_ops *ops)
  {
 -      int chipnr, realpage, page, blockmask;
 +      int chipnr, realpage, page, blockmask, column;
        struct nand_chip *chip = mtd->priv;
        uint32_t writelen = ops->len;
        uint8_t *oob = ops->oobbuf;
        uint8_t *buf = ops->datbuf;
 -      int bytes = mtd->writesize;
 -      int ret;
 +      int ret, subpage;
  
        ops->retlen = 0;
 +      if (!writelen)
 +              return 0;
  
        /* reject writes, which are not page aligned */
        if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
                return -EINVAL;
        }
  
 -      if (!writelen)
 -              return 0;
 +      column = to & (mtd->writesize - 1);
 +      subpage = column || (writelen & (mtd->writesize - 1));
 +
 +      if (subpage && oob)
 +              return -EINVAL;
  
        chipnr = (int)(to >> chip->chip_shift);
        chip->select_chip(mtd, chipnr);
            (chip->pagebuf << chip->page_shift) < (to + ops->len))
                chip->pagebuf = -1;
  
 -      chip->oob_poi = chip->buffers->oobwbuf;
 +      /* If we're not given explicit OOB data, let it be 0xFF */
 +      if (likely(!oob))
 +              memset(chip->oob_poi, 0xff, mtd->oobsize);
  
        while(1) {
 +              int bytes = mtd->writesize;
                int cached = writelen > bytes && page != blockmask;
 +              uint8_t *wbuf = buf;
 +
 +              /* Partial page write ? */
 +              if (unlikely(column || writelen < (mtd->writesize - 1))) {
 +                      cached = 0;
 +                      bytes = min_t(int, bytes - column, (int) writelen);
 +                      chip->pagebuf = -1;
 +                      memset(chip->buffers->databuf, 0xff, mtd->writesize);
 +                      memcpy(&chip->buffers->databuf[column], buf, bytes);
 +                      wbuf = chip->buffers->databuf;
 +              }
  
                if (unlikely(oob))
                        oob = nand_fill_oob(chip, oob, ops);
  
 -              ret = chip->write_page(mtd, chip, buf, page, cached,
 +              ret = chip->write_page(mtd, chip, wbuf, page, cached,
                                       (ops->mode == MTD_OOB_RAW));
                if (ret)
                        break;
                if (!writelen)
                        break;
  
 +              column = 0;
                buf += bytes;
                realpage++;
  
                }
        }
  
 -      if (unlikely(oob))
 -              memset(chip->oob_poi, 0xff, mtd->oobsize);
 -
        ops->retlen = ops->len - writelen;
 +      if (unlikely(oob))
 +              ops->oobretlen = ops->ooblen;
        return ret;
  }
  
@@@ -1745,10 -1714,10 +1746,10 @@@ static int nand_do_write_oob(struct mtd
        struct nand_chip *chip = mtd->priv;
  
        DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
 -            (unsigned int)to, (int)ops->len);
 +            (unsigned int)to, (int)ops->ooblen);
  
        /* Do not allow write past end of page */
 -      if ((ops->ooboffs + ops->len) > mtd->oobsize) {
 +      if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) {
                DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
                      "Attempt to write past end of page\n");
                return -EINVAL;
        if (page == chip->pagebuf)
                chip->pagebuf = -1;
  
 -      chip->oob_poi = chip->buffers->oobwbuf;
        memset(chip->oob_poi, 0xff, mtd->oobsize);
        nand_fill_oob(chip, ops->oobbuf, ops);
        status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
        if (status)
                return status;
  
 -      ops->retlen = ops->len;
 +      ops->oobretlen = ops->ooblen;
  
        return 0;
  }
@@@ -1804,7 -1774,7 +1805,7 @@@ static int nand_write_oob(struct mtd_in
        ops->retlen = 0;
  
        /* Do not allow writes past end of device */
 -      if ((to + ops->len) > mtd->size) {
 +      if (ops->datbuf && (to + ops->len) > mtd->size) {
                DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
                      "Attempt read beyond end of device\n");
                return -EINVAL;
@@@ -2218,8 -2188,8 +2219,8 @@@ static struct nand_flash_dev *nand_get_
        /* Newer devices have all the information in additional id bytes */
        if (!type->pagesize) {
                int extid;
 -              /* The 3rd id byte contains non relevant data ATM */
 -              extid = chip->read_byte(mtd);
 +              /* The 3rd id byte holds MLC / multichip data */
 +              chip->cellinfo = chip->read_byte(mtd);
                /* The 4th id byte is the important one */
                extid = chip->read_byte(mtd);
                /* Calc pagesize */
@@@ -2379,8 -2349,8 +2380,8 @@@ int nand_scan_tail(struct mtd_info *mtd
        if (!chip->buffers)
                return -ENOMEM;
  
 -      /* Preset the internal oob write buffer */
 -      memset(chip->buffers->oobwbuf, 0xff, mtd->oobsize);
 +      /* Set the internal oob buffer location, just after the page data */
 +      chip->oob_poi = chip->buffers->databuf + mtd->writesize;
  
        /*
         * If no default placement scheme is given, select an appropriate one
        }
        chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
  
 +      /*
 +       * Allow subpage writes up to ecc.steps. Not possible for MLC
 +       * FLASH.
 +       */
 +      if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
 +          !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
 +              switch(chip->ecc.steps) {
 +              case 2:
 +                      mtd->subpage_sft = 1;
 +                      break;
 +              case 4:
 +              case 8:
 +                      mtd->subpage_sft = 2;
 +                      break;
 +              }
 +      }
 +      chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
 +
        /* Initialize state */
        chip->state = FL_READY;
  
diff --combined include/linux/mtd/nand.h
index 1aeedf27a1ff29571c74f7da50fdaa8dc5c3941a,8b3ef418721980542adccfb4feb8cc63f82410af..2071b02f0526f6507d9355015939f09029a51586
@@@ -166,9 -166,6 +166,9 @@@ typedef enum 
   * for all large page devices, as they do not support
   * autoincrement.*/
  #define NAND_NO_READRDY               0x00000100
 +/* Chip does not allow subpage writes */
 +#define NAND_NO_SUBPAGE_WRITE 0x00000200
 +
  
  /* Options valid for Samsung large page devices */
  #define NAND_SAMSUNG_LP_OPTIONS \
  /* Nand scan has allocated controller struct */
  #define NAND_CONTROLLER_ALLOC 0x80000000
  
 +/* Cell info constants */
 +#define NAND_CI_CHIPNR_MSK    0x03
 +#define NAND_CI_CELLTYPE_MSK  0x0C
  
  /*
   * nand_state_t - chip states
@@@ -292,7 -286,9 +292,7 @@@ struct nand_ecc_ctrl 
   * struct nand_buffers - buffer structure for read/write
   * @ecccalc:  buffer for calculated ecc
   * @ecccode:  buffer for ecc read from flash
 - * @oobwbuf:  buffer for write oob data
   * @databuf:  buffer for data - dynamically sized
 - * @oobrbuf:  buffer to read oob data
   *
   * Do not change the order of buffers. databuf and oobrbuf must be in
   * consecutive order.
  struct nand_buffers {
        uint8_t ecccalc[NAND_MAX_OOBSIZE];
        uint8_t ecccode[NAND_MAX_OOBSIZE];
 -      uint8_t oobwbuf[NAND_MAX_OOBSIZE];
 -      uint8_t databuf[NAND_MAX_PAGESIZE];
 -      uint8_t oobrbuf[NAND_MAX_OOBSIZE];
 +      uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];
  };
  
  /**
   * @chipsize:         [INTERN] the size of one chip for multichip arrays
   * @pagemask:         [INTERN] page number mask = number of (pages / chip) - 1
   * @pagebuf:          [INTERN] holds the pagenumber which is currently in data_buf
 + * @subpagesize:      [INTERN] holds the subpagesize
   * @ecclayout:                [REPLACEABLE] the default ecc placement scheme
   * @bbt:              [INTERN] bad block table pointer
   * @bbt_td:           [REPLACEABLE] bad block table descriptor for flash lookup
   * @priv:             [OPTIONAL] pointer to private chip date
   * @errstat:          [OPTIONAL] hardware specific function to perform additional error status checks
   *                    (determine if errors are correctable)
-  * @write_page                [REPLACEABLE] High-level page write function
+  * @write_page:               [REPLACEABLE] High-level page write function
   */
  
  struct nand_chip {
        unsigned long   chipsize;
        int             pagemask;
        int             pagebuf;
 +      int             subpagesize;
 +      uint8_t         cellinfo;
        int             badblockpos;
  
        nand_state_t    state;