[PATCH 9/9] mtd: nand: stm_nand_bch: provide ST's implementation of Back Block Table
Brian Norris
computersforpeace at gmail.com
Sun Oct 5 23:25:10 PDT 2014
Hi Lee,
On Wed, Aug 27, 2014 at 01:42:24PM +0100, Lee Jones wrote:
> This is the BBT format ST use internally. It has to be used on boards
> which were flashed with or actively use ST's tooling and boards which
> are booted using ST's bootloaders.
>
> Signed-off-by: Lee Jones <lee.jones at linaro.org>
> ---
> drivers/mtd/nand/Kconfig | 8 +
> drivers/mtd/nand/Makefile | 1 +
> drivers/mtd/nand/stm_nand_bbt.c | 601 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 610 insertions(+)
> create mode 100644 drivers/mtd/nand/stm_nand_bbt.c
>
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 2738eec..5c04ec1 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -520,4 +520,12 @@ config MTD_NAND_STM_BCH
> help
> Adds support for the STMicroelectronics NANDi BCH Controller.
>
> +config MTD_NAND_STM_BCH_BBT
> + tristate "STMicroelectronics: NANDi BCH Controller Bad Block Table support"
> + default MTD_NAND_STM_BCH
> + help
> + Adds support for the STMicroelectronics Bad Block Table support.
> + If you are using a device which has has already been initialised
> + by ST or using their tooling/bootloaders, leave this enabled.
> +
> endif # MTD_NAND
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index 60f374b..5ef0462 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -47,6 +47,7 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
> obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
> obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
> obj-$(CONFIG_MTD_NAND_STM_BCH) += stm_nand_bch.o stm_nand_dt.o
> +obj-$(CONFIG_MTD_NAND_STM_BCH_BBT) += stm_nand_bbt.o
> obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
> obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
> obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
> diff --git a/drivers/mtd/nand/stm_nand_bbt.c b/drivers/mtd/nand/stm_nand_bbt.c
> new file mode 100644
> index 0000000..242bffd
> --- /dev/null
> +++ b/drivers/mtd/nand/stm_nand_bbt.c
> @@ -0,0 +1,601 @@
> +/*
> + * Support for STMicroelectronics Bad Block Table (BBT)
> + *
> + * Copyright (c) 2014 STMicroelectronics Limited
> + *
> + * Authors: Angus Clark <Angus.Clark at st.com>
> + * Lee Jones <lee.jones at linaro.org>
> + *
> + * 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.
> + */
Can you provide some sort of prose summary of your BBT format? Maybe
here at the top of the file, like with nand_bbt.c. Or a separate doc in
Documentation/mtd/nand/, if you think that's more appropriate.
> +
> +#include <linux/mtd/stm_nand.h>
> +#include <linux/mtd/stm_nand_bbt.h>
> +#include <generated/utsrelease.h>
> +
> +/*
> + * Inband Bad Block Table (IBBT)
> + */
> +#define NAND_IBBT_NBLOCKS 4
> +#define NAND_IBBT_SIGLEN 4
> +#define NAND_IBBT_PRIMARY 0
> +#define NAND_IBBT_MIRROR 1
> +#define NAND_IBBT_SCHEMA 0x10
> +#define NAND_IBBT_BCH_SCHEMA 0x10
> +
> +static uint8_t ibbt_sigs[2][NAND_IBBT_SIGLEN] = {
> + {'B', 'b', 't', '0'},
> + {'1', 't', 'b', 'B'},
> +};
> +
> +static char *bbt_strs[] = {
> + "primary",
> + "mirror",
> +};
> +
> +/* IBBT header */
> +struct nand_ibbt_header {
> + uint8_t signature[4]; /* "Bbt0" or "1tbB" signature */
> + uint8_t version; /* BBT version ("age") */
> + uint8_t reserved[3]; /* padding */
> + uint8_t schema[4]; /* "base" schema (x4) */
> +} __packed;
> +
> +/* Extend IBBT header with some stm-nand-bch niceties */
> +struct nand_ibbt_bch_header {
> + struct nand_ibbt_header base;
> + uint8_t schema[4]; /* "private" schema (x4) */
> + uint8_t ecc_size[4]; /* ECC bytes (0, 32, 54) (x4) */
> + char author[64]; /* Arbitrary string for S/W to use */
> +} __packed;
> +
> +/*
> + * Bad Block Tables/Bad Block Markers
> + */
> +#define BBT_MARK_BAD_FACTORY 0x0
> +#define BBT_MARK_BAD_WEAR 0x1
> +#define BBT_MARK_GOOD 0x3
> +
> +static void bbt_set_block_mark(uint8_t *bbt, uint32_t block, uint8_t mark)
> +{
> + unsigned int byte = block >> 2;
> + unsigned int shift = (block & 0x3) << 1;
> +
> + bbt[byte] &= ~(0x3 << shift);
> + bbt[byte] |= ((mark & 0x3) << shift);
> +}
> +
> +static uint8_t bbt_get_block_mark(uint8_t *bbt, uint32_t block)
> +{
> + unsigned int byte = block >> 2;
> + unsigned int shift = (block & 0x3) << 1;
> +
> + return (bbt[byte] >> shift) & 0x3;
> +}
> +
> +static int bbt_is_block_bad(uint8_t *bbt, uint32_t block)
> +{
> + return bbt_get_block_mark(bbt, block) == BBT_MARK_GOOD ? 0 : 1;
> +}
> +
> +/* Scan page for BBM(s), according to specified BBT options */
> +static int nandi_scan_bad_block_markers_page(struct nandi_controller *nandi,
> + uint32_t page)
> +{
> + struct mtd_info *mtd = &nandi->info.mtd;
> + struct nand_chip *chip = mtd->priv;
> + uint8_t *oob_buf = nandi->oob_buf;
> + int i, e;
> +
> + /* Read the OOB area */
> + flex_read_raw(nandi, page, mtd->writesize, oob_buf, mtd->oobsize);
> +
> + if (oob_buf[chip->badblockpos] == 0xff)
> + return 0;
> +
> + /* Tolerate 'alien' Hamming Boot Mode ECC */
> + e = 0;
> + for (i = 0; i < mtd->oobsize; i += 16)
> + e += hweight8(oob_buf[i + 3] ^ 'B');
> + if (e <= 1)
> + return 0;
> +
> + /* Tolerate 'alien' Hamming AFM ECC */
> + e = 0;
> + for (i = 0; i < mtd->oobsize; i += 16) {
> + e += hweight8(oob_buf[i + 3] ^ 'A');
> + e += hweight8(oob_buf[i + 4] ^ 'F');
> + e += hweight8(oob_buf[i + 5] ^ 'M');
> + if (e <= 1)
> + return 0;
> + }
> +
> + return 1;
> +}
> +
> +/* Scan block for BBM(s), according to specified BBT options */
> +static int nandi_scan_bad_block_markers_block(struct nandi_controller *nandi,
> + uint32_t block)
> +
> +{
> + struct mtd_info *mtd = &nandi->info.mtd;
> + struct nand_chip *chip = mtd->priv;
> + uint32_t pages_per_block = mtd->erasesize >> chip->page_shift;
> + uint32_t page = block << (chip->phys_erase_shift - chip->page_shift);
> +
> + if (nandi_scan_bad_block_markers_page(nandi, page))
> + return 1;
> +
> + if ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) &&
> + nandi_scan_bad_block_markers_page(nandi, page + 1))
> + return 1;
> +
> + if ((chip->bbt_options & NAND_BBT_SCANLASTPAGE) &&
> + nandi_scan_bad_block_markers_page(nandi,
> + page + pages_per_block - 1))
> + return 1;
> +
> + return 0;
> +}
> +
> +/* Scan for BBMs and build memory-resident BBT */
> +static int nandi_scan_build_bbt(struct nandi_controller *nandi,
> + struct nandi_bbt_info *bbt_info)
> +{
> + struct mtd_info *mtd = &nandi->info.mtd;
> + struct nand_chip *chip = mtd->priv;
> + uint32_t page_size = mtd->writesize;
> + uint8_t *bbt = bbt_info->bbt;
> + uint32_t block;
> +
> + dev_dbg(nandi->dev,
> + "scan device for bad-block markers [bbt options = 0x%02x]\n",
> + chip->bbt_options);
> +
> + memset(bbt, 0xff, page_size);
> + bbt_info->bbt_vers[0] = 0;
> + bbt_info->bbt_vers[1] = 0;
> + bbt_info->bbt_block[0] = nandi->blocks_per_device - 1;
> + bbt_info->bbt_block[1] = nandi->blocks_per_device - 2;
> +
> + for (block = 0; block < nandi->blocks_per_device; block++)
> + if (nandi_scan_bad_block_markers_block(nandi, block))
> + bbt_set_block_mark(bbt, block, BBT_MARK_BAD_FACTORY);
> +
> + return 0;
> +}
> +
> +/* Populate IBBT BCH Header */
> +static void bch_fill_ibbt_header(struct nandi_controller *nandi,
> + struct nand_ibbt_bch_header *ibbt_header,
> + int bak, uint8_t vers)
> +{
> + const char author[] = "STLinux " UTS_RELEASE " (stm-nand-bch)";
> +
> + memcpy(ibbt_header->base.signature, ibbt_sigs[bak], NAND_IBBT_SIGLEN);
> + ibbt_header->base.version = vers;
> + memset(ibbt_header->base.schema, NAND_IBBT_SCHEMA, 4);
> +
> + memset(ibbt_header->schema, NAND_IBBT_SCHEMA, 4);
> + memset(ibbt_header->ecc_size, bch_ecc_sizes[nandi->bch_ecc_mode], 4);
> + memcpy(ibbt_header->author, author, sizeof(author));
> +}
> +
> +/* Write IBBT to Flash */
> +static int bch_write_bbt_data(struct nandi_controller *nandi,
> + struct nandi_bbt_info *bbt_info,
> + uint32_t block, int bak, uint8_t vers)
> +{
> + struct nand_ibbt_bch_header *ibbt_header =
> + (struct nand_ibbt_bch_header *)nandi->page_buf;
> + struct nand_chip *chip = &nandi->info.chip;
> + struct mtd_info *mtd = &nandi->info.mtd;
> + uint32_t page_size = mtd->writesize;
> + uint32_t block_size = mtd->erasesize;
> + loff_t offs;
> + size_t retlen;
> + int ret;
> +
> + nandi->cached_page = -1;
> +
> + /* Write BBT contents to first page of block */
> + offs = (loff_t)block << chip->phys_erase_shift;
> + ret = mtd_write(mtd, offs, page_size, &retlen, bbt_info->bbt);
> + if (ret)
> + return ret;
> +
> + /* Update IBBT header and write to last page of block */
> + memset(ibbt_header, 0xff, mtd->writesize);
> + bch_fill_ibbt_header(nandi, ibbt_header, bak, vers);
> + offs += block_size - page_size;
> + ret = mtd_write(mtd, offs, sizeof(*ibbt_header), &retlen,
> + (uint8_t *)ibbt_header);
> + return ret;
> +}
> +
> +/*
> + * Update Flash-resident BBT:
> + * erase/search suitable block, and write table data to Flash
> + */
> +static int bch_update_bbt(struct nandi_controller *nandi,
> + struct nandi_bbt_info *bbt_info,
> + int bak, uint8_t vers)
> +{
> + struct nand_chip *chip = &nandi->info.chip;
> + loff_t offs;
> + uint32_t block;
> + uint32_t block_lower;
> + uint32_t block_other;
> +
> + block_other = bbt_info->bbt_block[(bak+1)%2];
> + block_lower = nandi->blocks_per_device - NAND_IBBT_NBLOCKS;
> +
> + for (block = bbt_info->bbt_block[bak]; block >= block_lower; block--) {
> + offs = (loff_t)block << chip->phys_erase_shift;
> +
> + /* Skip if block used by other table */
> + if (block == block_other)
> + continue;
> +
> + /* Skip if block is marked bad */
> + if (bbt_is_block_bad(bbt_info->bbt, block))
> + continue;
> +
> + /* Erase block, mark bad and skip on failure */
> + if (bch_erase_block(nandi, offs) & NAND_STATUS_FAIL) {
> + dev_info(nandi->dev,
> + "failed to erase block [%u:0x%012llx] while updating BBT\n",
> + block, offs);
> + vers++;
> + bbt_set_block_mark(bbt_info->bbt, block,
> + BBT_MARK_BAD_WEAR);
> + continue;
> + }
> +
> + /* Write BBT, mark bad and skip on failure */
> + if (bch_write_bbt_data(nandi, bbt_info, block, bak, vers)) {
> + dev_info(nandi->dev,
> + "failed to write BBT to block [%u:0x%012llx]\n",
> + block, offs);
> + vers++;
> + bbt_set_block_mark(bbt_info->bbt, block,
> + BBT_MARK_BAD_WEAR);
> + continue;
> + }
> +
> + /* Success */
> + bbt_info->bbt_block[bak] = block;
> + bbt_info->bbt_vers[bak] = vers;
> + break;
> + }
> +
> + /* No space in BBT area */
> + if (block < block_lower) {
> + dev_err(nandi->dev, "no space left in BBT area\n");
> + dev_err(nandi->dev, "failed to update %s BBT\n", bbt_strs[bak]);
> + return -ENOSPC;
> + }
> +
> + dev_info(nandi->dev, "wrote BBT [%s:%u] at 0x%012llx [%u]\n",
> + bbt_strs[bak], vers, offs, block);
> +
> + return 0;
> +}
> +
> +#define NAND_IBBT_UPDATE_PRIMARY 0x1
> +#define NAND_IBBT_UPDATE_MIRROR 0x2
> +#define NAND_IBBT_UPDATE_BOTH (NAND_IBBT_UPDATE_PRIMARY | \
> + NAND_IBBT_UPDATE_MIRROR)
> +static char *bbt_update_strs[] = {
> + "",
> + "primary",
> + "mirror",
> + "both",
> +};
> +
> +/*
> + * Update Flash-resident BBT(s):
> + * incrementing 'vers' number if required, and ensuring Primary
> + * and Mirror are kept in sync
> + */
> +static int bch_update_bbts(struct nandi_controller *nandi,
> + struct nandi_bbt_info *bbt_info,
> + unsigned int update, uint8_t vers)
> +{
> + int err;
> +
> + dev_info(nandi->dev, "updating %s BBT(s)\n", bbt_update_strs[update]);
> +
> + do {
> + /* Update Primary if specified */
> + if (update & NAND_IBBT_UPDATE_PRIMARY) {
> + err = bch_update_bbt(nandi, bbt_info, NAND_IBBT_PRIMARY,
> + vers);
> + /* Bail out on error (e.g. no space left in BBT area) */
> + if (err)
> + return err;
> +
> + /*
> + * If update resulted in a new BBT version
> + * (e.g. Erase/Write fail on BBT block) update version
> + * here, and force update of other table.
> + */
> + if (bbt_info->bbt_vers[NAND_IBBT_PRIMARY] != vers) {
> + vers = bbt_info->bbt_vers[NAND_IBBT_PRIMARY];
> + update = NAND_IBBT_UPDATE_MIRROR;
> + }
> + }
> +
> + /* Update Mirror if specified */
> + if (update & NAND_IBBT_UPDATE_MIRROR) {
> + err = bch_update_bbt(nandi, bbt_info, NAND_IBBT_MIRROR,
> + vers);
> + /* Bail out on error (e.g. no space left in BBT area) */
> + if (err)
> + return err;
> +
> + /*
> + * If update resulted in a new BBT version
> + * (e.g. Erase/Write fail on BBT block) update version
> + * here, and force update of other table.
> + */
> + if (bbt_info->bbt_vers[NAND_IBBT_MIRROR] != vers) {
> + vers = bbt_info->bbt_vers[NAND_IBBT_MIRROR];
> + update = NAND_IBBT_UPDATE_PRIMARY;
> + }
> + }
> +
> + /* Continue, until Primary and Mirror versions are in sync */
> + } while (bbt_info->bbt_vers[NAND_IBBT_PRIMARY] !=
> + bbt_info->bbt_vers[NAND_IBBT_MIRROR]);
> +
> + return 0;
> +}
> +
> +/* Scan block for IBBT signature */
> +static int bch_find_ibbt_sig(struct nandi_controller *nandi,
> + uint32_t block, int *bak, uint8_t *vers,
> + char *author)
> +{
> + struct nand_chip *chip = &nandi->info.chip;
> + struct mtd_info *mtd = &nandi->info.mtd;
> + struct nand_ibbt_bch_header *ibbt_header;
> + loff_t offs;
> + uint8_t *buf = nandi->page_buf;
> + int match_sig;
> + unsigned int b;
> + unsigned int i;
> + size_t retlen;
> + int ret;
> +
> + nandi->cached_page = -1;
> +
> + /* Load last page of block */
> + offs = (loff_t)block << chip->phys_erase_shift;
> + offs += mtd->erasesize - mtd->writesize;
> + ret = mtd_read(mtd, offs, sizeof(*ibbt_header), &retlen, buf);
> + if (ret < 0) {
> + dev_info(nandi->dev,
> + "Uncorrectable ECC error while scanning BBT signature at block %u [0x%012llx]\n",
> + block, offs);
> + return 0;
> + }
> + ibbt_header = (struct nand_ibbt_bch_header *)buf;
> +
> + /* Test IBBT signature */
> + match_sig = 0;
> + for (b = 0; b < 2 && !match_sig; b++) {
> + match_sig = 1;
> + for (i = 0; i < NAND_IBBT_SIGLEN; i++) {
> + if (ibbt_header->base.signature[i] != ibbt_sigs[b][i]) {
> + match_sig = 0;
> + break;
> + }
> + }
> +
> + }
> +
> + if (!match_sig)
> + return 0; /* Failed to match IBBT signature */
> +
> + /* Test IBBT schema */
> + for (i = 0; i < 4; i++)
> + if (ibbt_header->base.schema[i] != NAND_IBBT_SCHEMA)
> + return 0;
> +
> + /* Test IBBT BCH schema */
> + for (i = 0; i < 4; i++)
> + if (ibbt_header->schema[i] != NAND_IBBT_BCH_SCHEMA)
> + return 0;
> +
> + /* We have a match */
> + *vers = ibbt_header->base.version;
> + *bak = b - 1;
> + strncpy(author, ibbt_header->author, 64);
> +
> + return 1;
> +}
> +
> +/* Search for and load Flash-resident BBT, updating Primary/Mirror if req'd */
> +static int bch_load_bbt(struct nandi_controller *nandi,
> + struct nandi_bbt_info *bbt_info)
> +{
> + struct nand_chip *chip = &nandi->info.chip;
> + struct mtd_info *mtd = &nandi->info.mtd;
> + uint32_t page_size = mtd->writesize;
> + unsigned int update = 0;
> + uint32_t block;
> + loff_t offs;
> + uint8_t vers;
> + char author[64];
> + size_t retlen;
> + int ret;
> + int bak;
> +
> + dev_dbg(nandi->dev, "looking for Flash-resident BBTs\n");
> +
> + bbt_info->bbt_block[0] = 0;
> + bbt_info->bbt_block[1] = 0;
> + bbt_info->bbt_vers[0] = 0;
> + bbt_info->bbt_vers[1] = 0;
> +
> + /* Look for IBBT signatures */
> + for (block = nandi->blocks_per_device - NAND_IBBT_NBLOCKS;
> + block < nandi->blocks_per_device;
> + block++) {
> + offs = (loff_t)block << chip->phys_erase_shift;
> +
> + if (bch_find_ibbt_sig(nandi, block, &bak, &vers, author)) {
> + dev_dbg(nandi->dev,
> + "found BBT [%s:%u] at 0x%012llx [%u] (%s)\n",
> + bbt_strs[bak], vers, offs, block,
> + author);
> +
> + if (bbt_info->bbt_block[bak] == 0 ||
> + ((int8_t)(bbt_info->bbt_vers[bak] - vers)) < 0) {
> + bbt_info->bbt_block[bak] = block;
> + bbt_info->bbt_vers[bak] = vers;
> + }
> + }
> + }
> +
> + /* What have we found? */
> + if (bbt_info->bbt_block[0] == 0 && bbt_info->bbt_block[1] == 0) {
> + /* no primary, no mirror: return error */
> + return 1;
> + } else if (bbt_info->bbt_block[0] == 0) {
> + /* no primary: use mirror, update primary */
> + bak = 1;
> + update = NAND_IBBT_UPDATE_PRIMARY;
> + bbt_info->bbt_block[0] = nandi->blocks_per_device - 1;
> + } else if (bbt_info->bbt_block[1] == 0) {
> + /* no mirror: use primary, update mirror */
> + bak = 0;
> + update = NAND_IBBT_UPDATE_MIRROR;
> + bbt_info->bbt_block[1] = nandi->blocks_per_device - 1;
> + } else if (bbt_info->bbt_vers[0] == bbt_info->bbt_vers[1]) {
> + /* primary == mirror: use primary, no update required */
> + bak = 0;
> + } else if ((int8_t)(bbt_info->bbt_vers[1] -
> + bbt_info->bbt_vers[0]) < 0) {
> + /* primary > mirror: use primary, update mirror */
> + bak = 0;
> + update = NAND_IBBT_UPDATE_MIRROR;
> + } else {
> + /* mirror > primary: use mirror, update primary */
> + bak = 1;
> + update = NAND_IBBT_UPDATE_PRIMARY;
> + }
> +
> + vers = bbt_info->bbt_vers[bak];
> + block = bbt_info->bbt_block[bak];
> + offs = (loff_t)block << chip->phys_erase_shift;
> + dev_info(nandi->dev, "using BBT [%s:%u] at 0x%012llx [%u]\n",
> + bbt_strs[bak], vers, offs, block);
> +
> + /* Read BBT data */
> + ret = mtd_read(mtd, offs, page_size, &retlen, bbt_info->bbt);
> + if (ret < 0) {
> + dev_err(nandi->dev,
> + "error while reading BBT %s:%u] at 0x%012llx [%u]\n",
> + bbt_strs[bak], vers, offs, block);
> + return 1;
> + }
> +
> + /* Update other BBT if required */
> + if (update)
> + bch_update_bbts(nandi, bbt_info, update, vers);
> +
> + return 0;
> +}
> +
> +int bch_scan_bbt(struct mtd_info *mtd)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct nandi_controller *nandi = chip->priv;
> + struct nandi_bbt_info *bbt_info = &nandi->info.bbt_info;
> + int err;
> +
> + /* Load Flash-resident BBT */
> + err = bch_load_bbt(nandi, bbt_info);
> + if (err) {
> + dev_warn(nandi->dev,
> + "failed to find BBTs:"
> + " scanning device for bad-block markers\n");
checkpatch.pl complains:
WARNING: quoted string split across lines
#580: FILE: drivers/mtd/nand/stm_nand_bbt.c:526:
+ "failed to find BBTs:"
+ " scanning device for bad-block markers\n");
> +
> + /* Scan, build, and write BBT */
> + nandi_scan_build_bbt(nandi, bbt_info);
> + err = bch_update_bbts(nandi, bbt_info, NAND_IBBT_UPDATE_BOTH,
> + bbt_info->bbt_vers[0] + 1);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +int bch_block_isbad(struct mtd_info *mtd, loff_t offs, int getchip)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct nandi_controller *nandi = chip->priv;
> +
> + uint32_t block;
> +
> + /* Check for invalid offset */
> + if (offs > mtd->size)
Should be >=
> + return -EINVAL;
> +
> + block = offs >> chip->phys_erase_shift;
> +
> + /* Protect blocks reserved for BBTs */
> + if (block >= (nandi->blocks_per_device - NAND_IBBT_NBLOCKS))
> + return 1;
> +
> + return bbt_is_block_bad(nandi->info.bbt_info.bbt, block);
> +}
> +
> +int bch_block_markbad(struct mtd_info *mtd, loff_t offs)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct nandi_controller *nandi = chip->priv;
> +
> + uint32_t block;
> + int ret;
> +
> + /* Mark bad */
> + block = offs >> chip->phys_erase_shift;
> + bbt_set_block_mark(nandi->info.bbt_info.bbt, block, BBT_MARK_BAD_WEAR);
> +
> + /* Update BBTs, incrementing bbt_vers */
> + ret = bch_update_bbts(nandi, &nandi->info.bbt_info,
> + NAND_IBBT_UPDATE_BOTH,
> + nandi->info.bbt_info.bbt_vers[0] + 1);
> +
> + return ret;
> +}
> +
> +void nandi_dump_bad_blocks(struct nandi_controller *nandi)
> +{
> + struct nand_chip *chip = &nandi->info.chip;
> + int bad_count = 0;
> + uint32_t block;
> + uint8_t *bbt = nandi->info.bbt_info.bbt;
> + uint8_t mark;
> +
> + pr_info("BBT:\n");
> + for (block = 0; block < nandi->blocks_per_device; block++) {
> + mark = bbt_get_block_mark(bbt, block);
> + if (mark != BBT_MARK_GOOD) {
> + pr_info("\t\tBlock 0x%08x [%05u] marked bad [%s]\n",
> + block << chip->phys_erase_shift, block,
> + (mark == BBT_MARK_BAD_FACTORY) ?
> + "Factory" : "Wear");
> + bad_count++;
> + }
> + }
> + if (bad_count == 0)
> + pr_info("\t\tNo bad blocks listed in BBT\n");
> +}
> +EXPORT_SYMBOL(nandi_dump_bad_blocks);
You're currently allowing this source file to be built as a module. So
you should probably specify some module information (MODULE_AUTHOR(),
MODULE_LICENSE(), MODULE_DESCRIPTION()). Or else maybe you should just
avoid the module issues, and just build all your source files into a
single module, a la nand_base.c + nand_bbt.c = nand.{o,ko}.
Brian
More information about the linux-arm-kernel
mailing list