|
@@ -115,6 +115,35 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|
|
|
|
|
static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);
|
|
static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);
|
|
|
|
|
|
|
|
+static int check_offs_len(struct mtd_info *mtd,
|
|
|
|
+ loff_t ofs, uint64_t len)
|
|
|
|
+{
|
|
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ /* Start address must align on block boundary */
|
|
|
|
+ if (ofs & ((1 << chip->phys_erase_shift) - 1)) {
|
|
|
|
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Length must align on block boundary */
|
|
|
|
+ if (len & ((1 << chip->phys_erase_shift) - 1)) {
|
|
|
|
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
|
|
|
|
+ __func__);
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Do not allow past end of device */
|
|
|
|
+ if (ofs + len > mtd->size) {
|
|
|
|
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n",
|
|
|
|
+ __func__);
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* nand_release_device - [GENERIC] release chip
|
|
* nand_release_device - [GENERIC] release chip
|
|
* @mtd: MTD device structure
|
|
* @mtd: MTD device structure
|
|
@@ -123,8 +152,10 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);
|
|
*/
|
|
*/
|
|
static void nand_release_device(struct mtd_info *mtd)
|
|
static void nand_release_device(struct mtd_info *mtd)
|
|
{
|
|
{
|
|
- struct nand_chip *this = mtd->priv;
|
|
|
|
- this->select_chip(mtd, -1); /* De-select the NAND device */
|
|
|
|
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
|
|
+
|
|
|
|
+ /* De-select the NAND device */
|
|
|
|
+ chip->select_chip(mtd, -1);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -316,6 +347,9 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
|
struct nand_chip *chip = mtd->priv;
|
|
struct nand_chip *chip = mtd->priv;
|
|
u16 bad;
|
|
u16 bad;
|
|
|
|
|
|
|
|
+ if (chip->options & NAND_BBT_SCANLASTPAGE)
|
|
|
|
+ ofs += mtd->erasesize - mtd->writesize;
|
|
|
|
+
|
|
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
|
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
|
|
|
|
|
if (getchip) {
|
|
if (getchip) {
|
|
@@ -333,14 +367,18 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
|
bad = cpu_to_le16(chip->read_word(mtd));
|
|
bad = cpu_to_le16(chip->read_word(mtd));
|
|
if (chip->badblockpos & 0x1)
|
|
if (chip->badblockpos & 0x1)
|
|
bad >>= 8;
|
|
bad >>= 8;
|
|
- if ((bad & 0xFF) != 0xff)
|
|
|
|
- res = 1;
|
|
|
|
|
|
+ else
|
|
|
|
+ bad &= 0xFF;
|
|
} else {
|
|
} else {
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
|
|
- if (chip->read_byte(mtd) != 0xff)
|
|
|
|
- res = 1;
|
|
|
|
|
|
+ bad = chip->read_byte(mtd);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (likely(chip->badblockbits == 8))
|
|
|
|
+ res = bad != 0xFF;
|
|
|
|
+ else
|
|
|
|
+ res = hweight8(bad) < chip->badblockbits;
|
|
|
|
+
|
|
if (getchip)
|
|
if (getchip)
|
|
nand_release_device(mtd);
|
|
nand_release_device(mtd);
|
|
|
|
|
|
@@ -359,7 +397,10 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
{
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
struct nand_chip *chip = mtd->priv;
|
|
uint8_t buf[2] = { 0, 0 };
|
|
uint8_t buf[2] = { 0, 0 };
|
|
- int block, ret;
|
|
|
|
|
|
+ int block, ret, i = 0;
|
|
|
|
+
|
|
|
|
+ if (chip->options & NAND_BBT_SCANLASTPAGE)
|
|
|
|
+ ofs += mtd->erasesize - mtd->writesize;
|
|
|
|
|
|
/* Get block number */
|
|
/* Get block number */
|
|
block = (int)(ofs >> chip->bbt_erase_shift);
|
|
block = (int)(ofs >> chip->bbt_erase_shift);
|
|
@@ -370,17 +411,31 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
if (chip->options & NAND_USE_FLASH_BBT)
|
|
if (chip->options & NAND_USE_FLASH_BBT)
|
|
ret = nand_update_bbt(mtd, ofs);
|
|
ret = nand_update_bbt(mtd, ofs);
|
|
else {
|
|
else {
|
|
- /* We write two bytes, so we dont have to mess with 16 bit
|
|
|
|
- * access
|
|
|
|
- */
|
|
|
|
nand_get_device(chip, mtd, FL_WRITING);
|
|
nand_get_device(chip, mtd, FL_WRITING);
|
|
- ofs += mtd->oobsize;
|
|
|
|
- chip->ops.len = chip->ops.ooblen = 2;
|
|
|
|
- chip->ops.datbuf = NULL;
|
|
|
|
- chip->ops.oobbuf = buf;
|
|
|
|
- chip->ops.ooboffs = chip->badblockpos & ~0x01;
|
|
|
|
|
|
|
|
- ret = nand_do_write_oob(mtd, ofs, &chip->ops);
|
|
|
|
|
|
+ /* Write to first two pages and to byte 1 and 6 if necessary.
|
|
|
|
+ * If we write to more than one location, the first error
|
|
|
|
+ * encountered quits the procedure. We write two bytes per
|
|
|
|
+ * location, so we dont have to mess with 16 bit access.
|
|
|
|
+ */
|
|
|
|
+ do {
|
|
|
|
+ chip->ops.len = chip->ops.ooblen = 2;
|
|
|
|
+ chip->ops.datbuf = NULL;
|
|
|
|
+ chip->ops.oobbuf = buf;
|
|
|
|
+ chip->ops.ooboffs = chip->badblockpos & ~0x01;
|
|
|
|
+
|
|
|
|
+ ret = nand_do_write_oob(mtd, ofs, &chip->ops);
|
|
|
|
+
|
|
|
|
+ if (!ret && (chip->options & NAND_BBT_SCANBYTE1AND6)) {
|
|
|
|
+ chip->ops.ooboffs = NAND_SMALL_BADBLOCK_POS
|
|
|
|
+ & ~0x01;
|
|
|
|
+ ret = nand_do_write_oob(mtd, ofs, &chip->ops);
|
|
|
|
+ }
|
|
|
|
+ i++;
|
|
|
|
+ ofs += mtd->writesize;
|
|
|
|
+ } while (!ret && (chip->options & NAND_BBT_SCAN2NDPAGE) &&
|
|
|
|
+ i < 2);
|
|
|
|
+
|
|
nand_release_device(mtd);
|
|
nand_release_device(mtd);
|
|
}
|
|
}
|
|
if (!ret)
|
|
if (!ret)
|
|
@@ -399,6 +454,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
static int nand_check_wp(struct mtd_info *mtd)
|
|
static int nand_check_wp(struct mtd_info *mtd)
|
|
{
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
+
|
|
|
|
+ /* broken xD cards report WP despite being writable */
|
|
|
|
+ if (chip->options & NAND_BROKEN_XD)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
/* Check the WP bit */
|
|
/* Check the WP bit */
|
|
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
|
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
|
return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
|
|
return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
|
|
@@ -419,11 +479,6 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
|
|
{
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
|
|
- if (!(chip->options & NAND_BBT_SCANNED)) {
|
|
|
|
- chip->options |= NAND_BBT_SCANNED;
|
|
|
|
- chip->scan_bbt(mtd);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if (!chip->bbt)
|
|
if (!chip->bbt)
|
|
return chip->block_bad(mtd, ofs, getchip);
|
|
return chip->block_bad(mtd, ofs, getchip);
|
|
|
|
|
|
@@ -686,9 +741,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|
*
|
|
*
|
|
* Get the device and lock it for exclusive access
|
|
* Get the device and lock it for exclusive access
|
|
*/
|
|
*/
|
|
-static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
|
|
|
|
|
|
+static int
|
|
|
|
+nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
|
|
{
|
|
{
|
|
- this->state = new_state;
|
|
|
|
|
|
+ chip->state = new_state;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -701,10 +757,10 @@ static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int ne
|
|
* Erase can take up to 400ms and program up to 20ms according to
|
|
* Erase can take up to 400ms and program up to 20ms according to
|
|
* general NAND and SmartMedia specs
|
|
* general NAND and SmartMedia specs
|
|
*/
|
|
*/
|
|
-static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)
|
|
|
|
|
|
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
|
{
|
|
{
|
|
unsigned long timeo;
|
|
unsigned long timeo;
|
|
- int state = this->state;
|
|
|
|
|
|
+ int state = chip->state;
|
|
u32 time_start;
|
|
u32 time_start;
|
|
|
|
|
|
if (state == FL_ERASING)
|
|
if (state == FL_ERASING)
|
|
@@ -712,10 +768,10 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)
|
|
else
|
|
else
|
|
timeo = (CONFIG_SYS_HZ * 20) / 1000;
|
|
timeo = (CONFIG_SYS_HZ * 20) / 1000;
|
|
|
|
|
|
- if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
|
|
|
|
- this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
|
|
|
|
|
|
+ if ((state == FL_ERASING) && (chip->options & NAND_IS_AND))
|
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
|
|
else
|
|
else
|
|
- this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
|
|
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
|
|
|
|
|
time_start = get_timer(0);
|
|
time_start = get_timer(0);
|
|
|
|
|
|
@@ -725,11 +781,11 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)
|
|
return 0x01;
|
|
return 0x01;
|
|
}
|
|
}
|
|
|
|
|
|
- if (this->dev_ready) {
|
|
|
|
- if (this->dev_ready(mtd))
|
|
|
|
|
|
+ if (chip->dev_ready) {
|
|
|
|
+ if (chip->dev_ready(mtd))
|
|
break;
|
|
break;
|
|
} else {
|
|
} else {
|
|
- if (this->read_byte(mtd) & NAND_STATUS_READY)
|
|
|
|
|
|
+ if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -739,7 +795,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)
|
|
;
|
|
;
|
|
#endif /* PPCHAMELON_NAND_TIMER_HACK */
|
|
#endif /* PPCHAMELON_NAND_TIMER_HACK */
|
|
|
|
|
|
- return this->read_byte(mtd);
|
|
|
|
|
|
+ return (int)chip->read_byte(mtd);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -860,6 +916,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|
int data_col_addr, i, gaps = 0;
|
|
int data_col_addr, i, gaps = 0;
|
|
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
|
|
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
|
|
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
|
|
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
|
|
|
|
+ int index = 0;
|
|
|
|
|
|
/* Column address wihin the page aligned to ECC size (256bytes). */
|
|
/* Column address wihin the page aligned to ECC size (256bytes). */
|
|
start_step = data_offs / chip->ecc.size;
|
|
start_step = data_offs / chip->ecc.size;
|
|
@@ -898,26 +955,30 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|
} else {
|
|
} else {
|
|
/* send the command to read the particular ecc bytes */
|
|
/* send the command to read the particular ecc bytes */
|
|
/* take care about buswidth alignment in read_buf */
|
|
/* take care about buswidth alignment in read_buf */
|
|
- aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
|
|
|
|
|
|
+ index = start_step * chip->ecc.bytes;
|
|
|
|
+
|
|
|
|
+ aligned_pos = eccpos[index] & ~(busw - 1);
|
|
aligned_len = eccfrag_len;
|
|
aligned_len = eccfrag_len;
|
|
- if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
|
|
|
|
|
|
+ if (eccpos[index] & (busw - 1))
|
|
aligned_len++;
|
|
aligned_len++;
|
|
- if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
|
|
|
|
|
|
+ if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
|
|
aligned_len++;
|
|
aligned_len++;
|
|
|
|
|
|
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
|
|
|
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
|
|
|
+ mtd->writesize + aligned_pos, -1);
|
|
chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
|
|
chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
|
|
}
|
|
}
|
|
|
|
|
|
for (i = 0; i < eccfrag_len; i++)
|
|
for (i = 0; i < eccfrag_len; i++)
|
|
- chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
|
|
|
|
|
|
+ chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
|
|
|
|
|
|
p = bufpoi + data_col_addr;
|
|
p = bufpoi + data_col_addr;
|
|
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
|
|
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
|
|
int stat;
|
|
int stat;
|
|
|
|
|
|
- stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
|
|
|
|
- if (stat == -1)
|
|
|
|
|
|
+ stat = chip->ecc.correct(mtd, p,
|
|
|
|
+ &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
|
|
|
|
+ if (stat < 0)
|
|
mtd->ecc_stats.failed++;
|
|
mtd->ecc_stats.failed++;
|
|
else
|
|
else
|
|
mtd->ecc_stats.corrected += stat;
|
|
mtd->ecc_stats.corrected += stat;
|
|
@@ -1142,6 +1203,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|
int ret = 0;
|
|
int ret = 0;
|
|
uint32_t readlen = ops->len;
|
|
uint32_t readlen = ops->len;
|
|
uint32_t oobreadlen = ops->ooblen;
|
|
uint32_t oobreadlen = ops->ooblen;
|
|
|
|
+ uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ?
|
|
|
|
+ mtd->oobavail : mtd->oobsize;
|
|
|
|
+
|
|
uint8_t *bufpoi, *oob, *buf;
|
|
uint8_t *bufpoi, *oob, *buf;
|
|
|
|
|
|
stats = mtd->ecc_stats;
|
|
stats = mtd->ecc_stats;
|
|
@@ -1187,7 +1251,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|
|
|
|
|
/* Transfer not aligned data */
|
|
/* Transfer not aligned data */
|
|
if (!aligned) {
|
|
if (!aligned) {
|
|
- if (!NAND_SUBPAGE_READ(chip) && !oob)
|
|
|
|
|
|
+ if (!NAND_SUBPAGE_READ(chip) && !oob &&
|
|
|
|
+ !(mtd->ecc_stats.failed - stats.failed))
|
|
chip->pagebuf = realpage;
|
|
chip->pagebuf = realpage;
|
|
memcpy(buf, chip->buffers->databuf + col, bytes);
|
|
memcpy(buf, chip->buffers->databuf + col, bytes);
|
|
}
|
|
}
|
|
@@ -1195,18 +1260,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|
buf += bytes;
|
|
buf += bytes;
|
|
|
|
|
|
if (unlikely(oob)) {
|
|
if (unlikely(oob)) {
|
|
- /* Raw mode does data:oob:data:oob */
|
|
|
|
- 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);
|
|
|
|
|
|
+
|
|
|
|
+ int toread = min(oobreadlen, max_oobsize);
|
|
|
|
+
|
|
|
|
+ if (toread) {
|
|
|
|
+ oob = nand_transfer_oob(chip,
|
|
|
|
+ oob, ops, toread);
|
|
|
|
+ oobreadlen -= toread;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (!(chip->options & NAND_NO_READRDY)) {
|
|
if (!(chip->options & NAND_NO_READRDY)) {
|
|
@@ -1793,13 +1854,12 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
* nand_fill_oob - [Internal] Transfer client buffer to oob
|
|
* nand_fill_oob - [Internal] Transfer client buffer to oob
|
|
* @chip: nand chip structure
|
|
* @chip: nand chip structure
|
|
* @oob: oob data buffer
|
|
* @oob: oob data buffer
|
|
|
|
+ * @len: oob data write length
|
|
* @ops: oob ops structure
|
|
* @ops: oob ops structure
|
|
*/
|
|
*/
|
|
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
|
|
|
|
- struct mtd_oob_ops *ops)
|
|
|
|
|
|
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
|
|
|
|
+ struct mtd_oob_ops *ops)
|
|
{
|
|
{
|
|
- size_t len = ops->ooblen;
|
|
|
|
-
|
|
|
|
switch (ops->mode) {
|
|
switch (ops->mode) {
|
|
|
|
|
|
case MTD_OOB_PLACE:
|
|
case MTD_OOB_PLACE:
|
|
@@ -1838,7 +1898,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
-#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
|
|
|
|
|
|
+#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
|
|
|
|
|
|
/**
|
|
/**
|
|
* nand_do_write_ops - [Internal] NAND write with ECC
|
|
* nand_do_write_ops - [Internal] NAND write with ECC
|
|
@@ -1854,6 +1914,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|
int chipnr, realpage, page, blockmask, column;
|
|
int chipnr, realpage, page, blockmask, column;
|
|
struct nand_chip *chip = mtd->priv;
|
|
struct nand_chip *chip = mtd->priv;
|
|
uint32_t writelen = ops->len;
|
|
uint32_t writelen = ops->len;
|
|
|
|
+
|
|
|
|
+ uint32_t oobwritelen = ops->ooblen;
|
|
|
|
+ uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ?
|
|
|
|
+ mtd->oobavail : mtd->oobsize;
|
|
|
|
+
|
|
uint8_t *oob = ops->oobbuf;
|
|
uint8_t *oob = ops->oobbuf;
|
|
uint8_t *buf = ops->datbuf;
|
|
uint8_t *buf = ops->datbuf;
|
|
int ret, subpage;
|
|
int ret, subpage;
|
|
@@ -1890,6 +1955,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|
if (likely(!oob))
|
|
if (likely(!oob))
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
|
|
|
|
|
|
|
+ /* Don't allow multipage oob writes with offset */
|
|
|
|
+ if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
while (1) {
|
|
while (1) {
|
|
WATCHDOG_RESET();
|
|
WATCHDOG_RESET();
|
|
|
|
|
|
@@ -1907,8 +1976,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|
wbuf = chip->buffers->databuf;
|
|
wbuf = chip->buffers->databuf;
|
|
}
|
|
}
|
|
|
|
|
|
- if (unlikely(oob))
|
|
|
|
- oob = nand_fill_oob(chip, oob, ops);
|
|
|
|
|
|
+ if (unlikely(oob)) {
|
|
|
|
+ size_t len = min(oobwritelen, oobmaxlen);
|
|
|
|
+ oob = nand_fill_oob(chip, oob, len, ops);
|
|
|
|
+ oobwritelen -= len;
|
|
|
|
+ }
|
|
|
|
|
|
ret = chip->write_page(mtd, chip, wbuf, page, cached,
|
|
ret = chip->write_page(mtd, chip, wbuf, page, cached,
|
|
(ops->mode == MTD_OOB_RAW));
|
|
(ops->mode == MTD_OOB_RAW));
|
|
@@ -2043,7 +2115,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|
chip->pagebuf = -1;
|
|
chip->pagebuf = -1;
|
|
|
|
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
|
- nand_fill_oob(chip, ops->oobbuf, ops);
|
|
|
|
|
|
+ nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
|
|
status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
|
|
status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
|
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
|
|
|
|
|
@@ -2166,27 +2238,10 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|
__func__, (unsigned long long)instr->addr,
|
|
__func__, (unsigned long long)instr->addr,
|
|
(unsigned long long)instr->len);
|
|
(unsigned long long)instr->len);
|
|
|
|
|
|
- /* Start address must align on block boundary */
|
|
|
|
- if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
|
|
|
|
- MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
|
|
|
|
|
|
+ if (check_offs_len(mtd, instr->addr, instr->len))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
- }
|
|
|
|
|
|
|
|
- /* Length must align on block boundary */
|
|
|
|
- if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
|
|
|
|
- MTDDEBUG (MTD_DEBUG_LEVEL0,
|
|
|
|
- "nand_erase: Length not block aligned\n");
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Do not allow erase past end of device */
|
|
|
|
- if ((instr->len + instr->addr) > mtd->size) {
|
|
|
|
- MTDDEBUG (MTD_DEBUG_LEVEL0,
|
|
|
|
- "nand_erase: Erase past end of device\n");
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- instr->fail_addr = 0xffffffff;
|
|
|
|
|
|
+ instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
|
|
|
|
|
/* Grab the lock and see if the device is available */
|
|
/* Grab the lock and see if the device is available */
|
|
nand_get_device(chip, mtd, FL_ERASING);
|
|
nand_get_device(chip, mtd, FL_ERASING);
|
|
@@ -2371,7 +2426,8 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
struct nand_chip *chip = mtd->priv;
|
|
struct nand_chip *chip = mtd->priv;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- if ((ret = nand_block_isbad(mtd, ofs))) {
|
|
|
|
|
|
+ ret = nand_block_isbad(mtd, ofs);
|
|
|
|
+ if (ret) {
|
|
/* If it was bad already, return success and do nothing. */
|
|
/* If it was bad already, return success and do nothing. */
|
|
if (ret > 0)
|
|
if (ret > 0)
|
|
return 0;
|
|
return 0;
|
|
@@ -2444,6 +2500,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
|
int i;
|
|
int i;
|
|
int val;
|
|
int val;
|
|
|
|
|
|
|
|
+ /* try ONFI for unknow chip or LP */
|
|
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
|
|
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
|
|
if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
|
|
if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
|
|
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
|
|
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
|
|
@@ -2486,7 +2543,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
|
|
if (!mtd->name)
|
|
if (!mtd->name)
|
|
mtd->name = p->model;
|
|
mtd->name = p->model;
|
|
-
|
|
|
|
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
|
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
|
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
|
|
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
|
|
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
|
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
|
@@ -2495,6 +2551,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
|
if (le16_to_cpu(p->features) & 1)
|
|
if (le16_to_cpu(p->features) & 1)
|
|
*busw = NAND_BUSWIDTH_16;
|
|
*busw = NAND_BUSWIDTH_16;
|
|
|
|
|
|
|
|
+ chip->options &= ~NAND_CHIPOPTIONS_MSK;
|
|
|
|
+ chip->options |= (NAND_NO_READRDY |
|
|
|
|
+ NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
|
|
|
|
+
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
#else
|
|
#else
|
|
@@ -2506,41 +2566,6 @@ static inline int nand_flash_detect_onfi(struct mtd_info *mtd,
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
-static void nand_flash_detect_non_onfi(struct mtd_info *mtd,
|
|
|
|
- struct nand_chip *chip,
|
|
|
|
- const struct nand_flash_dev *type,
|
|
|
|
- int *busw)
|
|
|
|
-{
|
|
|
|
- /* Newer devices have all the information in additional id bytes */
|
|
|
|
- if (!type->pagesize) {
|
|
|
|
- int extid;
|
|
|
|
- /* 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 */
|
|
|
|
- mtd->writesize = 1024 << (extid & 0x3);
|
|
|
|
- extid >>= 2;
|
|
|
|
- /* Calc oobsize */
|
|
|
|
- mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
|
|
|
|
- extid >>= 2;
|
|
|
|
- /* Calc blocksize. Blocksize is multiples of 64KiB */
|
|
|
|
- mtd->erasesize = (64 * 1024) << (extid & 0x03);
|
|
|
|
- extid >>= 2;
|
|
|
|
- /* Get buswidth information */
|
|
|
|
- *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
- /*
|
|
|
|
- * Old devices have chip data hardcoded in the device id table
|
|
|
|
- */
|
|
|
|
- mtd->erasesize = type->erasesize;
|
|
|
|
- mtd->writesize = type->pagesize;
|
|
|
|
- mtd->oobsize = mtd->writesize / 32;
|
|
|
|
- *busw = type->options & NAND_BUSWIDTH_16;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Get the flash and manufacturer id and lookup if the type is supported
|
|
* Get the flash and manufacturer id and lookup if the type is supported
|
|
*/
|
|
*/
|
|
@@ -2550,8 +2575,9 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|
int *maf_id, int *dev_id,
|
|
int *maf_id, int *dev_id,
|
|
const struct nand_flash_dev *type)
|
|
const struct nand_flash_dev *type)
|
|
{
|
|
{
|
|
- int ret, maf_idx;
|
|
|
|
- int tmp_id, tmp_manf;
|
|
|
|
|
|
+ int i, maf_idx;
|
|
|
|
+ u8 id_data[8];
|
|
|
|
+ int ret;
|
|
|
|
|
|
/* Select the device */
|
|
/* Select the device */
|
|
chip->select_chip(mtd, 0);
|
|
chip->select_chip(mtd, 0);
|
|
@@ -2577,15 +2603,13 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
|
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
|
|
|
|
|
- /* Read manufacturer and device IDs */
|
|
|
|
-
|
|
|
|
- tmp_manf = chip->read_byte(mtd);
|
|
|
|
- tmp_id = chip->read_byte(mtd);
|
|
|
|
|
|
+ for (i = 0; i < 2; i++)
|
|
|
|
+ id_data[i] = chip->read_byte(mtd);
|
|
|
|
|
|
- if (tmp_manf != *maf_id || tmp_id != *dev_id) {
|
|
|
|
|
|
+ if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
|
|
printk(KERN_INFO "%s: second ID read did not match "
|
|
printk(KERN_INFO "%s: second ID read did not match "
|
|
"%02x,%02x against %02x,%02x\n", __func__,
|
|
"%02x,%02x against %02x,%02x\n", __func__,
|
|
- *maf_id, *dev_id, tmp_manf, tmp_id);
|
|
|
|
|
|
+ *maf_id, *dev_id, id_data[0], id_data[1]);
|
|
return ERR_PTR(-ENODEV);
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2596,30 +2620,121 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|
if (*dev_id == type->id)
|
|
if (*dev_id == type->id)
|
|
break;
|
|
break;
|
|
|
|
|
|
- if (!type->name) {
|
|
|
|
- /* supress warning if there is no nand */
|
|
|
|
- if (*maf_id != 0x00 && *maf_id != 0xff &&
|
|
|
|
- *dev_id != 0x00 && *dev_id != 0xff)
|
|
|
|
- printk(KERN_INFO "%s: unknown NAND device: "
|
|
|
|
- "Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
|
|
|
|
- __func__, *maf_id, *dev_id);
|
|
|
|
- return ERR_PTR(-ENODEV);
|
|
|
|
|
|
+ chip->onfi_version = 0;
|
|
|
|
+ if (!type->name || !type->pagesize) {
|
|
|
|
+ /* Check is chip is ONFI compliant */
|
|
|
|
+ ret = nand_flash_detect_onfi(mtd, chip, &busw);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto ident_done;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
|
|
|
+
|
|
|
|
+ /* Read entire ID string */
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < 8; i++)
|
|
|
|
+ id_data[i] = chip->read_byte(mtd);
|
|
|
|
+
|
|
|
|
+ if (!type->name)
|
|
|
|
+ return ERR_PTR(-ENODEV);
|
|
|
|
+
|
|
if (!mtd->name)
|
|
if (!mtd->name)
|
|
mtd->name = type->name;
|
|
mtd->name = type->name;
|
|
|
|
|
|
chip->chipsize = (uint64_t)type->chipsize << 20;
|
|
chip->chipsize = (uint64_t)type->chipsize << 20;
|
|
- chip->onfi_version = 0;
|
|
|
|
|
|
|
|
- ret = nand_flash_detect_onfi(mtd, chip, &busw);
|
|
|
|
- if (!ret)
|
|
|
|
- nand_flash_detect_non_onfi(mtd, chip, type, &busw);
|
|
|
|
|
|
+ if (!type->pagesize && chip->init_size) {
|
|
|
|
+ /* set the pagesize, oobsize, erasesize by the driver*/
|
|
|
|
+ busw = chip->init_size(mtd, chip, id_data);
|
|
|
|
+ } else if (!type->pagesize) {
|
|
|
|
+ int extid;
|
|
|
|
+ /* The 3rd id byte holds MLC / multichip data */
|
|
|
|
+ chip->cellinfo = id_data[2];
|
|
|
|
+ /* The 4th id byte is the important one */
|
|
|
|
+ extid = id_data[3];
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Field definitions are in the following datasheets:
|
|
|
|
+ * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
|
|
|
|
+ * New style (6 byte ID): Samsung K9GBG08U0M (p.40)
|
|
|
|
+ *
|
|
|
|
+ * Check for wraparound + Samsung ID + nonzero 6th byte
|
|
|
|
+ * to decide what to do.
|
|
|
|
+ */
|
|
|
|
+ if (id_data[0] == id_data[6] && id_data[1] == id_data[7] &&
|
|
|
|
+ id_data[0] == NAND_MFR_SAMSUNG &&
|
|
|
|
+ (chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
|
|
|
|
+ id_data[5] != 0x00) {
|
|
|
|
+ /* Calc pagesize */
|
|
|
|
+ mtd->writesize = 2048 << (extid & 0x03);
|
|
|
|
+ extid >>= 2;
|
|
|
|
+ /* Calc oobsize */
|
|
|
|
+ switch (extid & 0x03) {
|
|
|
|
+ case 1:
|
|
|
|
+ mtd->oobsize = 128;
|
|
|
|
+ break;
|
|
|
|
+ case 2:
|
|
|
|
+ mtd->oobsize = 218;
|
|
|
|
+ break;
|
|
|
|
+ case 3:
|
|
|
|
+ mtd->oobsize = 400;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ mtd->oobsize = 436;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ extid >>= 2;
|
|
|
|
+ /* Calc blocksize */
|
|
|
|
+ mtd->erasesize = (128 * 1024) <<
|
|
|
|
+ (((extid >> 1) & 0x04) | (extid & 0x03));
|
|
|
|
+ busw = 0;
|
|
|
|
+ } else {
|
|
|
|
+ /* Calc pagesize */
|
|
|
|
+ mtd->writesize = 1024 << (extid & 0x03);
|
|
|
|
+ extid >>= 2;
|
|
|
|
+ /* Calc oobsize */
|
|
|
|
+ mtd->oobsize = (8 << (extid & 0x01)) *
|
|
|
|
+ (mtd->writesize >> 9);
|
|
|
|
+ extid >>= 2;
|
|
|
|
+ /* Calc blocksize. Blocksize is multiples of 64KiB */
|
|
|
|
+ mtd->erasesize = (64 * 1024) << (extid & 0x03);
|
|
|
|
+ extid >>= 2;
|
|
|
|
+ /* Get buswidth information */
|
|
|
|
+ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ /*
|
|
|
|
+ * Old devices have chip data hardcoded in the device id table
|
|
|
|
+ */
|
|
|
|
+ mtd->erasesize = type->erasesize;
|
|
|
|
+ mtd->writesize = type->pagesize;
|
|
|
|
+ mtd->oobsize = mtd->writesize / 32;
|
|
|
|
+ busw = type->options & NAND_BUSWIDTH_16;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Check for Spansion/AMD ID + repeating 5th, 6th byte since
|
|
|
|
+ * some Spansion chips have erasesize that conflicts with size
|
|
|
|
+ * listed in nand_ids table
|
|
|
|
+ * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
|
|
|
|
+ */
|
|
|
|
+ if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
|
|
|
|
+ id_data[5] == 0x00 && id_data[6] == 0x00 &&
|
|
|
|
+ id_data[7] == 0x00 && mtd->writesize == 512) {
|
|
|
|
+ mtd->erasesize = 128 * 1024;
|
|
|
|
+ mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
/* Get chip options, preserve non chip based options */
|
|
/* Get chip options, preserve non chip based options */
|
|
chip->options &= ~NAND_CHIPOPTIONS_MSK;
|
|
chip->options &= ~NAND_CHIPOPTIONS_MSK;
|
|
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
|
|
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
|
|
|
|
|
|
|
|
+ /* Check if chip is a not a samsung device. Do not clear the
|
|
|
|
+ * options for chips which are not having an extended id.
|
|
|
|
+ */
|
|
|
|
+ if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
|
|
|
|
+ chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
|
|
|
|
+ident_done:
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Set chip as a default. Board drivers can override it, if necessary
|
|
* Set chip as a default. Board drivers can override it, if necessary
|
|
*/
|
|
*/
|
|
@@ -2654,18 +2769,48 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|
ffs(mtd->erasesize) - 1;
|
|
ffs(mtd->erasesize) - 1;
|
|
if (chip->chipsize & 0xffffffff)
|
|
if (chip->chipsize & 0xffffffff)
|
|
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
|
|
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
|
|
- else
|
|
|
|
- chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31;
|
|
|
|
|
|
+ else {
|
|
|
|
+ chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
|
|
|
|
+ chip->chip_shift += 32 - 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ chip->badblockbits = 8;
|
|
|
|
|
|
/* Set the bad block position */
|
|
/* Set the bad block position */
|
|
- chip->badblockpos = mtd->writesize > 512 ?
|
|
|
|
- NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
|
|
|
|
|
|
+ if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16))
|
|
|
|
+ chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
|
|
|
|
+ else
|
|
|
|
+ chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
|
|
|
|
|
|
- /* Check if chip is a not a samsung device. Do not clear the
|
|
|
|
- * options for chips which are not having an extended id.
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Bad block marker is stored in the last page of each block
|
|
|
|
+ * on Samsung and Hynix MLC devices; stored in first two pages
|
|
|
|
+ * of each block on Micron devices with 2KiB pages and on
|
|
|
|
+ * SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan
|
|
|
|
+ * only the first page.
|
|
*/
|
|
*/
|
|
- if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
|
|
|
|
- chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
|
|
|
|
|
|
+ if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
|
|
|
|
+ (*maf_id == NAND_MFR_SAMSUNG ||
|
|
|
|
+ *maf_id == NAND_MFR_HYNIX))
|
|
|
|
+ chip->options |= NAND_BBT_SCANLASTPAGE;
|
|
|
|
+ else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
|
|
|
|
+ (*maf_id == NAND_MFR_SAMSUNG ||
|
|
|
|
+ *maf_id == NAND_MFR_HYNIX ||
|
|
|
|
+ *maf_id == NAND_MFR_TOSHIBA ||
|
|
|
|
+ *maf_id == NAND_MFR_AMD)) ||
|
|
|
|
+ (mtd->writesize == 2048 &&
|
|
|
|
+ *maf_id == NAND_MFR_MICRON))
|
|
|
|
+ chip->options |= NAND_BBT_SCAN2NDPAGE;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Numonyx/ST 2K pages, x8 bus use BOTH byte 1 and 6
|
|
|
|
+ */
|
|
|
|
+ if (!(busw & NAND_BUSWIDTH_16) &&
|
|
|
|
+ *maf_id == NAND_MFR_STMICRO &&
|
|
|
|
+ mtd->writesize == 2048) {
|
|
|
|
+ chip->options |= NAND_BBT_SCANBYTE1AND6;
|
|
|
|
+ chip->badblockpos = 0;
|
|
|
|
+ }
|
|
|
|
|
|
/* Check for AND chips with 4 page planes */
|
|
/* Check for AND chips with 4 page planes */
|
|
if (chip->options & NAND_4PAGE_ARRAY)
|
|
if (chip->options & NAND_4PAGE_ARRAY)
|
|
@@ -2677,9 +2822,15 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
|
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
|
chip->cmdfunc = nand_command_lp;
|
|
chip->cmdfunc = nand_command_lp;
|
|
|
|
|
|
|
|
+ /* TODO onfi flash name */
|
|
MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
|
|
MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
|
|
- " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
|
|
|
|
- nand_manuf_ids[maf_idx].name, type->name);
|
|
|
|
|
|
+ " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
|
|
|
|
+ nand_manuf_ids[maf_idx].name,
|
|
|
|
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
|
|
|
|
+ chip->onfi_version ? chip->onfi_params.model : type->name);
|
|
|
|
+#else
|
|
|
|
+ type->name);
|
|
|
|
+#endif
|
|
|
|
|
|
return type;
|
|
return type;
|
|
}
|
|
}
|
|
@@ -2865,7 +3016,8 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
chip->ecc.write_page_raw = nand_write_page_raw;
|
|
chip->ecc.write_page_raw = nand_write_page_raw;
|
|
chip->ecc.read_oob = nand_read_oob_std;
|
|
chip->ecc.read_oob = nand_read_oob_std;
|
|
chip->ecc.write_oob = nand_write_oob_std;
|
|
chip->ecc.write_oob = nand_write_oob_std;
|
|
- chip->ecc.size = 256;
|
|
|
|
|
|
+ if (!chip->ecc.size)
|
|
|
|
+ chip->ecc.size = 256;
|
|
chip->ecc.bytes = 3;
|
|
chip->ecc.bytes = 3;
|
|
break;
|
|
break;
|
|
|
|
|
|
@@ -2973,7 +3125,8 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
|
|
|
|
/* Fill in remaining MTD driver data */
|
|
/* Fill in remaining MTD driver data */
|
|
mtd->type = MTD_NANDFLASH;
|
|
mtd->type = MTD_NANDFLASH;
|
|
- mtd->flags = MTD_CAP_NANDFLASH;
|
|
|
|
|
|
+ mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
|
|
|
|
+ MTD_CAP_NANDFLASH;
|
|
mtd->erase = nand_erase;
|
|
mtd->erase = nand_erase;
|
|
mtd->point = NULL;
|
|
mtd->point = NULL;
|
|
mtd->unpoint = NULL;
|
|
mtd->unpoint = NULL;
|
|
@@ -2992,9 +3145,10 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
|
|
|
|
/* Check, if we should skip the bad block table scan */
|
|
/* Check, if we should skip the bad block table scan */
|
|
if (chip->options & NAND_SKIP_BBTSCAN)
|
|
if (chip->options & NAND_SKIP_BBTSCAN)
|
|
- chip->options |= NAND_BBT_SCANNED;
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ /* Build bad block table */
|
|
|
|
+ return chip->scan_bbt(mtd);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -3039,4 +3193,9 @@ void nand_release(struct mtd_info *mtd)
|
|
kfree(chip->bbt);
|
|
kfree(chip->bbt);
|
|
if (!(chip->options & NAND_OWN_BUFFERS))
|
|
if (!(chip->options & NAND_OWN_BUFFERS))
|
|
kfree(chip->buffers);
|
|
kfree(chip->buffers);
|
|
|
|
+
|
|
|
|
+ /* Free bad block descriptor memory */
|
|
|
|
+ if (chip->badblock_pattern && chip->badblock_pattern->options
|
|
|
|
+ & NAND_BBT_DYNAMICSTRUCT)
|
|
|
|
+ kfree(chip->badblock_pattern);
|
|
}
|
|
}
|