9
0
Fork 0
barebox/drivers/mtd/nand/nand_imx_bbm.c

221 lines
4.5 KiB
C

/*
* imx_nand_bbm.c - create a flash bad block table for i.MX NAND
*
* Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <common.h>
#include <command.h>
#include <fs.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <malloc.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/err.h>
/*
* The i.MX NAND controller has the problem that it handles the
* data in chunks of 512 bytes. It doesn't treat 2k NAND chips as
* 2048 byte data + 64 OOB, but instead:
*
* 512b data + 16b OOB +
* 512b data + 16b OOB +
* 512b data + 16b OOB +
* 512b data + 16b OOB
*
* This means that the factory provided bad block marker ends up
* in the page data at offset 2000 instead of in the OOB data.
*
* To preserve the factory bad block information we take the following
* strategy:
*
* - If the NAND driver detects that no flash BBT is present on 2k NAND
* chips it will not create one because it would do so based on the wrong
* BBM position
* - This command is used to create a flash BBT then.
*
* From this point on we can forget about the BBMs and rely completely
* on the flash BBT.
*
*/
static int checkbad(struct mtd_info *mtd, loff_t ofs, void *__buf)
{
int ret, retlen;
uint8_t *buf = __buf;
ret = mtd->read(mtd, ofs, mtd->writesize, &retlen, buf);
if (ret < 0)
return ret;
if (buf[2000] != 0xff)
return 1;
return 0;
}
static void *create_bbt(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
int len, i, numblocks, ret;
loff_t from = 0;
void *buf;
uint8_t *bbt;
if ((chip->bbt_td && chip->bbt_td->pages[0] != -1) ||
(chip->bbt_md && chip->bbt_md->pages[0] != -1)) {
printf("Flash bbt already present\n");
return ERR_PTR(-EEXIST);
}
len = mtd->size >> (chip->bbt_erase_shift + 2);
/* Allocate memory (2bit per block) and clear the memory bad block table */
bbt = kzalloc(len, GFP_KERNEL);
if (!bbt)
return ERR_PTR(-ENOMEM);
buf = malloc(mtd->writesize);
if (!buf) {
ret = -ENOMEM;
goto out2;
}
numblocks = mtd->size >> (chip->bbt_erase_shift - 1);
for (i = 0; i < numblocks;) {
ret = checkbad(mtd, from, buf);
if (ret < 0)
goto out1;
if (ret) {
bbt[i >> 3] |= 0x03 << (i & 0x6);
printf("Bad eraseblock %d at 0x%08x\n", i >> 1,
(unsigned int)from);
}
i += 2;
from += (1 << chip->bbt_erase_shift);
}
return bbt;
out1:
free(buf);
out2:
free(bbt);
return ERR_PTR(ret);
}
static int attach_bbt(struct mtd_info *mtd, void *bbt)
{
struct nand_chip *chip = mtd->priv;
chip->bbt_td->options |= NAND_BBT_WRITE | NAND_BBT_CREATE;
chip->bbt_md->options |= NAND_BBT_WRITE | NAND_BBT_CREATE;
free(chip->bbt);
chip->bbt = bbt;
return nand_update_bbt(mtd, 0);
}
static int do_imx_nand_bbm(int argc, char *argv[])
{
int opt, ret;
struct cdev *cdev;
struct mtd_info *mtd;
int yes = 0;
void *bbt;
while ((opt = getopt(argc, argv, "y")) > 0) {
switch (opt) {
case 'y':
yes = 1;
break;
default:
return COMMAND_ERROR_USAGE;
}
}
cdev = cdev_open("nand0", O_RDWR);
if (!cdev)
return -ENOENT;
mtd = cdev->mtd;
if (!mtd)
return -EINVAL;
if (strcmp(mtd->name, "imx_nand")) {
printf("This is not an i.MX nand but a %s\n", mtd->name);
ret = -EINVAL;
goto out;
}
switch (mtd->writesize) {
case 512:
printf("writesize is 512. This command is not needed\n");
ret = 1;
goto out;
case 2048:
break;
default:
printf("not implemented for writesize %d\n", mtd->writesize);
ret = 1;
goto out;
}
bbt = create_bbt(mtd);
if (IS_ERR(bbt)) {
ret = 1;
goto out;
}
if (!yes) {
int c;
printf("create flash bbt (y/n)?");
c = getc();
if (c == 'y')
yes = 1;
printf("\n");
}
if (!yes) {
free(bbt);
ret = 1;
goto out;
}
ret = attach_bbt(mtd, bbt);
if (!ret)
printf("bbt successfully added\n");
else
free(bbt);
out:
cdev_close(cdev);
return ret;
}
BAREBOX_CMD_START(imx_nand_bbm)
.cmd = do_imx_nand_bbm,
BAREBOX_CMD_DESC("create BBT for i.MX NAND")
BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
BAREBOX_CMD_END