@@ -56,7 +56,7 @@ config MTD_NAND_DENALI
help
Enable the driver for NAND flash on Intel Moorestown, using the
Denali NAND controller core.
-
+
config MTD_NAND_DENALI_SCRATCH_REG_ADDR
hex "Denali NAND size scratch register address"
default "0xFF108018"
@@ -68,6 +68,30 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
scratch register here to enable this feature. On Intel Moorestown
boards, the scratch register is at 0xFF108018.
+choice
+ prompt "Compile for"
+ depends on MTD_NAND_DENALI
+ default MRST_NAND_CONTROLLER
+
+config MRST_NAND_CONTROLLER
+ bool "MRST NAND controller"
+ help
+
+config CE4100_NAND_CONTROLLER
+ bool "CE4100 NAND controller"
+ help
+
+endchoice
+
+config MTD_NAND_DENALI_BBT_BLKNUM
+ int "blocknums to store BBT -- MRST platform will use this"
+ default 8
+ range 2 10
+ depends on MTD_NAND_DENALI && MRST_NAND_CONTROLLER
+ help
+ This parameter defines the blocknums which is used to store Bad
+ Block Table.
+
config MTD_NAND_EDB7312
tristate "Support for Cirrus Logic EBD7312 evaluation board"
depends on ARCH_EDB7312
@@ -473,6 +473,9 @@ static void find_valid_banks(struct denali_nand_info *denali)
static void detect_partition_feature(struct denali_nand_info *denali)
{
+ void __iomem *scratch_reg;
+ int8_t shift;
+
/* For MRST platform, denali->fwblks represent the
* number of blocks firmware is taken,
* FW is in protect partition and MTD driver has no
@@ -493,6 +496,22 @@ static void detect_partition_feature(struct denali_nand_info *denali)
denali->fwblks = SPECTRA_START_BLOCK;
} else
denali->fwblks = SPECTRA_START_BLOCK;
+
+ /* For MRST platform, denali->manageblks represent the
+ * number of blocks firmware can manage.
+ * */
+ scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE);
+ if (!scratch_reg)
+ dev_err(&denali->dev->dev,
+ "denali: scrach reg ioremap failed\n");
+ else {
+ shift = (int8_t)ioread8(scratch_reg);
+ if (shift > 0)
+ denali->manageblks = 1 << shift;
+ else
+ denali->manageblks = 0;
+ iounmap(scratch_reg);
+ }
}
static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
@@ -546,8 +565,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
find_valid_banks(denali);
- detect_partition_feature(denali);
-
/* If the user specified to override the default timings
* with a specific ONFI mode, we apply those changes here.
*/
@@ -1324,6 +1341,438 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
}
}
+/**
+ * check_bbt_block: when reading the block which is contained
+ * BBT infromation, we do a check here. If reading failed, suppose
+ * device is power off during drive writing this block. So just
+ * erase this block and reture with special value.
+ *
+ * return value:
+ * 0: no error happend, read success;
+ * 1: read failed, erase success;
+ * -EIO: read failed, erase failed;
+ * other: param wrong
+ */
+static int check_bbt_block(struct mtd_info *mtd, loff_t from,
+ uint32_t len, uint8_t *buf)
+{
+ uint32_t ret, retlen = 0;
+ memset(buf, 0, len);
+ ret = mtd->read(mtd, from, len, &retlen, buf);
+ /* If read failed, driver should erase this block
+ * If erase failed, mark this block bad
+ * */
+ if (ret == -EBADMSG) {
+ /* this Block was bad for some reason
+ * erase it right now*/
+ struct erase_info ei;
+ memset(&ei, 0, sizeof(struct erase_info));
+ ei.mtd = mtd;
+ ei.addr = from;
+ ei.len = mtd->erasesize;
+ ret = mtd->erase(mtd, &ei);
+ if (ret != -EIO && ret != -EINVAL)
+ ret = 1;
+ } else if (ret == -EUCLEAN)
+ ret = 0;
+
+ return ret;
+}
+
+/*
+ * write_bbt_to_blk: write BBT information to the specified block
+ *
+ * return value:
+ * 0: no error happend, write success;
+ * 1: write failed;
+ * other: system error;
+ * */
+static int write_bbt_to_blk(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct denali_bbt_info *ptr = &denali->bbtinfo;
+ struct nand_chip *chip = &denali->nand;
+ struct erase_info ei;
+ struct device *dev = &denali->dev->dev;
+ uint32_t *bbtblk, len, blk;
+ uint8_t *buf, *bbt = NULL;
+ uint32_t ret, i, retlen;
+ int write_done;
+ loff_t to;
+retry:
+ if (denali->availbbtblk <= 0) {
+ dev_err(dev, "No available block reserved for storing BBT\n");
+ ret = 1;
+ return ret;
+ }
+ ptr->bbtlen = 4 * mtd->ecc_stats.badblocks;
+ ptr->serial++;
+ len = HEAD_LEN + ptr->bbtlen;
+ if (len % mtd->writesize)
+ len = (len / mtd->writesize + 1) * mtd->writesize;
+
+ buf = kzalloc(len, GFP_KERNEL);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ /* Get bad block number form memery bbt */
+ bbt = chip->bbt;
+ bbtblk = (uint32_t *)&buf[HEAD_LEN];
+ for (i = 0; i < denali->totalblks / 4; i++, bbt++) {
+ blk = *bbt;
+ if (!blk)
+ continue;
+ if (blk & 0x03)
+ *bbtblk++ = i * 4;
+ if ((blk >> 2) & 0x03)
+ *bbtblk++ = 1 + i * 4;
+ if ((blk >> 4) & 0x03)
+ *bbtblk++ = 2 + i * 4;
+ if ((blk >> 6) & 0x03)
+ *bbtblk++ = 3 + i * 4;
+ }
+
+ /* Add BBT basic information as BBT format said
+ * and write BBT information to specified blocks.
+ * Each block contains the same BBT
+ * */
+ write_done = 0;
+ buf[0] = 'B';
+ buf[1] = 'B';
+ buf[2] = 'T';
+ buf[3] = '0';
+ *(uint32_t *)(buf + 4) = ptr->bbtlen;
+ *(uint32_t *)(buf + 8) = ptr->serial;
+ for (i = denali->bbtstartblk; i < denali->totalblks; i++) {
+ /* skip Bad Block */
+ if (chip->bbt[i>>2] & (0x3<<((i&3)<<1)))
+ continue;
+ to = (loff_t)i * mtd->erasesize;
+ memset(&ei, 0, sizeof(struct erase_info));
+ ei.mtd = mtd;
+ ei.addr = to;
+ ei.len = mtd->erasesize;
+ ret = mtd->erase(mtd, &ei);
+ if (ret) {
+ /* If erase failed, mark this block bad
+ * and go to top to try to write again */
+ chip->bbt[i>>2] |= 0x1 << ((i&3)<<1);
+ mtd->ecc_stats.badblocks++;
+ denali->availbbtblk--;
+ kfree(buf);
+ dev_info(&denali->dev->dev, "erase bbtblk failed!!"
+ "found new bad block %d", i);
+ goto retry;
+ }
+ ret = mtd->write(mtd, to, len, &retlen, buf);
+ if (ret || retlen != len) {
+ /* If write failed, mark this block bad
+ * and go to top to try to write again */
+ chip->bbt[i>>2] |= 0x1 << ((i&3)<<1);
+ mtd->ecc_stats.badblocks++;
+ denali->availbbtblk--;
+ kfree(buf);
+ dev_info(&denali->dev->dev, "write bbtblk failed!!"
+ "found new bad block %d", i);
+ goto retry;
+ }
+ write_done++;
+ }
+
+ kfree(buf);
+
+ /* If at lease one block is written successfully,
+ * return with success*/
+ if (write_done)
+ ret = 0;
+ else {
+ if (denali->availbbtblk > 0)
+ BUG();
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/*
+ * create_bbt: scan NAND and create memery bbt
+ *
+ * Driver will scan the first 3 pages in each block.
+ * The first 2 bytes in OOB area in the 3 pages will
+ * identify whether the current block is bad.
+ * */
+static int create_bbt(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct nand_chip *chip = &denali->nand;
+ struct device *dev = &denali->dev->dev;
+ struct mtd_oob_ops ops;
+ uint32_t startblk, endblk, i, j, pagecnt = 3,
+ skip = denali->bbtskipbytes;
+ int checkbyte, ret = 0;
+ uint8_t *buf = NULL;
+ loff_t from = 0;
+
+ if (denali->manageblks == 0) {
+ dev_err(dev, "Never be here. The whole nand"
+ " should be managed by FTL\n");
+ BUG();
+ }
+
+ /* we skip protected partition because
+ * there is no permission for MTD driver
+ * to access, also skip kboot part, since
+ * this part bad block is managed by FTL
+ * */
+ startblk = denali->manageblks;
+ endblk = denali->totalblks;
+
+ buf = kzalloc(mtd->oobsize, GFP_KERNEL);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ memset(&ops, 0, sizeof(struct mtd_oob_ops));
+ ops.mode = MTD_OOB_PLACE;
+ ops.ooblen = mtd->oobsize;
+ ops.oobbuf = buf;
+ ops.ooboffs = 0;
+ ops.oobretlen = 0;
+ ops.datbuf = NULL;
+ for (i = startblk; i < endblk; i++) {
+ from = (loff_t)i * mtd->erasesize;
+ for (j = 0; j < pagecnt; j++) {
+ ret = mtd->read_oob(mtd, from, &ops);
+ if (ret)
+ goto read_oob_failed;
+ for (checkbyte = 0;
+ checkbyte < skip;
+ checkbyte++)
+ if (ops.oobbuf[checkbyte] != 0xff) {
+ chip->bbt[i>>2] |= 0x3 << ((i&3)<<1);
+ mtd->ecc_stats.badblocks++;
+ goto next_scan;
+ }
+ from += mtd->writesize;
+ }
+next_scan:
+ continue;
+ }
+read_oob_failed:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * denali_scan_bbt - To creat a memery BBT if the BBT information
+ * is already stored in the blocks driver reserved.
+ *
+ * If NAND contains BBT information, denali_scan_bbt will create
+ * chip->bbt by reading BBT out.
+ * If NAND doesn't contain, suppose this NAND to be a new NAND.
+ *
+ * BBT is stored in the end 8(default) blocks of NAND chip. Each
+ * block contains the same BBT information. That means NAND chip
+ * contain 8 copys of a BBT information. If these blocks are all
+ * bad, driver will return with failure.
+ *
+ * Format of BBT defines below:
+ * --------------------------------------------------------------
+ * | magic | length | serial | reserved | bad block |
+ * | number | | number | | number |
+ * | (4bytes) | (4bytes) | (4bytes) | (32bytes) | information |
+ * --------------------------------------------------------------
+ * @ magic number is: 'B''B''T''0' to identify whether this block contains
+ * BBT information.
+ * @ length: represent the length of "bad block number information" part.
+ * @ serial number: represent the version of BBT. each time update the
+ * BBT will increase this value by 1.
+ * @ reserved: no use right now.
+ * @ bad block number information: store bad block number, each number
+ * is a 32bits value.
+ * */
+static int denali_scan_bbt(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct nand_chip *chip = &denali->nand;
+ struct denali_bbt_info *ptr = &denali->bbtinfo;
+ struct device *dev = &denali->dev->dev;
+ uint32_t len = denali->totalblks;
+ uint32_t serial;
+ uint64_t mtdsize;
+ uint32_t i, badblk, get_bbt = 0, ret = 0;
+ uint8_t bbt_w = 0,/* a flag represent whether write BBT to NAND*/
+ *buf = NULL;
+ loff_t from = 0;
+ /* let mtd know the BBT blocks
+ * after handle BBT, resett mtd size back*/
+ mtdsize = mtd->size;
+ mtd->size = chip->numchips * chip->chipsize;
+ /* alloc 2 bit for each data block
+ * do the same as kernel did */
+ if (len % 4)
+ len = len/4 + 1;
+ else
+ len = len/4;
+ chip->bbt = NULL;
+ chip->bbt = kzalloc(len, GFP_KERNEL);
+ if (chip->bbt == NULL) {
+ ret = -ENOMEM;
+ goto failed_alloc_bbt;
+ }
+
+ /* alloc buffer
+ * suppose the BBT size is the maximum value */
+ len = denali->totalblks * 4 + HEAD_LEN;
+ if (len % mtd->writesize)
+ len = (len/mtd->writesize + 1) * mtd->writesize;
+ buf = kzalloc(len, GFP_KERNEL);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ goto failed_alloc_buf;
+ }
+
+ /* check whether if there is already a bbt in NAND */
+ for (i = denali->bbtstartblk; i < denali->totalblks; i++) {
+ from = (loff_t)i * mtd->erasesize;
+ ret = check_bbt_block(mtd, from, len, buf);
+ if (ret == -EIO) {
+ dev_notice(dev, "bbt start block %d is BAD", i);
+ /* mark the storing BBT block to be bad */
+ chip->bbt[i>>2] |= 0x01 << ((i&0x03)<<1);
+ denali->availbbtblk--;
+ mtd->ecc_stats.badblocks++;
+ continue;
+ } else if (ret == 1) {
+ dev_notice(dev, "bbt start block %d is ERASED,"
+ " start NEXT", i);
+ continue;
+ } else if (ret) {
+ dev_err(dev, "ERROR param in check_bbt_block"
+ " ret %d", ret);
+ goto failed_tail;
+ }
+ /* If read successed, store BBT basic information
+ * If there are different serial number, the block
+ * contained the biggest serial number must have
+ * the latest BBT information.
+ * so here just update BBT basice information
+ * only if found a bigger serial number.*/
+ if (buf[0] == 'B' && buf[1] == 'B' &&
+ buf[2] == 'T' && buf[3] == '0') {
+ serial = *(uint32_t *)(buf + 8);
+ if (serial > ptr->serial) {
+ ptr->version = i;
+ ptr->bbtlen = *(uint32_t *)(buf + 4);
+ ptr->serial = serial;
+ }
+ get_bbt = 1;
+ }
+ memset(buf, 0, mtd->writesize);
+ }
+
+ if (denali->availbbtblk <= 0) {
+ /* all the blocks stored BBT are bad
+ * driver thought this is a bad NAND chip
+ * and return with a failure
+ * */
+ ret = 1;
+ goto failed_tail;
+ }
+
+ if (get_bbt) {
+ /* Found NAND contains available BBT information
+ * and will read it out.
+ * needn't check because we have already checked
+ * above*/
+ len = ptr->bbtlen + HEAD_LEN;
+ if (len % mtd->writesize)
+ len = (len/mtd->writesize + 1) * mtd->writesize;
+ from = (loff_t)ptr->version * mtd->erasesize;
+ check_bbt_block(mtd, from, len, buf);
+ if (ptr->serial != *(uint32_t *)(buf + 8) ||
+ ptr->bbtlen != *(uint32_t *)(buf + 4)) {
+ dev_err(dev, "BUG when get BBT form block %d",
+ ptr->version);
+ BUG();
+ }
+ for (i = HEAD_LEN; i < ptr->bbtlen + HEAD_LEN; i += 4) {
+ badblk = *(uint32_t *)(buf + i);
+ if (!(chip->bbt[badblk >> 2] &
+ (0x3 << ((badblk & 0x3) << 1)))) {
+ chip->bbt[badblk >> 2] |=
+ 0x03 << ((badblk & 0x3) << 1);
+ mtd->ecc_stats.badblocks++;
+ }
+ }
+ } else {
+ /* If NAND doesn't contain any BBT information,
+ * driver create memery BBT by scaning NAND itself
+ * */
+ ret = create_bbt(mtd);
+ if (ret)
+ goto failed_tail;
+ ptr->version = 0;
+ ptr->serial = 0;
+ ptr->bbtlen = 0;
+ bbt_w = 1; /* set to write BBT to NAND */
+ }
+
+ /* If found new bad block or driver scaned NAND itself,
+ * write BBT information back to NAND
+ * */
+ if (ptr->bbtlen < mtd->ecc_stats.badblocks * 4)
+ ret = write_bbt_to_blk(mtd);
+ else if (ptr->bbtlen > mtd->ecc_stats.badblocks * 4) {
+ dev_err(dev, "bbtlen can't be large than badblocks number");
+ BUG();
+ }
+
+ if (!ret) {
+ kfree(buf);
+ mtd->size = mtdsize;
+ return ret;
+ }
+
+failed_tail:
+ kfree(buf);
+failed_alloc_buf:
+ kfree(chip->bbt);
+failed_alloc_bbt:
+ mtd->size = mtdsize;
+ return ret;
+}
+
+/*
+ * denali_block_markbad: To mark a block as bad.
+ * Update memery bbt and BBT nand information
+ * */
+static int denali_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct nand_chip *chip = &denali->nand;
+ uint32_t block = ofs >> chip->bbt_erase_shift;
+ uint64_t mtdsize;
+ int ret = 0;
+ /* let mtd know the BBT blocks */
+ mtdsize = mtd->size;
+ mtd->size = chip->numchips * chip->chipsize;
+
+ chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+ mtd->ecc_stats.badblocks++;
+ /* update the flash based BBT */
+ ret = write_bbt_to_blk(mtd);
+ if (ret)
+ mtd->ecc_stats.badblocks--;
+
+ /* after handle BBT, resett mtd size back*/
+ mtd->size = mtdsize;
+ return ret;
+}
+
/* stubs for ECC functions not used by the NAND core */
static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data,
uint8_t *ecc_code)
@@ -1641,6 +2090,37 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
denali->nand.phys_erase_shift;
denali->blksperchip = denali->totalblks / denali->nand.numchips;
+ if (denali->platform == INTEL_MRST) {
+ denali->bbtblks = BBT_BLKNUM;
+ denali->availbbtblk = denali->bbtblks;
+ denali->bbtstartblk = denali->totalblks - denali->bbtblks;
+ detect_partition_feature(denali);
+ denali->nand.bbt_td = NULL;
+ denali->nand.bbt_md = NULL;
+ denali->nand.options &= ~(NAND_USE_FLASH_BBT);
+ if (denali->manageblks) {
+ /* represent FW just manages a part of the whole NAND,
+ * and the reset of the NAND need MTD driver to care,
+ * so NAND driver need to manage BBT. BBT is hidden by
+ * NAND driver, user can not access BBT blocks.
+ * */
+ denali->mtd.size -= denali->mtd.erasesize *
+ denali->bbtblks;
+ denali->nand.options &= ~(NAND_SKIP_BBTSCAN);
+ }
+ /* otherwise represent FW manages the whole NAND and there must
+ * has a FTL layer, FTL layer will manage the whole NAND,
+ * so MTD driver needn't care the BBT
+ * and just report the whole NAND size
+ * in this situation, user can only use those filesystem base on
+ * block device, e.g.ext3. NAND filesystem can't be used, e.g.
+ * YAFFS/YAFFS2 and ubifs.
+ * */
+ denali->nand.options |= NAND_NO_SUBPAGE_WRITE;
+ denali->nand.scan_bbt = denali_scan_bbt;
+ denali->nand.block_markbad = denali_block_markbad;
+ }
+
/* These functions are required by the NAND core framework, otherwise,
* the NAND core will assert. However, we don't need them, so we'll stub
* them out. */
@@ -714,6 +714,7 @@
#define LLD_MAX_FLASH_BANKS 4
#define DENALI_BUF_SIZE (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE)
+#define BBT_BLKNUM CONFIG_MTD_NAND_DENALI_BBT_BLKNUM
struct nand_buf {
int head;
@@ -724,6 +725,14 @@ struct nand_buf {
#define INTEL_CE4100 1
#define INTEL_MRST 2
+#define HEAD_LEN 44
+
+/*denali_bbt_info: To store basic BBT information*/
+struct denali_bbt_info {
+ uint32_t version;
+ uint32_t bbtlen;
+ uint32_t serial;
+};
struct denali_nand_info {
struct mtd_info mtd;
@@ -748,9 +757,16 @@ struct denali_nand_info {
uint32_t devnum; /* represent how many nands connected */
uint32_t fwblks; /* represent how many blocks FW used */
+ uint32_t manageblks; /* how many blocks managed by FW */
uint32_t totalblks;
uint32_t blksperchip;
+ uint32_t bbtstartblk;
+ uint32_t bbtblks;
uint32_t bbtskipbytes;
+
+ /*used for BBT management*/
+ struct denali_bbt_info bbtinfo;
+ uint32_t availbbtblk; /* how many available blocks can store BBT */
};
#endif /*_LLD_NAND_*/