From patchwork Wed Aug 13 09:12:07 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lee Jones X-Patchwork-Id: 35335 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ig0-f200.google.com (mail-ig0-f200.google.com [209.85.213.200]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 975E7203C5 for ; Wed, 13 Aug 2014 09:13:08 +0000 (UTC) Received: by mail-ig0-f200.google.com with SMTP id uq10sf2521966igb.3 for ; Wed, 13 Aug 2014 02:13:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=Tcq6kQDZbMygSz8zu/XOUyFkK3U2DVCAhDBrAOuUtuI=; b=N1PdniRj589iTfg7SKy3cxkARbX0jH27nK/7mZCUF4VJjCyQDm5ZSzD0Gp9D6pX6Dr h/A5ysHwistIpZG6naU3blj+Aelx1FOdyvryQp6HbOf9pnLmDV9TLTAUhfg0JKHZOKlD CoggsAjRBCLSUjmQQE+Qe/YK/iHxrgEz+qbASYp5JhJnkIdS2VIIDDLV+alIkyqBnAdt 6aPq8xBR/WI+2oirXyWHoN8v1L9RDqZjMizTiruk8icj4tCUKJP9oc8v73ZoKcIbi3fq TN5Wi4TNtqfDpTawf8eyL9O+UuKF4lFTft8MRUXvv8ecrRqW9O3rLWmqUfzeMTbWp7H9 CpvQ== X-Gm-Message-State: ALoCoQn4Z+rNVCWWWOBH4KgpWwMZF73L08w7x4lwnj2LZLUW/9BF6qQHeFxJy8nq1d6qVY/ef90y X-Received: by 10.182.66.68 with SMTP id d4mr1721735obt.39.1407921188049; Wed, 13 Aug 2014 02:13:08 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.24.228 with SMTP id 91ls151763qgr.70.gmail; Wed, 13 Aug 2014 02:13:07 -0700 (PDT) X-Received: by 10.52.83.227 with SMTP id t3mr2188260vdy.20.1407921187930; Wed, 13 Aug 2014 02:13:07 -0700 (PDT) Received: from mail-vc0-f178.google.com (mail-vc0-f178.google.com [209.85.220.178]) by mx.google.com with ESMTPS id ga4si743652vdc.38.2014.08.13.02.13.07 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 13 Aug 2014 02:13:07 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.178 as permitted sender) client-ip=209.85.220.178; Received: by mail-vc0-f178.google.com with SMTP id la4so14722497vcb.37 for ; Wed, 13 Aug 2014 02:13:07 -0700 (PDT) X-Received: by 10.52.127.5 with SMTP id nc5mr59414vdb.59.1407921187815; Wed, 13 Aug 2014 02:13:07 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.221.37.5 with SMTP id tc5csp314622vcb; Wed, 13 Aug 2014 02:13:06 -0700 (PDT) X-Received: by 10.68.115.48 with SMTP id jl16mr3010238pbb.78.1407921186351; Wed, 13 Aug 2014 02:13:06 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id qt10si1034576pbb.221.2014.08.13.02.13.05 for ; Wed, 13 Aug 2014 02:13:06 -0700 (PDT) Received-SPF: none (google.com: linux-kernel-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752082AbaHMJM4 (ORCPT + 24 others); Wed, 13 Aug 2014 05:12:56 -0400 Received: from mail-ig0-f182.google.com ([209.85.213.182]:47526 "EHLO mail-ig0-f182.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751973AbaHMJMm (ORCPT ); Wed, 13 Aug 2014 05:12:42 -0400 Received: by mail-ig0-f182.google.com with SMTP id c1so1073138igq.9 for ; Wed, 13 Aug 2014 02:12:41 -0700 (PDT) X-Received: by 10.43.63.134 with SMTP id xe6mr1555457icb.97.1407921161396; Wed, 13 Aug 2014 02:12:41 -0700 (PDT) Received: from localhost.localdomain (host109-148-113-192.range109-148.btcentralplus.com. [109.148.113.192]) by mx.google.com with ESMTPSA id ga11sm6628023igd.8.2014.08.13.02.12.37 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 13 Aug 2014 02:12:41 -0700 (PDT) From: Lee Jones To: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: lee.jones@linaro.org, kernel@stlinux.com, computersforpeace@gmail.com, pekon@pek-sem.com, linux-mtd@lists.infradead.org Subject: [PATCH 8/8] mtd: nand: stm_nand_bch: provide ST's implementation of Back Block Table Date: Wed, 13 Aug 2014 10:12:07 +0100 Message-Id: <1407921127-8590-9-git-send-email-lee.jones@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1407921127-8590-1-git-send-email-lee.jones@linaro.org> References: <1407921127-8590-1-git-send-email-lee.jones@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: lee.jones@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.178 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , 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 --- drivers/mtd/nand/Kconfig | 8 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/stm_nand_bbt.c | 601 +++++++++++++++++++++++++++++++++++++++ include/linux/mtd/stm_nand_bbt.h | 17 ++ 4 files changed, 627 insertions(+) create mode 100644 drivers/mtd/nand/stm_nand_bbt.c create mode 100644 include/linux/mtd/stm_nand_bbt.h 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 + * Lee Jones + * + * 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. + */ + +#include +#include +#include + +/* + * 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"); + + /* 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) + 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); diff --git a/include/linux/mtd/stm_nand_bbt.h b/include/linux/mtd/stm_nand_bbt.h new file mode 100644 index 0000000..70d3c17 --- /dev/null +++ b/include/linux/mtd/stm_nand_bbt.h @@ -0,0 +1,17 @@ +#include + +/* BCH ECC sizes */ +static int bch_ecc_sizes[] = { + [BCH_18BIT_ECC] = 32, + [BCH_30BIT_ECC] = 54, + [BCH_NO_ECC] = 0, +}; + +#if defined(MTD_NAND_STM_BBT) +extern void nandi_dump_bad_blocks(struct nandi_controller *nandi); +extern int bch_scan_bbt(struct mtd_info *mtd); +extern int bch_block_isbad(struct mtd_info *mtd, loff_t offs, int getchip); +extern int bch_block_markbad(struct mtd_info *mtd, loff_t offs); +#else +inline void nandi_dump_bad_blocks(struct nandi_controller *nandi) {} +#endif