mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-30 01:52:51 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			4447 lines
		
	
	
		
			117 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			4447 lines
		
	
	
		
			117 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From c8878d6eda88d844593bb6a6e205eafc27f32b34 Mon Sep 17 00:00:00 2001
 | |
| From: Felix Fietkau <nbd@nbd.name>
 | |
| Date: Thu, 10 Mar 2022 18:09:15 +0100
 | |
| Subject: [PATCH 02/37] update actiontec web7200 support
 | |
| 
 | |
| Signed-off-by: Felix Fietkau <nbd@nbd.name>
 | |
| ---
 | |
|  .../ramips/dts/mt7621_actiontec_web7200.dts   |  108 +-
 | |
|  .../ramips/files/drivers/mtd/nmbm/Kconfig     |   35 -
 | |
|  .../ramips/files/drivers/mtd/nmbm/Makefile    |    6 -
 | |
|  .../ramips/files/drivers/mtd/nmbm/nmbm-core.c | 2936 -----------------
 | |
|  .../files/drivers/mtd/nmbm/nmbm-debug.h       |   20 -
 | |
|  .../files/drivers/mtd/nmbm/nmbm-debug.inl     |    0
 | |
|  .../ramips/files/drivers/mtd/nmbm/nmbm-mtd.c  |  795 -----
 | |
|  .../files/drivers/mtd/nmbm/nmbm-private.h     |  137 -
 | |
|  .../linux/ramips/files/include/nmbm/nmbm-os.h |   69 -
 | |
|  target/linux/ramips/files/include/nmbm/nmbm.h |  102 -
 | |
|  target/linux/ramips/image/mt7621.mk           |   20 +-
 | |
|  .../etc/hotplug.d/ieee80211/10_fix_wifi_mac   |    5 +
 | |
|  .../mt7621/base-files/lib/upgrade/platform.sh |    7 +-
 | |
|  target/linux/ramips/mt7621/config-5.4         |    8 -
 | |
|  .../499-mtd-add-nmbm-support.patch            |   21 -
 | |
|  15 files changed, 84 insertions(+), 4185 deletions(-)
 | |
|  delete mode 100644 target/linux/ramips/files/drivers/mtd/nmbm/Kconfig
 | |
|  delete mode 100644 target/linux/ramips/files/drivers/mtd/nmbm/Makefile
 | |
|  delete mode 100644 target/linux/ramips/files/drivers/mtd/nmbm/nmbm-core.c
 | |
|  delete mode 100644 target/linux/ramips/files/drivers/mtd/nmbm/nmbm-debug.h
 | |
|  delete mode 100644 target/linux/ramips/files/drivers/mtd/nmbm/nmbm-debug.inl
 | |
|  delete mode 100644 target/linux/ramips/files/drivers/mtd/nmbm/nmbm-mtd.c
 | |
|  delete mode 100644 target/linux/ramips/files/drivers/mtd/nmbm/nmbm-private.h
 | |
|  delete mode 100644 target/linux/ramips/files/include/nmbm/nmbm-os.h
 | |
|  delete mode 100644 target/linux/ramips/files/include/nmbm/nmbm.h
 | |
|  delete mode 100644 target/linux/ramips/patches-5.4/499-mtd-add-nmbm-support.patch
 | |
| 
 | |
| diff --git a/target/linux/ramips/dts/mt7621_actiontec_web7200.dts b/target/linux/ramips/dts/mt7621_actiontec_web7200.dts
 | |
| index f112354ec3..a4002f01a8 100644
 | |
| --- a/target/linux/ramips/dts/mt7621_actiontec_web7200.dts
 | |
| +++ b/target/linux/ramips/dts/mt7621_actiontec_web7200.dts
 | |
| @@ -51,68 +51,70 @@
 | |
|  		};
 | |
|  	};
 | |
|  
 | |
| -	nmbm {
 | |
| -		compatible = "generic,nmbm";
 | |
| -		lower-mtd-device = <&nand>;
 | |
| -		forced-create;
 | |
| -		#size-cells = <0x01>;
 | |
| -		#address-cells = <0x01>;
 | |
| -
 | |
| -		partitions {
 | |
| -			compatible = "fixed-partitions";
 | |
| -			#size-cells = <0x01>;
 | |
| -			#address-cells = <0x01>;
 | |
| -
 | |
| -			partition@0 {
 | |
| -				label = "u-boot";
 | |
| -				reg = <0x00 0x80000>;
 | |
| -				read-only;
 | |
| -			};
 | |
| -
 | |
| -			partition@80000 {
 | |
| -				label = "u-boot-env";
 | |
| -				reg = <0x80000 0x80000>;
 | |
| -			};
 | |
| -
 | |
| -			factory: partition@100000 {
 | |
| -				label = "factory";
 | |
| -				reg = <0x100000 0x80000>;
 | |
| -			};
 | |
| -
 | |
| -			partition@180000 {
 | |
| -				compatible = "denx,fit";
 | |
| -				label = "firmware";
 | |
| -				openwrt,cmdline-match = "bootselect=0";
 | |
| -				reg = <0x180000 0x3000000>;
 | |
| -			};
 | |
| -
 | |
| -			partition@3180000 {
 | |
| -				compatible = "denx,fit";
 | |
| -				label = "firmware2";
 | |
| -				openwrt,cmdline-match = "bootselect=1";
 | |
| -				reg = <0x3180000 0x3000000>;
 | |
| -			};
 | |
| -
 | |
| -			partition@6980000 {
 | |
| -				label = "aei_data";
 | |
| -				reg = <0x6980000 0x400000>;
 | |
| -			};
 | |
| -
 | |
| -			partition@6180000 {
 | |
| -				label = "aei_data_ext";
 | |
| -				reg = <0x6180000 0x800000>;
 | |
| -			};
 | |
| -		};
 | |
| -	};
 | |
|  };
 | |
|  
 | |
|  &nand {
 | |
|  	status = "okay";
 | |
|  
 | |
| +	mediatek,nmbm;
 | |
| +	mediatek,bmt-remap-range =
 | |
| +		<0x0 0x580000>,
 | |
| +		<0x3180000 0x3580000>;
 | |
| +
 | |
|  	partitions {
 | |
|  		compatible = "fixed-partitions";
 | |
|  		#address-cells = <1>;
 | |
|  		#size-cells = <1>;
 | |
| +
 | |
| +		partition@0 {
 | |
| +			label = "u-boot";
 | |
| +			reg = <0x00 0x80000>;
 | |
| +			read-only;
 | |
| +		};
 | |
| +
 | |
| +		partition@80000 {
 | |
| +			label = "u-boot-env";
 | |
| +			reg = <0x80000 0x80000>;
 | |
| +		};
 | |
| +
 | |
| +		factory: partition@100000 {
 | |
| +			label = "factory";
 | |
| +			reg = <0x100000 0x80000>;
 | |
| +		};
 | |
| +
 | |
| +		partition@180000 {
 | |
| +			compatible = "denx,fit";
 | |
| +			label = "firmware";
 | |
| +			openwrt,cmdline-match = "bootselect=0";
 | |
| +			reg = <0x180000 0x3000000>;
 | |
| +		};
 | |
| +
 | |
| +		partition@580000 {
 | |
| +			label = "ubi_1";
 | |
| +			reg = <0x580000 0x2c00000>;
 | |
| +		};
 | |
| +
 | |
| +		partition@3180000 {
 | |
| +			compatible = "denx,fit";
 | |
| +			label = "firmware2";
 | |
| +			openwrt,cmdline-match = "bootselect=1";
 | |
| +			reg = <0x3180000 0x3000000>;
 | |
| +		};
 | |
| +
 | |
| +		partition@3580000 {
 | |
| +			label = "ubi_2";
 | |
| +			reg = <0x3580000 0x2c00000>;
 | |
| +		};
 | |
| +
 | |
| +		partition@6980000 {
 | |
| +			label = "aei_data";
 | |
| +			reg = <0x6980000 0x400000>;
 | |
| +		};
 | |
| +
 | |
| +		partition@6180000 {
 | |
| +			label = "aei_data_ext";
 | |
| +			reg = <0x6180000 0x800000>;
 | |
| +		};
 | |
|  	};
 | |
|  };
 | |
|  
 | |
| diff --git a/target/linux/ramips/files/drivers/mtd/nmbm/Kconfig b/target/linux/ramips/files/drivers/mtd/nmbm/Kconfig
 | |
| deleted file mode 100644
 | |
| index 98df305728..0000000000
 | |
| --- a/target/linux/ramips/files/drivers/mtd/nmbm/Kconfig
 | |
| +++ /dev/null
 | |
| @@ -1,35 +0,0 @@
 | |
| -
 | |
| -config NMBM
 | |
| -	bool "Enable NAND mapping block management"
 | |
| -	default n
 | |
| -	select CRC32
 | |
| -
 | |
| -choice
 | |
| -	prompt "Default log level"
 | |
| -	depends on NMBM
 | |
| -	default NMBM_LOG_LEVEL_INFO
 | |
| -
 | |
| -config NMBM_LOG_LEVEL_DEBUG
 | |
| -	bool "0 - Debug"
 | |
| -
 | |
| -config NMBM_LOG_LEVEL_INFO
 | |
| -	bool "1 - Info"
 | |
| -
 | |
| -config NMBM_LOG_LEVEL_WARN
 | |
| -	bool "2 - Warn"
 | |
| -
 | |
| -config NMBM_LOG_LEVEL_ERR
 | |
| -	bool "3 - Error"
 | |
| -
 | |
| -config NMBM_LOG_LEVEL_EMERG
 | |
| -	bool "4 - Emergency"
 | |
| -
 | |
| -config NMBM_LOG_LEVEL_NONE
 | |
| -	bool "5 - None"
 | |
| -
 | |
| -endchoice
 | |
| -
 | |
| -config NMBM_MTD
 | |
| -	bool "Enable MTD based NAND mapping block management"
 | |
| -	default n
 | |
| -	depends on NMBM
 | |
| diff --git a/target/linux/ramips/files/drivers/mtd/nmbm/Makefile b/target/linux/ramips/files/drivers/mtd/nmbm/Makefile
 | |
| deleted file mode 100644
 | |
| index 46e6d50a80..0000000000
 | |
| --- a/target/linux/ramips/files/drivers/mtd/nmbm/Makefile
 | |
| +++ /dev/null
 | |
| @@ -1,6 +0,0 @@
 | |
| -# SPDX-License-Identifier: GPL-2.0
 | |
| -#
 | |
| -# (C) Copyright 2020 MediaTek Inc. All rights reserved.
 | |
| -
 | |
| -obj-$(CONFIG_NMBM) += nmbm-core.o
 | |
| -obj-$(CONFIG_NMBM_MTD) += nmbm-mtd.o
 | |
| diff --git a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-core.c b/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-core.c
 | |
| deleted file mode 100644
 | |
| index 18dfb6adda..0000000000
 | |
| --- a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-core.c
 | |
| +++ /dev/null
 | |
| @@ -1,2936 +0,0 @@
 | |
| -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 | |
| -/*
 | |
| - * Copyright (C) 2021 MediaTek Inc. All Rights Reserved.
 | |
| - *
 | |
| - * Author: Weijie Gao <weijie.gao@mediatek.com>
 | |
| - */
 | |
| -
 | |
| -#include "nmbm-private.h"
 | |
| -
 | |
| -#include "nmbm-debug.h"
 | |
| -
 | |
| -#define NMBM_VER_MAJOR			1
 | |
| -#define NMBM_VER_MINOR			0
 | |
| -#define NMBM_VER			NMBM_VERSION_MAKE(NMBM_VER_MAJOR, \
 | |
| -							  NMBM_VER_MINOR)
 | |
| -
 | |
| -#define NMBM_ALIGN(v, a)		(((v) + (a) - 1) & ~((a) - 1))
 | |
| -
 | |
| -/*****************************************************************************/
 | |
| -/* Logging related functions */
 | |
| -/*****************************************************************************/
 | |
| -
 | |
| -/*
 | |
| - * nmbm_log_lower - Print log using OS specific routine
 | |
| - * @nld: NMBM lower device structure
 | |
| - * @level: log level
 | |
| - * @fmt: format string
 | |
| - */
 | |
| -static void nmbm_log_lower(struct nmbm_lower_device *nld,
 | |
| -			   enum nmbm_log_category level, const char *fmt, ...)
 | |
| -{
 | |
| -	va_list ap;
 | |
| -
 | |
| -	if (!nld->logprint)
 | |
| -		return;
 | |
| -
 | |
| -	va_start(ap, fmt);
 | |
| -	nld->logprint(nld->arg, level, fmt, ap);
 | |
| -	va_end(ap);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_log - Print log using OS specific routine
 | |
| - * @ni: NMBM instance structure
 | |
| - * @level: log level
 | |
| - * @fmt: format string
 | |
| - */
 | |
| -static void nmbm_log(struct nmbm_instance *ni, enum nmbm_log_category level,
 | |
| -		     const char *fmt, ...)
 | |
| -{
 | |
| -	va_list ap;
 | |
| -
 | |
| -	if (!ni)
 | |
| -		return;
 | |
| -
 | |
| -	if (!ni->lower.logprint || level < ni->log_display_level)
 | |
| -		return;
 | |
| -
 | |
| -	va_start(ap, fmt);
 | |
| -	ni->lower.logprint(ni->lower.arg, level, fmt, ap);
 | |
| -	va_end(ap);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_set_log_level - Set log display level
 | |
| - * @ni: NMBM instance structure
 | |
| - * @level: log display level
 | |
| - */
 | |
| -enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni,
 | |
| -					  enum nmbm_log_category level)
 | |
| -{
 | |
| -	enum nmbm_log_category old;
 | |
| -
 | |
| -	if (!ni)
 | |
| -		return __NMBM_LOG_MAX;
 | |
| -
 | |
| -	old = ni->log_display_level;
 | |
| -	ni->log_display_level = level;
 | |
| -	return old;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nlog_table_creation - Print log of table creation event
 | |
| - * @ni: NMBM instance structure
 | |
| - * @main_table: whether the table is main info table
 | |
| - * @start_ba: start block address of the table
 | |
| - * @end_ba: block address after the end of the table
 | |
| - */
 | |
| -static void nlog_table_creation(struct nmbm_instance *ni, bool main_table,
 | |
| -			       uint32_t start_ba, uint32_t end_ba)
 | |
| -{
 | |
| -	if (start_ba == end_ba - 1)
 | |
| -		nlog_info(ni, "%s info table has been written to block %u\n",
 | |
| -			 main_table ? "Main" : "Backup", start_ba);
 | |
| -	else
 | |
| -		nlog_info(ni, "%s info table has been written to block %u-%u\n",
 | |
| -			 main_table ? "Main" : "Backup", start_ba, end_ba - 1);
 | |
| -
 | |
| -	nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nlog_table_update - Print log of table update event
 | |
| - * @ni: NMBM instance structure
 | |
| - * @main_table: whether the table is main info table
 | |
| - * @start_ba: start block address of the table
 | |
| - * @end_ba: block address after the end of the table
 | |
| - */
 | |
| -static void nlog_table_update(struct nmbm_instance *ni, bool main_table,
 | |
| -			     uint32_t start_ba, uint32_t end_ba)
 | |
| -{
 | |
| -	if (start_ba == end_ba - 1)
 | |
| -		nlog_debug(ni, "%s info table has been updated in block %u\n",
 | |
| -			  main_table ? "Main" : "Backup", start_ba);
 | |
| -	else
 | |
| -		nlog_debug(ni, "%s info table has been updated in block %u-%u\n",
 | |
| -			  main_table ? "Main" : "Backup", start_ba, end_ba - 1);
 | |
| -
 | |
| -	nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nlog_table_found - Print log of table found event
 | |
| - * @ni: NMBM instance structure
 | |
| - * @first_table: whether the table is first found info table
 | |
| - * @write_count: write count of the info table
 | |
| - * @start_ba: start block address of the table
 | |
| - * @end_ba: block address after the end of the table
 | |
| - */
 | |
| -static void nlog_table_found(struct nmbm_instance *ni, bool first_table,
 | |
| -			    uint32_t write_count, uint32_t start_ba,
 | |
| -			    uint32_t end_ba)
 | |
| -{
 | |
| -	if (start_ba == end_ba - 1)
 | |
| -		nlog_info(ni, "%s info table with writecount %u found in block %u\n",
 | |
| -			 first_table ? "First" : "Second", write_count,
 | |
| -			 start_ba);
 | |
| -	else
 | |
| -		nlog_info(ni, "%s info table with writecount %u found in block %u-%u\n",
 | |
| -			 first_table ? "First" : "Second", write_count,
 | |
| -			 start_ba, end_ba - 1);
 | |
| -
 | |
| -	nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
 | |
| -}
 | |
| -
 | |
| -/*****************************************************************************/
 | |
| -/* Address conversion functions */
 | |
| -/*****************************************************************************/
 | |
| -
 | |
| -/*
 | |
| - * addr2ba - Convert a linear address to block address
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: Linear address
 | |
| - */
 | |
| -static uint32_t addr2ba(struct nmbm_instance *ni, uint64_t addr)
 | |
| -{
 | |
| -	return addr >> ni->erasesize_shift;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * ba2addr - Convert a block address to linear address
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: Block address
 | |
| - */
 | |
| -static uint64_t ba2addr(struct nmbm_instance *ni, uint32_t ba)
 | |
| -{
 | |
| -	return (uint64_t)ba << ni->erasesize_shift;
 | |
| -}
 | |
| -/*
 | |
| - * size2blk - Get minimum required blocks for storing specific size of data
 | |
| - * @ni: NMBM instance structure
 | |
| - * @size: size for storing
 | |
| - */
 | |
| -static uint32_t size2blk(struct nmbm_instance *ni, uint64_t size)
 | |
| -{
 | |
| -	return (size + ni->lower.erasesize - 1) >> ni->erasesize_shift;
 | |
| -}
 | |
| -
 | |
| -/*****************************************************************************/
 | |
| -/* High level NAND chip APIs */
 | |
| -/*****************************************************************************/
 | |
| -
 | |
| -/*
 | |
| - * nmbm_reset_chip - Reset NAND device
 | |
| - * @nld: Lower NAND chip structure
 | |
| - */
 | |
| -static void nmbm_reset_chip(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	if (ni->lower.reset_chip)
 | |
| -		ni->lower.reset_chip(ni->lower.arg);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_read_phys_page - Read page with retry
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: linear address where the data will be read from
 | |
| - * @data: the main data to be read
 | |
| - * @oob: the oob data to be read
 | |
| - * @mode: mode for processing oob data
 | |
| - *
 | |
| - * Read a page for at most NMBM_TRY_COUNT times.
 | |
| - *
 | |
| - * Return 0 for success, positive value for corrected bitflip count,
 | |
| - * -EBADMSG for ecc error, other negative values for other errors
 | |
| - */
 | |
| -static int nmbm_read_phys_page(struct nmbm_instance *ni, uint64_t addr,
 | |
| -			       void *data, void *oob, enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	int tries, ret;
 | |
| -
 | |
| -	for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
 | |
| -		ret = ni->lower.read_page(ni->lower.arg, addr, data, oob, mode);
 | |
| -		if (ret >= 0)
 | |
| -			return ret;
 | |
| -
 | |
| -		nmbm_reset_chip(ni);
 | |
| -	}
 | |
| -
 | |
| -	if (ret != -EBADMSG)
 | |
| -		nlog_err(ni, "Page read failed at address 0x%08llx\n", addr);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_write_phys_page - Write page with retry
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: linear address where the data will be written to
 | |
| - * @data: the main data to be written
 | |
| - * @oob: the oob data to be written
 | |
| - * @mode: mode for processing oob data
 | |
| - *
 | |
| - * Write a page for at most NMBM_TRY_COUNT times.
 | |
| - */
 | |
| -static bool nmbm_write_phys_page(struct nmbm_instance *ni, uint64_t addr,
 | |
| -				 const void *data, const void *oob,
 | |
| -				 enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	int tries, ret;
 | |
| -
 | |
| -	if (ni->lower.flags & NMBM_F_READ_ONLY) {
 | |
| -		nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
 | |
| -		ret = ni->lower.write_page(ni->lower.arg, addr, data, oob, mode);
 | |
| -		if (!ret)
 | |
| -			return true;
 | |
| -
 | |
| -		nmbm_reset_chip(ni);
 | |
| -	}
 | |
| -
 | |
| -	nlog_err(ni, "Page write failed at address 0x%08llx\n", addr);
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_erase_phys_block - Erase a block with retry
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: Linear address
 | |
| - *
 | |
| - * Erase a block for at most NMBM_TRY_COUNT times.
 | |
| - */
 | |
| -static bool nmbm_erase_phys_block(struct nmbm_instance *ni, uint64_t addr)
 | |
| -{
 | |
| -	int tries, ret;
 | |
| -
 | |
| -	if (ni->lower.flags & NMBM_F_READ_ONLY) {
 | |
| -		nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
 | |
| -		ret = ni->lower.erase_block(ni->lower.arg, addr);
 | |
| -		if (!ret)
 | |
| -			return true;
 | |
| -
 | |
| -		nmbm_reset_chip(ni);
 | |
| -	}
 | |
| -
 | |
| -	nlog_err(ni, "Block erasure failed at address 0x%08llx\n", addr);
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_check_bad_phys_block - Check whether a block is marked bad in OOB
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: block address
 | |
| - */
 | |
| -static bool nmbm_check_bad_phys_block(struct nmbm_instance *ni, uint32_t ba)
 | |
| -{
 | |
| -	uint64_t addr = ba2addr(ni, ba);
 | |
| -	int ret;
 | |
| -
 | |
| -	if (ni->lower.is_bad_block)
 | |
| -		return ni->lower.is_bad_block(ni->lower.arg, addr);
 | |
| -
 | |
| -	/* Treat ECC error as read success */
 | |
| -	ret = nmbm_read_phys_page(ni, addr, NULL,
 | |
| -				  ni->page_cache + ni->lower.writesize,
 | |
| -				  NMBM_MODE_RAW);
 | |
| -	if (ret < 0 && ret != -EBADMSG)
 | |
| -		return true;
 | |
| -
 | |
| -	return ni->page_cache[ni->lower.writesize] != 0xff;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_mark_phys_bad_block - Mark a block bad
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: Linear address
 | |
| - */
 | |
| -static int nmbm_mark_phys_bad_block(struct nmbm_instance *ni, uint32_t ba)
 | |
| -{
 | |
| -	uint64_t addr = ba2addr(ni, ba);
 | |
| -	enum nmbm_log_category level;
 | |
| -	uint32_t off;
 | |
| -
 | |
| -	if (ni->lower.flags & NMBM_F_READ_ONLY) {
 | |
| -		nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	nlog_info(ni, "Block %u [0x%08llx] will be marked bad\n", ba, addr);
 | |
| -
 | |
| -	if (ni->lower.mark_bad_block)
 | |
| -		return ni->lower.mark_bad_block(ni->lower.arg, addr);
 | |
| -
 | |
| -	/* Whole page set to 0x00 */
 | |
| -	memset(ni->page_cache, 0, ni->rawpage_size);
 | |
| -
 | |
| -	/* Write to all pages within this block, disable all errors */
 | |
| -	level = nmbm_set_log_level(ni, __NMBM_LOG_MAX);
 | |
| -
 | |
| -	for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
 | |
| -		nmbm_write_phys_page(ni, addr + off, ni->page_cache,
 | |
| -				     ni->page_cache + ni->lower.writesize,
 | |
| -				     NMBM_MODE_RAW);
 | |
| -	}
 | |
| -
 | |
| -	nmbm_set_log_level(ni, level);
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -/*****************************************************************************/
 | |
| -/* NMBM related functions */
 | |
| -/*****************************************************************************/
 | |
| -
 | |
| -/*
 | |
| - * nmbm_check_header - Check whether a NMBM structure is valid
 | |
| - * @data: pointer to a NMBM structure with a NMBM header at beginning
 | |
| - * @size: Size of the buffer pointed by @header
 | |
| - *
 | |
| - * The size of the NMBM structure may be larger than NMBM header,
 | |
| - * e.g. block mapping table and block state table.
 | |
| - */
 | |
| -static bool nmbm_check_header(const void *data, uint32_t size)
 | |
| -{
 | |
| -	const struct nmbm_header *header = data;
 | |
| -	struct nmbm_header nhdr;
 | |
| -	uint32_t new_checksum;
 | |
| -
 | |
| -	/*
 | |
| -	 * Make sure expected structure size is equal or smaller than
 | |
| -	 * buffer size.
 | |
| -	 */
 | |
| -	if (header->size > size)
 | |
| -		return false;
 | |
| -
 | |
| -	memcpy(&nhdr, data, sizeof(nhdr));
 | |
| -
 | |
| -	nhdr.checksum = 0;
 | |
| -	new_checksum = nmbm_crc32(0, &nhdr, sizeof(nhdr));
 | |
| -	if (header->size > sizeof(nhdr))
 | |
| -		new_checksum = nmbm_crc32(new_checksum,
 | |
| -			(const uint8_t *)data + sizeof(nhdr),
 | |
| -			header->size - sizeof(nhdr));
 | |
| -
 | |
| -	if (header->checksum != new_checksum)
 | |
| -		return false;
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_update_checksum - Update checksum of a NMBM structure
 | |
| - * @header: pointer to a NMBM structure with a NMBM header at beginning
 | |
| - *
 | |
| - * The size of the NMBM structure must be specified by @header->size
 | |
| - */
 | |
| -static void nmbm_update_checksum(struct nmbm_header *header)
 | |
| -{
 | |
| -	header->checksum = 0;
 | |
| -	header->checksum = nmbm_crc32(0, header, header->size);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_get_spare_block_count - Calculate number of blocks should be reserved
 | |
| - * @block_count: number of blocks of data
 | |
| - *
 | |
| - * Calculate number of blocks should be reserved for data
 | |
| - */
 | |
| -static uint32_t nmbm_get_spare_block_count(uint32_t block_count)
 | |
| -{
 | |
| -	uint32_t val;
 | |
| -
 | |
| -	val = (block_count + NMBM_SPARE_BLOCK_DIV / 2) / NMBM_SPARE_BLOCK_DIV;
 | |
| -	val *= NMBM_SPARE_BLOCK_MULTI;
 | |
| -
 | |
| -	if (val < NMBM_SPARE_BLOCK_MIN)
 | |
| -		val = NMBM_SPARE_BLOCK_MIN;
 | |
| -
 | |
| -	return val;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_get_block_state_raw - Get state of a block from raw block state table
 | |
| - * @block_state: pointer to raw block state table (bitmap)
 | |
| - * @ba: block address
 | |
| - */
 | |
| -static uint32_t nmbm_get_block_state_raw(nmbm_bitmap_t *block_state,
 | |
| -					 uint32_t ba)
 | |
| -{
 | |
| -	uint32_t unit, shift;
 | |
| -
 | |
| -	unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT;
 | |
| -	shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK;
 | |
| -
 | |
| -	return (block_state[unit] >> shift) & BLOCK_ST_MASK;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_get_block_state - Get state of a block from block state table
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: block address
 | |
| - */
 | |
| -static uint32_t nmbm_get_block_state(struct nmbm_instance *ni, uint32_t ba)
 | |
| -{
 | |
| -	return nmbm_get_block_state_raw(ni->block_state, ba);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_set_block_state - Set state of a block to block state table
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: block address
 | |
| - * @state: block state
 | |
| - *
 | |
| - * Set state of a block. If the block state changed, ni->block_state_changed
 | |
| - * will be increased.
 | |
| - */
 | |
| -static bool nmbm_set_block_state(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				 uint32_t state)
 | |
| -{
 | |
| -	uint32_t unit, shift, orig;
 | |
| -	nmbm_bitmap_t uv;
 | |
| -
 | |
| -	unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT;
 | |
| -	shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK;
 | |
| -
 | |
| -	orig = (ni->block_state[unit] >> shift) & BLOCK_ST_MASK;
 | |
| -	state &= BLOCK_ST_MASK;
 | |
| -
 | |
| -	uv = ni->block_state[unit] & (~(BLOCK_ST_MASK << shift));
 | |
| -	uv |= state << shift;
 | |
| -	ni->block_state[unit] = uv;
 | |
| -
 | |
| -	if (state == BLOCK_ST_BAD)
 | |
| -		nmbm_mark_block_color_bad(ni, ba);
 | |
| -
 | |
| -	if (orig != state) {
 | |
| -		ni->block_state_changed++;
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_block_walk_asc - Skip specified number of good blocks, ascending addr.
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: start physical block address
 | |
| - * @nba: return physical block address after walk
 | |
| - * @count: number of good blocks to be skipped
 | |
| - * @limit: highest block address allowed for walking
 | |
| - *
 | |
| - * Start from @ba, skipping any bad blocks, counting @count good blocks, and
 | |
| - * return the next good block address.
 | |
| - *
 | |
| - * If no enough good blocks counted while @limit reached, false will be returned.
 | |
| - *
 | |
| - * If @count == 0, nearest good block address will be returned.
 | |
| - * @limit is not counted in walking.
 | |
| - */
 | |
| -static bool nmbm_block_walk_asc(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				uint32_t *nba, uint32_t count,
 | |
| -				uint32_t limit)
 | |
| -{
 | |
| -	int32_t nblock = count;
 | |
| -
 | |
| -	if (limit >= ni->block_count)
 | |
| -		limit = ni->block_count - 1;
 | |
| -
 | |
| -	while (ba < limit) {
 | |
| -		if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD)
 | |
| -			nblock--;
 | |
| -
 | |
| -		if (nblock < 0) {
 | |
| -			*nba = ba;
 | |
| -			return true;
 | |
| -		}
 | |
| -
 | |
| -		ba++;
 | |
| -	}
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_block_walk_desc - Skip specified number of good blocks, descending addr
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: start physical block address
 | |
| - * @nba: return physical block address after walk
 | |
| - * @count: number of good blocks to be skipped
 | |
| - * @limit: lowest block address allowed for walking
 | |
| - *
 | |
| - * Start from @ba, skipping any bad blocks, counting @count good blocks, and
 | |
| - * return the next good block address.
 | |
| - *
 | |
| - * If no enough good blocks counted while @limit reached, false will be returned.
 | |
| - *
 | |
| - * If @count == 0, nearest good block address will be returned.
 | |
| - * @limit is not counted in walking.
 | |
| - */
 | |
| -static bool nmbm_block_walk_desc(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				 uint32_t *nba, uint32_t count, uint32_t limit)
 | |
| -{
 | |
| -	int32_t nblock = count;
 | |
| -
 | |
| -	if (limit >= ni->block_count)
 | |
| -		limit = ni->block_count - 1;
 | |
| -
 | |
| -	while (ba > limit) {
 | |
| -		if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD)
 | |
| -			nblock--;
 | |
| -
 | |
| -		if (nblock < 0) {
 | |
| -			*nba = ba;
 | |
| -			return true;
 | |
| -		}
 | |
| -
 | |
| -		ba--;
 | |
| -	}
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_block_walk - Skip specified number of good blocks from curr. block addr
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ascending: whether to walk ascending
 | |
| - * @ba: start physical block address
 | |
| - * @nba: return physical block address after walk
 | |
| - * @count: number of good blocks to be skipped
 | |
| - * @limit: highest/lowest block address allowed for walking
 | |
| - *
 | |
| - * Start from @ba, skipping any bad blocks, counting @count good blocks, and
 | |
| - * return the next good block address.
 | |
| - *
 | |
| - * If no enough good blocks counted while @limit reached, false will be returned.
 | |
| - *
 | |
| - * If @count == 0, nearest good block address will be returned.
 | |
| - * @limit can be set to negative if no limit required.
 | |
| - * @limit is not counted in walking.
 | |
| - */
 | |
| -static bool nmbm_block_walk(struct nmbm_instance *ni, bool ascending,
 | |
| -			    uint32_t ba, uint32_t *nba, int32_t count,
 | |
| -			    int32_t limit)
 | |
| -{
 | |
| -	if (ascending)
 | |
| -		return nmbm_block_walk_asc(ni, ba, nba, count, limit);
 | |
| -
 | |
| -	return nmbm_block_walk_desc(ni, ba, nba, count, limit);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_scan_badblocks - Scan and record all bad blocks
 | |
| - * @ni: NMBM instance structure
 | |
| - *
 | |
| - * Scan the entire lower NAND chip and record all bad blocks in to block state
 | |
| - * table.
 | |
| - */
 | |
| -static void nmbm_scan_badblocks(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	uint32_t ba;
 | |
| -
 | |
| -	for (ba = 0; ba < ni->block_count; ba++) {
 | |
| -		if (nmbm_check_bad_phys_block(ni, ba)) {
 | |
| -			nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -			nlog_info(ni, "Bad block %u [0x%08llx]\n", ba,
 | |
| -				 ba2addr(ni, ba));
 | |
| -		}
 | |
| -	}
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_build_mapping_table - Build initial block mapping table
 | |
| - * @ni: NMBM instance structure
 | |
| - *
 | |
| - * The initial mapping table will be compatible with the stratage of
 | |
| - * factory production.
 | |
| - */
 | |
| -static void nmbm_build_mapping_table(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	uint32_t pb, lb;
 | |
| -
 | |
| -	for (pb = 0, lb = 0; pb < ni->mgmt_start_ba; pb++) {
 | |
| -		if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
 | |
| -			continue;
 | |
| -
 | |
| -		/* Always map to the next good block */
 | |
| -		ni->block_mapping[lb++] = pb;
 | |
| -	}
 | |
| -
 | |
| -	ni->data_block_count = lb;
 | |
| -
 | |
| -	/* Unusable/Management blocks */
 | |
| -	for (pb = lb; pb < ni->block_count; pb++)
 | |
| -		ni->block_mapping[pb] = -1;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_erase_block_and_check - Erase a block and check its usability
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: block address to be erased
 | |
| - *
 | |
| - * Erase a block anc check its usability
 | |
| - *
 | |
| - * Return true if the block is usable, false if erasure failure or the block
 | |
| - * has too many bitflips.
 | |
| - */
 | |
| -static bool nmbm_erase_block_and_check(struct nmbm_instance *ni, uint32_t ba)
 | |
| -{
 | |
| -	uint64_t addr, off;
 | |
| -	bool success;
 | |
| -	int ret;
 | |
| -
 | |
| -	success = nmbm_erase_phys_block(ni, ba2addr(ni, ba));
 | |
| -	if (!success)
 | |
| -		return false;
 | |
| -
 | |
| -	if (!(ni->lower.flags & NMBM_F_EMPTY_PAGE_ECC_OK))
 | |
| -		return true;
 | |
| -
 | |
| -	/* Check every page to make sure there aren't too many bitflips */
 | |
| -
 | |
| -	addr = ba2addr(ni, ba);
 | |
| -
 | |
| -	for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL,
 | |
| -					  NMBM_MODE_PLACE_OOB);
 | |
| -		if (ret == -EBADMSG) {
 | |
| -			/*
 | |
| -			 * NMBM_F_EMPTY_PAGE_ECC_OK means the empty page is
 | |
| -			 * still protected by ECC. So reading pages with ECC
 | |
| -			 * enabled and -EBADMSG means there are too many
 | |
| -			 * bitflips that can't be recovered, and the block
 | |
| -			 * containing the page should be marked bad.
 | |
| -			 */
 | |
| -			nlog_err(ni,
 | |
| -				 "Too many bitflips in empty page at 0x%llx\n",
 | |
| -				 addr + off);
 | |
| -			return false;
 | |
| -		}
 | |
| -	}
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_erase_range - Erase a range of blocks
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: block address where the erasure will start
 | |
| - * @limit: top block address allowed for erasure
 | |
| - *
 | |
| - * Erase blocks within the specific range. Newly-found bad blocks will be
 | |
| - * marked.
 | |
| - *
 | |
| - * @limit is not counted into the allowed erasure address.
 | |
| - */
 | |
| -static void nmbm_erase_range(struct nmbm_instance *ni, uint32_t ba,
 | |
| -			     uint32_t limit)
 | |
| -{
 | |
| -	bool success;
 | |
| -
 | |
| -	while (ba < limit) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
 | |
| -			goto next_block;
 | |
| -
 | |
| -		/* Insurance to detect unexpected bad block marked by user */
 | |
| -		if (nmbm_check_bad_phys_block(ni, ba)) {
 | |
| -			nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -			goto next_block;
 | |
| -		}
 | |
| -
 | |
| -		success = nmbm_erase_block_and_check(ni, ba);
 | |
| -		if (success)
 | |
| -			goto next_block;
 | |
| -
 | |
| -		nmbm_mark_phys_bad_block(ni, ba);
 | |
| -		nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -
 | |
| -	next_block:
 | |
| -		ba++;
 | |
| -	}
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_write_repeated_data - Write critical data to a block with retry
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: block address where the data will be written to
 | |
| - * @data: the data to be written
 | |
| - * @size: size of the data
 | |
| - *
 | |
| - * Write data to every page of the block. Success only if all pages within
 | |
| - * this block have been successfully written.
 | |
| - *
 | |
| - * Make sure data size is not bigger than one page.
 | |
| - *
 | |
| - * This function will write and verify every page for at most
 | |
| - * NMBM_TRY_COUNT times.
 | |
| - */
 | |
| -static bool nmbm_write_repeated_data(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				     const void *data, uint32_t size)
 | |
| -{
 | |
| -	uint64_t addr, off;
 | |
| -	bool success;
 | |
| -	int ret;
 | |
| -
 | |
| -	if (size > ni->lower.writesize)
 | |
| -		return false;
 | |
| -
 | |
| -	addr = ba2addr(ni, ba);
 | |
| -
 | |
| -	for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		/* Prepare page data. fill 0xff to unused region */
 | |
| -		memcpy(ni->page_cache, data, size);
 | |
| -		memset(ni->page_cache + size, 0xff, ni->rawpage_size - size);
 | |
| -
 | |
| -		success = nmbm_write_phys_page(ni, addr + off, ni->page_cache,
 | |
| -					       NULL, NMBM_MODE_PLACE_OOB);
 | |
| -		if (!success)
 | |
| -			return false;
 | |
| -
 | |
| -		/* Verify the data just written. ECC error indicates failure */
 | |
| -		ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL,
 | |
| -					  NMBM_MODE_PLACE_OOB);
 | |
| -		if (ret < 0)
 | |
| -			return false;
 | |
| -
 | |
| -		if (memcmp(ni->page_cache, data, size))
 | |
| -			return false;
 | |
| -	}
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_write_signature - Write signature to NAND chip
 | |
| - * @ni: NMBM instance structure
 | |
| - * @limit: top block address allowed for writing
 | |
| - * @signature: the signature to be written
 | |
| - * @signature_ba: the actual block address where signature is written to
 | |
| - *
 | |
| - * Write signature within a specific range, from chip bottom to limit.
 | |
| - * At most one block will be written.
 | |
| - *
 | |
| - * @limit is not counted into the allowed write address.
 | |
| - */
 | |
| -static bool nmbm_write_signature(struct nmbm_instance *ni, uint32_t limit,
 | |
| -				 const struct nmbm_signature *signature,
 | |
| -				 uint32_t *signature_ba)
 | |
| -{
 | |
| -	uint32_t ba = ni->block_count - 1;
 | |
| -	bool success;
 | |
| -
 | |
| -	while (ba > limit) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
 | |
| -			goto next_block;
 | |
| -
 | |
| -		/* Insurance to detect unexpected bad block marked by user */
 | |
| -		if (nmbm_check_bad_phys_block(ni, ba)) {
 | |
| -			nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -			goto next_block;
 | |
| -		}
 | |
| -
 | |
| -		success = nmbm_erase_block_and_check(ni, ba);
 | |
| -		if (!success)
 | |
| -			goto skip_bad_block;
 | |
| -
 | |
| -		success = nmbm_write_repeated_data(ni, ba, signature,
 | |
| -						   sizeof(*signature));
 | |
| -		if (success) {
 | |
| -			*signature_ba = ba;
 | |
| -			return true;
 | |
| -		}
 | |
| -
 | |
| -	skip_bad_block:
 | |
| -		nmbm_mark_phys_bad_block(ni, ba);
 | |
| -		nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -
 | |
| -	next_block:
 | |
| -		ba--;
 | |
| -	};
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbn_read_data - Read data
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: linear address where the data will be read from
 | |
| - * @data: the data to be read
 | |
| - * @size: the size of data
 | |
| - *
 | |
| - * Read data range.
 | |
| - * Every page will be tried for at most NMBM_TRY_COUNT times.
 | |
| - *
 | |
| - * Return 0 for success, positive value for corrected bitflip count,
 | |
| - * -EBADMSG for ecc error, other negative values for other errors
 | |
| - */
 | |
| -static int nmbn_read_data(struct nmbm_instance *ni, uint64_t addr, void *data,
 | |
| -			  uint32_t size)
 | |
| -{
 | |
| -	uint64_t off = addr;
 | |
| -	uint8_t *ptr = data;
 | |
| -	uint32_t sizeremain = size, chunksize, leading;
 | |
| -	int ret;
 | |
| -
 | |
| -	while (sizeremain) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		leading = off & ni->writesize_mask;
 | |
| -		chunksize = ni->lower.writesize - leading;
 | |
| -		if (chunksize > sizeremain)
 | |
| -			chunksize = sizeremain;
 | |
| -
 | |
| -		if (chunksize == ni->lower.writesize) {
 | |
| -			ret = nmbm_read_phys_page(ni, off - leading, ptr, NULL,
 | |
| -						  NMBM_MODE_PLACE_OOB);
 | |
| -			if (ret < 0)
 | |
| -				return ret;
 | |
| -		} else {
 | |
| -			ret = nmbm_read_phys_page(ni, off - leading,
 | |
| -						  ni->page_cache, NULL,
 | |
| -						  NMBM_MODE_PLACE_OOB);
 | |
| -			if (ret < 0)
 | |
| -				return ret;
 | |
| -
 | |
| -			memcpy(ptr, ni->page_cache + leading, chunksize);
 | |
| -		}
 | |
| -
 | |
| -		off += chunksize;
 | |
| -		ptr += chunksize;
 | |
| -		sizeremain -= chunksize;
 | |
| -	}
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbn_write_verify_data - Write data with validation
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: linear address where the data will be written to
 | |
| - * @data: the data to be written
 | |
| - * @size: the size of data
 | |
| - *
 | |
| - * Write data and verify.
 | |
| - * Every page will be tried for at most NMBM_TRY_COUNT times.
 | |
| - */
 | |
| -static bool nmbn_write_verify_data(struct nmbm_instance *ni, uint64_t addr,
 | |
| -				   const void *data, uint32_t size)
 | |
| -{
 | |
| -	uint64_t off = addr;
 | |
| -	const uint8_t *ptr = data;
 | |
| -	uint32_t sizeremain = size, chunksize, leading;
 | |
| -	bool success;
 | |
| -	int ret;
 | |
| -
 | |
| -	while (sizeremain) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		leading = off & ni->writesize_mask;
 | |
| -		chunksize = ni->lower.writesize - leading;
 | |
| -		if (chunksize > sizeremain)
 | |
| -			chunksize = sizeremain;
 | |
| -
 | |
| -		/* Prepare page data. fill 0xff to unused region */
 | |
| -		memset(ni->page_cache, 0xff, ni->rawpage_size);
 | |
| -		memcpy(ni->page_cache + leading, ptr, chunksize);
 | |
| -
 | |
| -		success = nmbm_write_phys_page(ni, off - leading,
 | |
| -					       ni->page_cache, NULL,
 | |
| -					       NMBM_MODE_PLACE_OOB);
 | |
| -		if (!success)
 | |
| -			return false;
 | |
| -
 | |
| -		/* Verify the data just written. ECC error indicates failure */
 | |
| -		ret = nmbm_read_phys_page(ni, off - leading, ni->page_cache,
 | |
| -					  NULL, NMBM_MODE_PLACE_OOB);
 | |
| -		if (ret < 0)
 | |
| -			return false;
 | |
| -
 | |
| -		if (memcmp(ni->page_cache + leading, ptr, chunksize))
 | |
| -			return false;
 | |
| -
 | |
| -		off += chunksize;
 | |
| -		ptr += chunksize;
 | |
| -		sizeremain -= chunksize;
 | |
| -	}
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_write_mgmt_range - Write management data into NAND within a range
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: preferred start block address for writing
 | |
| - * @limit: highest block address allowed for writing
 | |
| - * @data: the data to be written
 | |
| - * @size: the size of data
 | |
| - * @actual_start_ba: actual start block address of data
 | |
| - * @actual_end_ba: block address after the end of data
 | |
| - *
 | |
| - * @limit is not counted into the allowed write address.
 | |
| - */
 | |
| -static bool nmbm_write_mgmt_range(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				  uint32_t limit, const void *data,
 | |
| -				  uint32_t size, uint32_t *actual_start_ba,
 | |
| -				  uint32_t *actual_end_ba)
 | |
| -{
 | |
| -	const uint8_t *ptr = data;
 | |
| -	uint32_t sizeremain = size, chunksize;
 | |
| -	bool success;
 | |
| -
 | |
| -	while (sizeremain && ba < limit) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		chunksize = sizeremain;
 | |
| -		if (chunksize > ni->lower.erasesize)
 | |
| -			chunksize = ni->lower.erasesize;
 | |
| -
 | |
| -		if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
 | |
| -			goto next_block;
 | |
| -
 | |
| -		/* Insurance to detect unexpected bad block marked by user */
 | |
| -		if (nmbm_check_bad_phys_block(ni, ba)) {
 | |
| -			nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -			goto next_block;
 | |
| -		}
 | |
| -
 | |
| -		success = nmbm_erase_block_and_check(ni, ba);
 | |
| -		if (!success)
 | |
| -			goto skip_bad_block;
 | |
| -
 | |
| -		success = nmbn_write_verify_data(ni, ba2addr(ni, ba), ptr,
 | |
| -						 chunksize);
 | |
| -		if (!success)
 | |
| -			goto skip_bad_block;
 | |
| -
 | |
| -		if (sizeremain == size)
 | |
| -			*actual_start_ba = ba;
 | |
| -
 | |
| -		ptr += chunksize;
 | |
| -		sizeremain -= chunksize;
 | |
| -
 | |
| -		goto next_block;
 | |
| -
 | |
| -	skip_bad_block:
 | |
| -		nmbm_mark_phys_bad_block(ni, ba);
 | |
| -		nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -
 | |
| -	next_block:
 | |
| -		ba++;
 | |
| -	}
 | |
| -
 | |
| -	if (sizeremain)
 | |
| -		return false;
 | |
| -
 | |
| -	*actual_end_ba = ba;
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_generate_info_table_cache - Generate info table cache data
 | |
| - * @ni: NMBM instance structure
 | |
| - *
 | |
| - * Generate info table cache data to be written into flash.
 | |
| - */
 | |
| -static bool nmbm_generate_info_table_cache(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	bool changed = false;
 | |
| -
 | |
| -	memset(ni->info_table_cache, 0xff, ni->info_table_size);
 | |
| -
 | |
| -	memcpy(ni->info_table_cache + ni->info_table.state_table_off,
 | |
| -	       ni->block_state, ni->state_table_size);
 | |
| -
 | |
| -	memcpy(ni->info_table_cache + ni->info_table.mapping_table_off,
 | |
| -		ni->block_mapping, ni->mapping_table_size);
 | |
| -
 | |
| -	ni->info_table.header.magic = NMBM_MAGIC_INFO_TABLE;
 | |
| -	ni->info_table.header.version = NMBM_VER;
 | |
| -	ni->info_table.header.size = ni->info_table_size;
 | |
| -
 | |
| -	if (ni->block_state_changed || ni->block_mapping_changed) {
 | |
| -		ni->info_table.write_count++;
 | |
| -		changed = true;
 | |
| -	}
 | |
| -
 | |
| -	memcpy(ni->info_table_cache, &ni->info_table, sizeof(ni->info_table));
 | |
| -
 | |
| -	nmbm_update_checksum((struct nmbm_header *)ni->info_table_cache);
 | |
| -
 | |
| -	return changed;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_write_info_table - Write info table into NAND within a range
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: preferred start block address for writing
 | |
| - * @limit: highest block address allowed for writing
 | |
| - * @actual_start_ba: actual start block address of info table
 | |
| - * @actual_end_ba: block address after the end of info table
 | |
| - *
 | |
| - * @limit is counted into the allowed write address.
 | |
| - */
 | |
| -static bool nmbm_write_info_table(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				  uint32_t limit, uint32_t *actual_start_ba,
 | |
| -				  uint32_t *actual_end_ba)
 | |
| -{
 | |
| -	return nmbm_write_mgmt_range(ni, ba, limit, ni->info_table_cache,
 | |
| -				     ni->info_table_size, actual_start_ba,
 | |
| -				     actual_end_ba);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_mark_tables_clean - Mark info table `clean'
 | |
| - * @ni: NMBM instance structure
 | |
| - */
 | |
| -static void nmbm_mark_tables_clean(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	ni->block_state_changed = 0;
 | |
| -	ni->block_mapping_changed = 0;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_try_reserve_blocks - Reserve blocks with compromisation
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: start physical block address
 | |
| - * @nba: return physical block address after reservation
 | |
| - * @count: number of good blocks to be skipped
 | |
| - * @min_count: minimum number of good blocks to be skipped
 | |
| - * @limit: highest/lowest block address allowed for walking
 | |
| - *
 | |
| - * Reserve specific blocks. If failed, try to reserve as many as possible.
 | |
| - */
 | |
| -static bool nmbm_try_reserve_blocks(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				    uint32_t *nba, uint32_t count,
 | |
| -				    int32_t min_count, int32_t limit)
 | |
| -{
 | |
| -	int32_t nblocks = count;
 | |
| -	bool success;
 | |
| -
 | |
| -	while (nblocks >= min_count) {
 | |
| -		success = nmbm_block_walk(ni, true, ba, nba, nblocks, limit);
 | |
| -		if (success)
 | |
| -			return true;
 | |
| -
 | |
| -		nblocks--;
 | |
| -	}
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_rebuild_info_table - Build main & backup info table from scratch
 | |
| - * @ni: NMBM instance structure
 | |
| - * @allow_no_gap: allow no spare blocks between two tables
 | |
| - */
 | |
| -static bool nmbm_rebuild_info_table(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	uint32_t table_start_ba, table_end_ba, next_start_ba;
 | |
| -	uint32_t main_table_end_ba;
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Set initial value */
 | |
| -	ni->main_table_ba = 0;
 | |
| -	ni->backup_table_ba = 0;
 | |
| -	ni->mapping_blocks_ba = ni->mapping_blocks_top_ba;
 | |
| -
 | |
| -	/* Write main table */
 | |
| -	success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
 | |
| -					ni->mapping_blocks_top_ba,
 | |
| -					&table_start_ba, &table_end_ba);
 | |
| -	if (!success) {
 | |
| -		/* Failed to write main table, data will be lost */
 | |
| -		nlog_emerg(ni, "Unable to write at least one info table!\n");
 | |
| -		nlog_emerg(ni, "Please save your data before power off!\n");
 | |
| -		ni->protected = 1;
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	/* Main info table is successfully written, record its offset */
 | |
| -	ni->main_table_ba = table_start_ba;
 | |
| -	main_table_end_ba = table_end_ba;
 | |
| -
 | |
| -	/* Adjust mapping_blocks_ba */
 | |
| -	ni->mapping_blocks_ba = table_end_ba;
 | |
| -
 | |
| -	nmbm_mark_tables_clean(ni);
 | |
| -
 | |
| -	nlog_table_creation(ni, true, table_start_ba, table_end_ba);
 | |
| -
 | |
| -	/* Reserve spare blocks for main info table. */
 | |
| -	success = nmbm_try_reserve_blocks(ni, table_end_ba,
 | |
| -					  &next_start_ba,
 | |
| -					  ni->info_table_spare_blocks, 0,
 | |
| -					  ni->mapping_blocks_top_ba -
 | |
| -					  size2blk(ni, ni->info_table_size));
 | |
| -	if (!success) {
 | |
| -		/* There is no spare block. */
 | |
| -		nlog_debug(ni, "No room for backup info table\n");
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Write backup info table. */
 | |
| -	success = nmbm_write_info_table(ni, next_start_ba,
 | |
| -					ni->mapping_blocks_top_ba,
 | |
| -					&table_start_ba, &table_end_ba);
 | |
| -	if (!success) {
 | |
| -		/* There is no enough blocks for backup table. */
 | |
| -		nlog_debug(ni, "No room for backup info table\n");
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Backup table is successfully written, record its offset */
 | |
| -	ni->backup_table_ba = table_start_ba;
 | |
| -
 | |
| -	/* Adjust mapping_blocks_off */
 | |
| -	ni->mapping_blocks_ba = table_end_ba;
 | |
| -
 | |
| -	/* Erase spare blocks of main table to clean possible interference data */
 | |
| -	nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba);
 | |
| -
 | |
| -	nlog_table_creation(ni, false, table_start_ba, table_end_ba);
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_rescue_single_info_table - Rescue when there is only one info table
 | |
| - * @ni: NMBM instance structure
 | |
| - *
 | |
| - * This function is called when there is only one info table exists.
 | |
| - * This function may fail if we can't write new info table
 | |
| - */
 | |
| -static bool nmbm_rescue_single_info_table(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	uint32_t table_start_ba, table_end_ba, write_ba;
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Try to write new info table in front of existing table */
 | |
| -	success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
 | |
| -					ni->main_table_ba,
 | |
| -					&table_start_ba,
 | |
| -					&table_end_ba);
 | |
| -	if (success) {
 | |
| -		/*
 | |
| -		 * New table becomes the main table, existing table becomes
 | |
| -		 * the backup table.
 | |
| -		 */
 | |
| -		ni->backup_table_ba = ni->main_table_ba;
 | |
| -		ni->main_table_ba = table_start_ba;
 | |
| -
 | |
| -		nmbm_mark_tables_clean(ni);
 | |
| -
 | |
| -		/* Erase spare blocks of main table to clean possible interference data */
 | |
| -		nmbm_erase_range(ni, table_end_ba, ni->backup_table_ba);
 | |
| -
 | |
| -		nlog_table_creation(ni, true, table_start_ba, table_end_ba);
 | |
| -
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Try to reserve spare blocks for existing table */
 | |
| -	success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba,
 | |
| -					  ni->info_table_spare_blocks, 0,
 | |
| -					  ni->mapping_blocks_top_ba -
 | |
| -					  size2blk(ni, ni->info_table_size));
 | |
| -	if (!success) {
 | |
| -		nlog_warn(ni, "Failed to rescue single info table\n");
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	/* Try to write new info table next to the existing table */
 | |
| -	while (write_ba >= ni->mapping_blocks_ba) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		success = nmbm_write_info_table(ni, write_ba,
 | |
| -						ni->mapping_blocks_top_ba,
 | |
| -						&table_start_ba,
 | |
| -						&table_end_ba);
 | |
| -		if (success)
 | |
| -			break;
 | |
| -
 | |
| -		write_ba--;
 | |
| -	}
 | |
| -
 | |
| -	if (success) {
 | |
| -		/* Erase spare blocks of main table to clean possible interference data */
 | |
| -		nmbm_erase_range(ni, ni->mapping_blocks_ba, table_start_ba);
 | |
| -
 | |
| -		/* New table becomes the backup table */
 | |
| -		ni->backup_table_ba = table_start_ba;
 | |
| -		ni->mapping_blocks_ba = table_end_ba;
 | |
| -
 | |
| -		nmbm_mark_tables_clean(ni);
 | |
| -
 | |
| -		nlog_table_creation(ni, false, table_start_ba, table_end_ba);
 | |
| -
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	nlog_warn(ni, "Failed to rescue single info table\n");
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_update_single_info_table - Update specific one info table
 | |
| - * @ni: NMBM instance structure
 | |
| - */
 | |
| -static bool nmbm_update_single_info_table(struct nmbm_instance *ni,
 | |
| -					  bool update_main_table)
 | |
| -{
 | |
| -	uint32_t write_start_ba, write_limit, table_start_ba, table_end_ba;
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Determine the write range */
 | |
| -	if (update_main_table) {
 | |
| -		write_start_ba = ni->main_table_ba;
 | |
| -		write_limit = ni->backup_table_ba;
 | |
| -	} else {
 | |
| -		write_start_ba = ni->backup_table_ba;
 | |
| -		write_limit = ni->mapping_blocks_top_ba;
 | |
| -	}
 | |
| -
 | |
| -	nmbm_mark_block_color_mgmt(ni, write_start_ba, write_limit - 1);
 | |
| -
 | |
| -	success = nmbm_write_info_table(ni, write_start_ba, write_limit,
 | |
| -					&table_start_ba, &table_end_ba);
 | |
| -	if (success) {
 | |
| -		if (update_main_table) {
 | |
| -			ni->main_table_ba = table_start_ba;
 | |
| -		} else {
 | |
| -			ni->backup_table_ba = table_start_ba;
 | |
| -			ni->mapping_blocks_ba = table_end_ba;
 | |
| -		}
 | |
| -
 | |
| -		nmbm_mark_tables_clean(ni);
 | |
| -
 | |
| -		nlog_table_update(ni, update_main_table, table_start_ba,
 | |
| -				 table_end_ba);
 | |
| -
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	if (update_main_table) {
 | |
| -		/*
 | |
| -		 * If failed to update main table, make backup table the new
 | |
| -		 * main table, and call nmbm_rescue_single_info_table()
 | |
| -		 */
 | |
| -		nlog_warn(ni, "Unable to update %s info table\n",
 | |
| -			 update_main_table ? "Main" : "Backup");
 | |
| -
 | |
| -		ni->main_table_ba = ni->backup_table_ba;
 | |
| -		ni->backup_table_ba = 0;
 | |
| -		return nmbm_rescue_single_info_table(ni);
 | |
| -	}
 | |
| -
 | |
| -	/* Only one table left */
 | |
| -	ni->mapping_blocks_ba = ni->backup_table_ba;
 | |
| -	ni->backup_table_ba = 0;
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_rescue_main_info_table - Rescue when failed to write main info table
 | |
| - * @ni: NMBM instance structure
 | |
| - *
 | |
| - * This function is called when main info table failed to be written, and
 | |
| - *    backup info table exists.
 | |
| - */
 | |
| -static bool nmbm_rescue_main_info_table(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	uint32_t tmp_table_start_ba, tmp_table_end_ba, main_table_start_ba;
 | |
| -	uint32_t main_table_end_ba, write_ba;
 | |
| -	uint32_t info_table_erasesize = size2blk(ni, ni->info_table_size);
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Try to reserve spare blocks for existing backup info table */
 | |
| -	success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba,
 | |
| -					  ni->info_table_spare_blocks, 0,
 | |
| -					  ni->mapping_blocks_top_ba -
 | |
| -					  info_table_erasesize);
 | |
| -	if (!success) {
 | |
| -		/* There is no spare block. Backup info table becomes the main table. */
 | |
| -		nlog_err(ni, "No room for temporary info table\n");
 | |
| -		ni->main_table_ba = ni->backup_table_ba;
 | |
| -		ni->backup_table_ba = 0;
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Try to write temporary info table into spare unmapped blocks */
 | |
| -	while (write_ba >= ni->mapping_blocks_ba) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		success = nmbm_write_info_table(ni, write_ba,
 | |
| -						ni->mapping_blocks_top_ba,
 | |
| -						&tmp_table_start_ba,
 | |
| -						&tmp_table_end_ba);
 | |
| -		if (success)
 | |
| -			break;
 | |
| -
 | |
| -		write_ba--;
 | |
| -	}
 | |
| -
 | |
| -	if (!success) {
 | |
| -		/* Backup info table becomes the main table */
 | |
| -		nlog_err(ni, "Failed to update main info table\n");
 | |
| -		ni->main_table_ba = ni->backup_table_ba;
 | |
| -		ni->backup_table_ba = 0;
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Adjust mapping_blocks_off */
 | |
| -	ni->mapping_blocks_ba = tmp_table_end_ba;
 | |
| -
 | |
| -	nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba,
 | |
| -				   tmp_table_end_ba - 1);
 | |
| -
 | |
| -	/*
 | |
| -	 * Now write main info table at the beginning of management area.
 | |
| -	 * This operation will generally destroy the original backup info
 | |
| -	 * table.
 | |
| -	 */
 | |
| -	success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
 | |
| -					tmp_table_start_ba,
 | |
| -					&main_table_start_ba,
 | |
| -					&main_table_end_ba);
 | |
| -	if (!success) {
 | |
| -		/* Temporary info table becomes the main table */
 | |
| -		ni->main_table_ba = tmp_table_start_ba;
 | |
| -		ni->backup_table_ba = 0;
 | |
| -
 | |
| -		nmbm_mark_tables_clean(ni);
 | |
| -
 | |
| -		nlog_err(ni, "Failed to update main info table\n");
 | |
| -		nmbm_mark_block_color_info_table(ni, tmp_table_start_ba,
 | |
| -						 tmp_table_end_ba - 1);
 | |
| -
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Main info table has been successfully written, record its offset */
 | |
| -	ni->main_table_ba = main_table_start_ba;
 | |
| -
 | |
| -	nmbm_mark_tables_clean(ni);
 | |
| -
 | |
| -	nlog_table_creation(ni, true, main_table_start_ba, main_table_end_ba);
 | |
| -
 | |
| -	/*
 | |
| -	 * Temporary info table becomes the new backup info table if it's
 | |
| -	 * not overwritten.
 | |
| -	 */
 | |
| -	if (main_table_end_ba <= tmp_table_start_ba) {
 | |
| -		ni->backup_table_ba = tmp_table_start_ba;
 | |
| -
 | |
| -		nlog_table_creation(ni, false, tmp_table_start_ba,
 | |
| -				   tmp_table_end_ba);
 | |
| -
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Adjust mapping_blocks_off */
 | |
| -	ni->mapping_blocks_ba = main_table_end_ba;
 | |
| -
 | |
| -	/* Try to reserve spare blocks for new main info table */
 | |
| -	success = nmbm_try_reserve_blocks(ni, main_table_end_ba, &write_ba,
 | |
| -					  ni->info_table_spare_blocks, 0,
 | |
| -					  ni->mapping_blocks_top_ba -
 | |
| -					  info_table_erasesize);
 | |
| -	if (!success) {
 | |
| -		/* There is no spare block. Only main table exists. */
 | |
| -		nlog_err(ni, "No room for backup info table\n");
 | |
| -		ni->backup_table_ba = 0;
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Write new backup info table. */
 | |
| -	while (write_ba >= main_table_end_ba) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		success = nmbm_write_info_table(ni, write_ba,
 | |
| -						ni->mapping_blocks_top_ba,
 | |
| -						&tmp_table_start_ba,
 | |
| -						&tmp_table_end_ba);
 | |
| -		if (success)
 | |
| -			break;
 | |
| -
 | |
| -		write_ba--;
 | |
| -	}
 | |
| -
 | |
| -	if (!success) {
 | |
| -		nlog_err(ni, "No room for backup info table\n");
 | |
| -		ni->backup_table_ba = 0;
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Backup info table has been successfully written, record its offset */
 | |
| -	ni->backup_table_ba = tmp_table_start_ba;
 | |
| -
 | |
| -	/* Adjust mapping_blocks_off */
 | |
| -	ni->mapping_blocks_ba = tmp_table_end_ba;
 | |
| -
 | |
| -	/* Erase spare blocks of main table to clean possible interference data */
 | |
| -	nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba);
 | |
| -
 | |
| -	nlog_table_creation(ni, false, tmp_table_start_ba, tmp_table_end_ba);
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_update_info_table_once - Update info table once
 | |
| - * @ni: NMBM instance structure
 | |
| - * @force: force update
 | |
| - *
 | |
| - * Update both main and backup info table. Return true if at least one info
 | |
| - * table has been successfully written.
 | |
| - * This function only try to update info table once regard less of the result.
 | |
| - */
 | |
| -static bool nmbm_update_info_table_once(struct nmbm_instance *ni, bool force)
 | |
| -{
 | |
| -	uint32_t table_start_ba, table_end_ba;
 | |
| -	uint32_t main_table_limit;
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Do nothing if there is no change */
 | |
| -	if (!nmbm_generate_info_table_cache(ni) && !force)
 | |
| -		return true;
 | |
| -
 | |
| -	/* Check whether both two tables exist */
 | |
| -	if (!ni->backup_table_ba) {
 | |
| -		main_table_limit = ni->mapping_blocks_top_ba;
 | |
| -		goto write_main_table;
 | |
| -	}
 | |
| -
 | |
| -	nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba,
 | |
| -				   ni->mapping_blocks_ba - 1);
 | |
| -
 | |
| -	/*
 | |
| -	 * Write backup info table in its current range.
 | |
| -	 * Note that limit is set to mapping_blocks_top_off to provide as many
 | |
| -	 * spare blocks as possible for the backup table. If at last
 | |
| -	 * unmapped blocks are used by backup table, mapping_blocks_off will
 | |
| -	 * be adjusted.
 | |
| -	 */
 | |
| -	success = nmbm_write_info_table(ni, ni->backup_table_ba,
 | |
| -					ni->mapping_blocks_top_ba,
 | |
| -					&table_start_ba, &table_end_ba);
 | |
| -	if (!success) {
 | |
| -		/*
 | |
| -		 * There is nothing to do if failed to write backup table.
 | |
| -		 * Write the main table now.
 | |
| -		 */
 | |
| -		nlog_err(ni, "No room for backup table\n");
 | |
| -		ni->mapping_blocks_ba = ni->backup_table_ba;
 | |
| -		ni->backup_table_ba = 0;
 | |
| -		main_table_limit = ni->mapping_blocks_top_ba;
 | |
| -		goto write_main_table;
 | |
| -	}
 | |
| -
 | |
| -	/* Backup table is successfully written, record its offset */
 | |
| -	ni->backup_table_ba = table_start_ba;
 | |
| -
 | |
| -	/* Adjust mapping_blocks_off */
 | |
| -	ni->mapping_blocks_ba = table_end_ba;
 | |
| -
 | |
| -	nmbm_mark_tables_clean(ni);
 | |
| -
 | |
| -	/* The normal limit of main table */
 | |
| -	main_table_limit = ni->backup_table_ba;
 | |
| -
 | |
| -	nlog_table_update(ni, false, table_start_ba, table_end_ba);
 | |
| -
 | |
| -write_main_table:
 | |
| -	if (!ni->main_table_ba)
 | |
| -		goto rebuild_tables;
 | |
| -
 | |
| -	if (!ni->backup_table_ba)
 | |
| -		nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
 | |
| -					   ni->mapping_blocks_ba - 1);
 | |
| -	else
 | |
| -		nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
 | |
| -					   ni->backup_table_ba - 1);
 | |
| -
 | |
| -	/* Write main info table in its current range */
 | |
| -	success = nmbm_write_info_table(ni, ni->main_table_ba,
 | |
| -					main_table_limit, &table_start_ba,
 | |
| -					&table_end_ba);
 | |
| -	if (!success) {
 | |
| -		/* If failed to write main table, go rescue procedure */
 | |
| -		if (!ni->backup_table_ba)
 | |
| -			goto rebuild_tables;
 | |
| -
 | |
| -		return nmbm_rescue_main_info_table(ni);
 | |
| -	}
 | |
| -
 | |
| -	/* Main info table is successfully written, record its offset */
 | |
| -	ni->main_table_ba = table_start_ba;
 | |
| -
 | |
| -	/* Adjust mapping_blocks_off */
 | |
| -	if (!ni->backup_table_ba)
 | |
| -		ni->mapping_blocks_ba = table_end_ba;
 | |
| -
 | |
| -	nmbm_mark_tables_clean(ni);
 | |
| -
 | |
| -	nlog_table_update(ni, true, table_start_ba, table_end_ba);
 | |
| -
 | |
| -	return true;
 | |
| -
 | |
| -rebuild_tables:
 | |
| -	return nmbm_rebuild_info_table(ni);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_update_info_table - Update info table
 | |
| - * @ni: NMBM instance structure
 | |
| - *
 | |
| - * Update both main and backup info table. Return true if at least one table
 | |
| - * has been successfully written.
 | |
| - * This function will try to update info table repeatedly until no new bad
 | |
| - * block found during updating.
 | |
| - */
 | |
| -static bool nmbm_update_info_table(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	bool success;
 | |
| -
 | |
| -	if (ni->protected)
 | |
| -		return true;
 | |
| -
 | |
| -	while (ni->block_state_changed || ni->block_mapping_changed) {
 | |
| -		success = nmbm_update_info_table_once(ni, false);
 | |
| -		if (!success) {
 | |
| -			nlog_err(ni, "Failed to update info table\n");
 | |
| -			return false;
 | |
| -		}
 | |
| -	}
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_map_block - Map a bad block to a unused spare block
 | |
| - * @ni: NMBM instance structure
 | |
| - * @lb: logic block addr to map
 | |
| - */
 | |
| -static bool nmbm_map_block(struct nmbm_instance *ni, uint32_t lb)
 | |
| -{
 | |
| -	uint32_t pb;
 | |
| -	bool success;
 | |
| -
 | |
| -	if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) {
 | |
| -		nlog_warn(ni, "No spare unmapped blocks.\n");
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	success = nmbm_block_walk(ni, false, ni->mapping_blocks_top_ba, &pb, 0,
 | |
| -				  ni->mapping_blocks_ba);
 | |
| -	if (!success) {
 | |
| -		nlog_warn(ni, "No spare unmapped blocks.\n");
 | |
| -		nmbm_update_info_table(ni);
 | |
| -		ni->mapping_blocks_top_ba = ni->mapping_blocks_ba;
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	ni->block_mapping[lb] = pb;
 | |
| -	ni->mapping_blocks_top_ba--;
 | |
| -	ni->block_mapping_changed++;
 | |
| -
 | |
| -	nlog_info(ni, "Logic block %u mapped to physical blcok %u\n", lb, pb);
 | |
| -	nmbm_mark_block_color_mapped(ni, pb);
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_create_info_table - Create info table(s)
 | |
| - * @ni: NMBM instance structure
 | |
| - *
 | |
| - * This function assumes that the chip has no existing info table(s)
 | |
| - */
 | |
| -static bool nmbm_create_info_table(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	uint32_t lb;
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Set initial mapping_blocks_top_off  */
 | |
| -	success = nmbm_block_walk(ni, false, ni->signature_ba,
 | |
| -				  &ni->mapping_blocks_top_ba, 1,
 | |
| -				  ni->mgmt_start_ba);
 | |
| -	if (!success) {
 | |
| -		nlog_err(ni, "No room for spare blocks\n");
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	/* Generate info table cache */
 | |
| -	nmbm_generate_info_table_cache(ni);
 | |
| -
 | |
| -	/* Write info table */
 | |
| -	success = nmbm_rebuild_info_table(ni);
 | |
| -	if (!success) {
 | |
| -		nlog_err(ni, "Failed to build info tables\n");
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	/* Remap bad block(s) at end of data area */
 | |
| -	for (lb = ni->data_block_count; lb < ni->mgmt_start_ba; lb++) {
 | |
| -		success = nmbm_map_block(ni, lb);
 | |
| -		if (!success)
 | |
| -			break;
 | |
| -
 | |
| -		ni->data_block_count++;
 | |
| -	}
 | |
| -
 | |
| -	/* If state table and/or mapping table changed, update info table. */
 | |
| -	success = nmbm_update_info_table(ni);
 | |
| -	if (!success)
 | |
| -		return false;
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_create_new - Create NMBM on a new chip
 | |
| - * @ni: NMBM instance structure
 | |
| - */
 | |
| -static bool nmbm_create_new(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Determine the boundary of management blocks */
 | |
| -	ni->mgmt_start_ba = ni->block_count * (NMBM_MGMT_DIV - ni->lower.max_ratio) / NMBM_MGMT_DIV;
 | |
| -
 | |
| -	if (ni->lower.max_reserved_blocks && ni->block_count - ni->mgmt_start_ba > ni->lower.max_reserved_blocks)
 | |
| -		ni->mgmt_start_ba = ni->block_count - ni->lower.max_reserved_blocks;
 | |
| -
 | |
| -	nlog_info(ni, "NMBM management region starts at block %u [0x%08llx]\n",
 | |
| -		  ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba));
 | |
| -	nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ni->block_count - 1);
 | |
| -
 | |
| -	/* Fill block state table & mapping table */
 | |
| -	nmbm_scan_badblocks(ni);
 | |
| -	nmbm_build_mapping_table(ni);
 | |
| -
 | |
| -	/* Write signature */
 | |
| -	ni->signature.header.magic = NMBM_MAGIC_SIGNATURE;
 | |
| -	ni->signature.header.version = NMBM_VER;
 | |
| -	ni->signature.header.size = sizeof(ni->signature);
 | |
| -	ni->signature.nand_size = ni->lower.size;
 | |
| -	ni->signature.block_size = ni->lower.erasesize;
 | |
| -	ni->signature.page_size = ni->lower.writesize;
 | |
| -	ni->signature.spare_size = ni->lower.oobsize;
 | |
| -	ni->signature.mgmt_start_pb = ni->mgmt_start_ba;
 | |
| -	ni->signature.max_try_count = NMBM_TRY_COUNT;
 | |
| -	nmbm_update_checksum(&ni->signature.header);
 | |
| -
 | |
| -	if (ni->lower.flags & NMBM_F_READ_ONLY) {
 | |
| -		nlog_info(ni, "NMBM has been initialized in read-only mode\n");
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	success = nmbm_write_signature(ni, ni->mgmt_start_ba,
 | |
| -				       &ni->signature, &ni->signature_ba);
 | |
| -	if (!success) {
 | |
| -		nlog_err(ni, "Failed to write signature to a proper offset\n");
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	nlog_info(ni, "Signature has been written to block %u [0x%08llx]\n",
 | |
| -		 ni->signature_ba, ba2addr(ni, ni->signature_ba));
 | |
| -	nmbm_mark_block_color_signature(ni, ni->signature_ba);
 | |
| -
 | |
| -	/* Write info table(s) */
 | |
| -	success = nmbm_create_info_table(ni);
 | |
| -	if (success) {
 | |
| -		nlog_info(ni, "NMBM has been successfully created\n");
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_check_info_table_header - Check if a info table header is valid
 | |
| - * @ni: NMBM instance structure
 | |
| - * @data: pointer to the info table header
 | |
| - */
 | |
| -static bool nmbm_check_info_table_header(struct nmbm_instance *ni, void *data)
 | |
| -{
 | |
| -	struct nmbm_info_table_header *ifthdr = data;
 | |
| -
 | |
| -	if (ifthdr->header.magic != NMBM_MAGIC_INFO_TABLE)
 | |
| -		return false;
 | |
| -
 | |
| -	if (ifthdr->header.size != ni->info_table_size)
 | |
| -		return false;
 | |
| -
 | |
| -	if (ifthdr->mapping_table_off - ifthdr->state_table_off < ni->state_table_size)
 | |
| -		return false;
 | |
| -
 | |
| -	if (ni->info_table_size - ifthdr->mapping_table_off < ni->mapping_table_size)
 | |
| -		return false;
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_check_info_table - Check if a whole info table is valid
 | |
| - * @ni: NMBM instance structure
 | |
| - * @start_ba: start block address of this table
 | |
| - * @end_ba: end block address of this table
 | |
| - * @data: pointer to the info table header
 | |
| - * @mapping_blocks_top_ba: return the block address of top remapped block
 | |
| - */
 | |
| -static bool nmbm_check_info_table(struct nmbm_instance *ni, uint32_t start_ba,
 | |
| -				  uint32_t end_ba, void *data,
 | |
| -				  uint32_t *mapping_blocks_top_ba)
 | |
| -{
 | |
| -	struct nmbm_info_table_header *ifthdr = data;
 | |
| -	int32_t *block_mapping = (int32_t *)((uintptr_t)data + ifthdr->mapping_table_off);
 | |
| -	nmbm_bitmap_t *block_state = (nmbm_bitmap_t *)((uintptr_t)data + ifthdr->state_table_off);
 | |
| -	uint32_t minimum_mapping_pb = ni->signature_ba;
 | |
| -	uint32_t ba;
 | |
| -
 | |
| -	for (ba = 0; ba < ni->data_block_count; ba++) {
 | |
| -		if ((block_mapping[ba] >= ni->data_block_count && block_mapping[ba] < end_ba) ||
 | |
| -		    block_mapping[ba] == ni->signature_ba)
 | |
| -			return false;
 | |
| -
 | |
| -		if (block_mapping[ba] >= end_ba && block_mapping[ba] < minimum_mapping_pb)
 | |
| -			minimum_mapping_pb = block_mapping[ba];
 | |
| -	}
 | |
| -
 | |
| -	for (ba = start_ba; ba < end_ba; ba++) {
 | |
| -		if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
 | |
| -			continue;
 | |
| -
 | |
| -		if (nmbm_get_block_state_raw(block_state, ba) != BLOCK_ST_GOOD)
 | |
| -			return false;
 | |
| -	}
 | |
| -
 | |
| -	*mapping_blocks_top_ba = minimum_mapping_pb - 1;
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_try_load_info_table - Try to load info table from a address
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: start block address of the info table
 | |
| - * @eba: return the block address after end of the table
 | |
| - * @write_count: return the write count of this table
 | |
| - * @mapping_blocks_top_ba: return the block address of top remapped block
 | |
| - * @table_loaded: used to record whether ni->info_table has valid data
 | |
| - */
 | |
| -static bool nmbm_try_load_info_table(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				     uint32_t *eba, uint32_t *write_count,
 | |
| -				     uint32_t *mapping_blocks_top_ba,
 | |
| -				     bool table_loaded)
 | |
| -{
 | |
| -	struct nmbm_info_table_header *ifthdr = (void *)ni->info_table_cache;
 | |
| -	uint8_t *off = ni->info_table_cache;
 | |
| -	uint32_t limit = ba + size2blk(ni, ni->info_table_size);
 | |
| -	uint32_t start_ba = 0, chunksize, sizeremain = ni->info_table_size;
 | |
| -	bool success, checkhdr = true;
 | |
| -	int ret;
 | |
| -
 | |
| -	while (sizeremain && ba < limit) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
 | |
| -			goto next_block;
 | |
| -
 | |
| -		if (nmbm_check_bad_phys_block(ni, ba)) {
 | |
| -			nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -			goto next_block;
 | |
| -		}
 | |
| -
 | |
| -		chunksize = sizeremain;
 | |
| -		if (chunksize > ni->lower.erasesize)
 | |
| -			chunksize = ni->lower.erasesize;
 | |
| -
 | |
| -		/* Assume block with ECC error has no info table data */
 | |
| -		ret = nmbn_read_data(ni, ba2addr(ni, ba), off, chunksize);
 | |
| -		if (ret < 0)
 | |
| -			goto skip_bad_block;
 | |
| -		else if (ret > 0)
 | |
| -			return false;
 | |
| -
 | |
| -		if (checkhdr) {
 | |
| -			success = nmbm_check_info_table_header(ni, off);
 | |
| -			if (!success)
 | |
| -				return false;
 | |
| -
 | |
| -			start_ba = ba;
 | |
| -			checkhdr = false;
 | |
| -		}
 | |
| -
 | |
| -		off += chunksize;
 | |
| -		sizeremain -= chunksize;
 | |
| -
 | |
| -		goto next_block;
 | |
| -
 | |
| -	skip_bad_block:
 | |
| -		/* Only mark bad in memory */
 | |
| -		nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
 | |
| -
 | |
| -	next_block:
 | |
| -		ba++;
 | |
| -	}
 | |
| -
 | |
| -	if (sizeremain)
 | |
| -		return false;
 | |
| -
 | |
| -	success = nmbm_check_header(ni->info_table_cache, ni->info_table_size);
 | |
| -	if (!success)
 | |
| -		return false;
 | |
| -
 | |
| -	*eba = ba;
 | |
| -	*write_count = ifthdr->write_count;
 | |
| -
 | |
| -	success = nmbm_check_info_table(ni, start_ba, ba, ni->info_table_cache,
 | |
| -					mapping_blocks_top_ba);
 | |
| -	if (!success)
 | |
| -		return false;
 | |
| -
 | |
| -	if (!table_loaded || ifthdr->write_count > ni->info_table.write_count) {
 | |
| -		memcpy(&ni->info_table, ifthdr, sizeof(ni->info_table));
 | |
| -		memcpy(ni->block_state,
 | |
| -		       (uint8_t *)ifthdr + ifthdr->state_table_off,
 | |
| -		       ni->state_table_size);
 | |
| -		memcpy(ni->block_mapping,
 | |
| -		       (uint8_t *)ifthdr + ifthdr->mapping_table_off,
 | |
| -		       ni->mapping_table_size);
 | |
| -		ni->info_table.write_count = ifthdr->write_count;
 | |
| -	}
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_search_info_table - Search info table from specific address
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: start block address to search
 | |
| - * @limit: highest block address allowed for searching
 | |
| - * @table_start_ba: return the start block address of this table
 | |
| - * @table_end_ba: return the block address after end of this table
 | |
| - * @write_count: return the write count of this table
 | |
| - * @mapping_blocks_top_ba: return the block address of top remapped block
 | |
| - * @table_loaded: used to record whether ni->info_table has valid data
 | |
| - */
 | |
| -static bool nmbm_search_info_table(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				   uint32_t limit, uint32_t *table_start_ba,
 | |
| -				   uint32_t *table_end_ba,
 | |
| -				   uint32_t *write_count,
 | |
| -				   uint32_t *mapping_blocks_top_ba,
 | |
| -				   bool table_loaded)
 | |
| -{
 | |
| -	bool success;
 | |
| -
 | |
| -	while (ba < limit - size2blk(ni, ni->info_table_size)) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		success = nmbm_try_load_info_table(ni, ba, table_end_ba,
 | |
| -						   write_count,
 | |
| -						   mapping_blocks_top_ba,
 | |
| -						   table_loaded);
 | |
| -		if (success) {
 | |
| -			*table_start_ba = ba;
 | |
| -			return true;
 | |
| -		}
 | |
| -
 | |
| -		ba++;
 | |
| -	}
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_load_info_table - Load info table(s) from a chip
 | |
| - * @ni: NMBM instance structure
 | |
| - * @ba: start block address to search info table
 | |
| - * @limit: highest block address allowed for searching
 | |
| - */
 | |
| -static bool nmbm_load_info_table(struct nmbm_instance *ni, uint32_t ba,
 | |
| -				 uint32_t limit)
 | |
| -{
 | |
| -	uint32_t main_table_end_ba, backup_table_end_ba, table_end_ba;
 | |
| -	uint32_t main_mapping_blocks_top_ba, backup_mapping_blocks_top_ba;
 | |
| -	uint32_t main_table_write_count, backup_table_write_count;
 | |
| -	uint32_t i;
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Set initial value */
 | |
| -	ni->main_table_ba = 0;
 | |
| -	ni->backup_table_ba = 0;
 | |
| -	ni->info_table.write_count = 0;
 | |
| -	ni->mapping_blocks_top_ba = ni->signature_ba - 1;
 | |
| -	ni->data_block_count = ni->signature.mgmt_start_pb;
 | |
| -
 | |
| -	/* Find first info table */
 | |
| -	success = nmbm_search_info_table(ni, ba, limit, &ni->main_table_ba,
 | |
| -		&main_table_end_ba, &main_table_write_count,
 | |
| -		&main_mapping_blocks_top_ba, false);
 | |
| -	if (!success) {
 | |
| -		nlog_warn(ni, "No valid info table found\n");
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	table_end_ba = main_table_end_ba;
 | |
| -
 | |
| -	nlog_table_found(ni, true, main_table_write_count, ni->main_table_ba,
 | |
| -			main_table_end_ba);
 | |
| -
 | |
| -	/* Find second info table */
 | |
| -	success = nmbm_search_info_table(ni, main_table_end_ba, limit,
 | |
| -		&ni->backup_table_ba, &backup_table_end_ba,
 | |
| -		&backup_table_write_count, &backup_mapping_blocks_top_ba, true);
 | |
| -	if (!success) {
 | |
| -		nlog_warn(ni, "Second info table not found\n");
 | |
| -	} else {
 | |
| -		table_end_ba = backup_table_end_ba;
 | |
| -
 | |
| -		nlog_table_found(ni, false, backup_table_write_count,
 | |
| -				ni->backup_table_ba, backup_table_end_ba);
 | |
| -	}
 | |
| -
 | |
| -	/* Pick mapping_blocks_top_ba */
 | |
| -	if (!ni->backup_table_ba) {
 | |
| -		ni->mapping_blocks_top_ba= main_mapping_blocks_top_ba;
 | |
| -	} else {
 | |
| -		if (main_table_write_count >= backup_table_write_count)
 | |
| -			ni->mapping_blocks_top_ba = main_mapping_blocks_top_ba;
 | |
| -		else
 | |
| -			ni->mapping_blocks_top_ba = backup_mapping_blocks_top_ba;
 | |
| -	}
 | |
| -
 | |
| -	/* Set final mapping_blocks_ba */
 | |
| -	ni->mapping_blocks_ba = table_end_ba;
 | |
| -
 | |
| -	/* Set final data_block_count */
 | |
| -	for (i = ni->signature.mgmt_start_pb; i > 0; i--) {
 | |
| -		if (ni->block_mapping[i - 1] >= 0) {
 | |
| -			ni->data_block_count = i;
 | |
| -			break;
 | |
| -		}
 | |
| -	}
 | |
| -
 | |
| -	/* Debug purpose: mark mapped blocks and bad blocks */
 | |
| -	for (i = 0; i < ni->data_block_count; i++) {
 | |
| -		if (ni->block_mapping[i] > ni->mapping_blocks_top_ba)
 | |
| -			nmbm_mark_block_color_mapped(ni, ni->block_mapping[i]);
 | |
| -	}
 | |
| -
 | |
| -	for (i = 0; i < ni->block_count; i++) {
 | |
| -		if (nmbm_get_block_state(ni, i) == BLOCK_ST_BAD)
 | |
| -			nmbm_mark_block_color_bad(ni, i);
 | |
| -	}
 | |
| -
 | |
| -	/* Regenerate the info table cache from the final selected info table */
 | |
| -	nmbm_generate_info_table_cache(ni);
 | |
| -
 | |
| -	if (ni->lower.flags & NMBM_F_READ_ONLY)
 | |
| -		return true;
 | |
| -
 | |
| -	/*
 | |
| -	 * If only one table exists, try to write another table.
 | |
| -	 * If two tables have different write count, try to update info table
 | |
| -	 */
 | |
| -	if (!ni->backup_table_ba) {
 | |
| -		success = nmbm_rescue_single_info_table(ni);
 | |
| -	} else if (main_table_write_count != backup_table_write_count) {
 | |
| -		/* Mark state & mapping tables changed */
 | |
| -		ni->block_state_changed = 1;
 | |
| -		ni->block_mapping_changed = 1;
 | |
| -
 | |
| -		success = nmbm_update_single_info_table(ni,
 | |
| -			main_table_write_count < backup_table_write_count);
 | |
| -	} else {
 | |
| -		success = true;
 | |
| -	}
 | |
| -
 | |
| -	/*
 | |
| -	 * If there is no spare unmapped blocks, or still only one table
 | |
| -	 * exists, set the chip to read-only
 | |
| -	 */
 | |
| -	if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) {
 | |
| -		nlog_warn(ni, "No spare unmapped blocks. Device is now read-only\n");
 | |
| -		ni->protected = 1;
 | |
| -	} else if (!success) {
 | |
| -		nlog_warn(ni, "Only one info table found. Device is now read-only\n");
 | |
| -		ni->protected = 1;
 | |
| -	}
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_load_existing - Load NMBM from a new chip
 | |
| - * @ni: NMBM instance structure
 | |
| - */
 | |
| -static bool nmbm_load_existing(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Calculate the boundary of management blocks */
 | |
| -	ni->mgmt_start_ba = ni->signature.mgmt_start_pb;
 | |
| -
 | |
| -	nlog_debug(ni, "NMBM management region starts at block %u [0x%08llx]\n",
 | |
| -		  ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba));
 | |
| -	nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
 | |
| -				   ni->signature_ba - 1);
 | |
| -
 | |
| -	/* Look for info table(s) */
 | |
| -	success = nmbm_load_info_table(ni, ni->mgmt_start_ba,
 | |
| -		ni->signature_ba);
 | |
| -	if (success) {
 | |
| -		nlog_info(ni, "NMBM has been successfully attached %s\n",
 | |
| -			  (ni->lower.flags & NMBM_F_READ_ONLY) ? "in read-only mode" : "");
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	if (!(ni->lower.flags & NMBM_F_CREATE))
 | |
| -		return false;
 | |
| -
 | |
| -	/* Fill block state table & mapping table */
 | |
| -	nmbm_scan_badblocks(ni);
 | |
| -	nmbm_build_mapping_table(ni);
 | |
| -
 | |
| -	if (ni->lower.flags & NMBM_F_READ_ONLY) {
 | |
| -		nlog_info(ni, "NMBM has been initialized in read-only mode\n");
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	/* Write info table(s) */
 | |
| -	success = nmbm_create_info_table(ni);
 | |
| -	if (success) {
 | |
| -		nlog_info(ni, "NMBM has been successfully created\n");
 | |
| -		return true;
 | |
| -	}
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_find_signature - Find signature in the lower NAND chip
 | |
| - * @ni: NMBM instance structure
 | |
| - * @signature_ba: used for storing block address of the signature
 | |
| - * @signature_ba: return the actual block address of signature block
 | |
| - *
 | |
| - * Find a valid signature from a specific range in the lower NAND chip,
 | |
| - * from bottom (highest address) to top (lowest address)
 | |
| - *
 | |
| - * Return true if found.
 | |
| - */
 | |
| -static bool nmbm_find_signature(struct nmbm_instance *ni,
 | |
| -				struct nmbm_signature *signature,
 | |
| -				uint32_t *signature_ba)
 | |
| -{
 | |
| -	struct nmbm_signature sig;
 | |
| -	uint64_t off, addr;
 | |
| -	uint32_t block_count, ba, limit;
 | |
| -	bool success;
 | |
| -	int ret;
 | |
| -
 | |
| -	/* Calculate top and bottom block address */
 | |
| -	block_count = ni->lower.size >> ni->erasesize_shift;
 | |
| -	ba = block_count;
 | |
| -	limit = (block_count / NMBM_MGMT_DIV) * (NMBM_MGMT_DIV - ni->lower.max_ratio);
 | |
| -	if (ni->lower.max_reserved_blocks && block_count - limit > ni->lower.max_reserved_blocks)
 | |
| -		limit = block_count - ni->lower.max_reserved_blocks;
 | |
| -
 | |
| -	while (ba >= limit) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		ba--;
 | |
| -		addr = ba2addr(ni, ba);
 | |
| -
 | |
| -		if (nmbm_check_bad_phys_block(ni, ba))
 | |
| -			continue;
 | |
| -
 | |
| -		/* Check every page.
 | |
| -		 * As long as at leaset one page contains valid signature,
 | |
| -		 * the block is treated as a valid signature block.
 | |
| -		 */
 | |
| -		for (off = 0; off < ni->lower.erasesize;
 | |
| -		     off += ni->lower.writesize) {
 | |
| -			WATCHDOG_RESET();
 | |
| -
 | |
| -			ret = nmbn_read_data(ni, addr + off, &sig,
 | |
| -					     sizeof(sig));
 | |
| -			if (ret)
 | |
| -				continue;
 | |
| -
 | |
| -			/* Check for header size and checksum */
 | |
| -			success = nmbm_check_header(&sig, sizeof(sig));
 | |
| -			if (!success)
 | |
| -				continue;
 | |
| -
 | |
| -			/* Check for header magic */
 | |
| -			if (sig.header.magic == NMBM_MAGIC_SIGNATURE) {
 | |
| -				/* Found it */
 | |
| -				memcpy(signature, &sig, sizeof(sig));
 | |
| -				*signature_ba = ba;
 | |
| -				return true;
 | |
| -			}
 | |
| -		}
 | |
| -	};
 | |
| -
 | |
| -	return false;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * is_power_of_2_u64 - Check whether a 64-bit integer is power of 2
 | |
| - * @n: number to check
 | |
| - */
 | |
| -static bool is_power_of_2_u64(uint64_t n)
 | |
| -{
 | |
| -	return (n != 0 && ((n & (n - 1)) == 0));
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_check_lower_members - Validate the members of lower NAND device
 | |
| - * @nld: Lower NAND chip structure
 | |
| - */
 | |
| -static bool nmbm_check_lower_members(struct nmbm_lower_device *nld)
 | |
| -{
 | |
| -
 | |
| -	if (!nld->size || !is_power_of_2_u64(nld->size)) {
 | |
| -		nmbm_log_lower(nld, NMBM_LOG_ERR,
 | |
| -			       "Chip size %llu is not valid\n", nld->size);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	if (!nld->erasesize || !is_power_of_2(nld->erasesize)) {
 | |
| -		nmbm_log_lower(nld, NMBM_LOG_ERR,
 | |
| -			       "Block size %u is not valid\n", nld->erasesize);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	if (!nld->writesize || !is_power_of_2(nld->writesize)) {
 | |
| -		nmbm_log_lower(nld, NMBM_LOG_ERR,
 | |
| -			       "Page size %u is not valid\n", nld->writesize);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	if (!nld->oobsize || !is_power_of_2(nld->oobsize)) {
 | |
| -		nmbm_log_lower(nld, NMBM_LOG_ERR,
 | |
| -			       "Page spare size %u is not valid\n", nld->oobsize);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	if (!nld->read_page) {
 | |
| -		nmbm_log_lower(nld, NMBM_LOG_ERR, "read_page() is required\n");
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	if (!(nld->flags & NMBM_F_READ_ONLY) && (!nld->write_page || !nld->erase_block)) {
 | |
| -		nmbm_log_lower(nld, NMBM_LOG_ERR,
 | |
| -			       "write_page() and erase_block() are required\n");
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	/* Data sanity check */
 | |
| -	if (!nld->max_ratio)
 | |
| -		nld->max_ratio = 1;
 | |
| -
 | |
| -	if (nld->max_ratio >= NMBM_MGMT_DIV - 1) {
 | |
| -		nmbm_log_lower(nld, NMBM_LOG_ERR,
 | |
| -			       "max ratio %u is invalid\n", nld->max_ratio);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	if (nld->max_reserved_blocks && nld->max_reserved_blocks < NMBM_MGMT_BLOCKS_MIN) {
 | |
| -		nmbm_log_lower(nld, NMBM_LOG_ERR,
 | |
| -			       "max reserved blocks %u is too small\n", nld->max_reserved_blocks);
 | |
| -		return false;
 | |
| -	}
 | |
| -
 | |
| -	return true;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_calc_structure_size - Calculate the instance structure size
 | |
| - * @nld: NMBM lower device structure
 | |
| - */
 | |
| -size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld)
 | |
| -{
 | |
| -	uint32_t state_table_size, mapping_table_size, info_table_size;
 | |
| -	uint32_t block_count;
 | |
| -
 | |
| -	block_count = nmbm_lldiv(nld->size, nld->erasesize);
 | |
| -
 | |
| -	/* Calculate info table size */
 | |
| -	state_table_size = ((block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) /
 | |
| -		NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE;
 | |
| -	mapping_table_size = block_count * sizeof(int32_t);
 | |
| -
 | |
| -	info_table_size = NMBM_ALIGN(sizeof(struct nmbm_info_table_header),
 | |
| -				     nld->writesize);
 | |
| -	info_table_size += NMBM_ALIGN(state_table_size, nld->writesize);
 | |
| -	info_table_size += NMBM_ALIGN(mapping_table_size, nld->writesize);
 | |
| -
 | |
| -	return info_table_size + state_table_size + mapping_table_size +
 | |
| -		nld->writesize + nld->oobsize + sizeof(struct nmbm_instance);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_init_structure - Initialize members of instance structure
 | |
| - * @ni: NMBM instance structure
 | |
| - */
 | |
| -static void nmbm_init_structure(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	uint32_t pages_per_block, blocks_per_chip;
 | |
| -	uintptr_t ptr;
 | |
| -
 | |
| -	pages_per_block = ni->lower.erasesize / ni->lower.writesize;
 | |
| -	blocks_per_chip = nmbm_lldiv(ni->lower.size, ni->lower.erasesize);
 | |
| -
 | |
| -	ni->rawpage_size = ni->lower.writesize + ni->lower.oobsize;
 | |
| -	ni->rawblock_size = pages_per_block * ni->rawpage_size;
 | |
| -	ni->rawchip_size = blocks_per_chip * ni->rawblock_size;
 | |
| -
 | |
| -	ni->writesize_mask = ni->lower.writesize - 1;
 | |
| -	ni->erasesize_mask = ni->lower.erasesize - 1;
 | |
| -
 | |
| -	ni->writesize_shift = ffs(ni->lower.writesize) - 1;
 | |
| -	ni->erasesize_shift = ffs(ni->lower.erasesize) - 1;
 | |
| -
 | |
| -	/* Calculate number of block this chip */
 | |
| -	ni->block_count = ni->lower.size >> ni->erasesize_shift;
 | |
| -
 | |
| -	/* Calculate info table size */
 | |
| -	ni->state_table_size = ((ni->block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) /
 | |
| -		NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE;
 | |
| -	ni->mapping_table_size = ni->block_count * sizeof(*ni->block_mapping);
 | |
| -
 | |
| -	ni->info_table_size = NMBM_ALIGN(sizeof(ni->info_table),
 | |
| -					 ni->lower.writesize);
 | |
| -	ni->info_table.state_table_off = ni->info_table_size;
 | |
| -
 | |
| -	ni->info_table_size += NMBM_ALIGN(ni->state_table_size,
 | |
| -					  ni->lower.writesize);
 | |
| -	ni->info_table.mapping_table_off = ni->info_table_size;
 | |
| -
 | |
| -	ni->info_table_size += NMBM_ALIGN(ni->mapping_table_size,
 | |
| -					  ni->lower.writesize);
 | |
| -
 | |
| -	ni->info_table_spare_blocks = nmbm_get_spare_block_count(
 | |
| -		size2blk(ni, ni->info_table_size));
 | |
| -
 | |
| -	/* Assign memory to members */
 | |
| -	ptr = (uintptr_t)ni + sizeof(*ni);
 | |
| -
 | |
| -	ni->info_table_cache = (void *)ptr;
 | |
| -	ptr += ni->info_table_size;
 | |
| -
 | |
| -	ni->block_state = (void *)ptr;
 | |
| -	ptr += ni->state_table_size;
 | |
| -
 | |
| -	ni->block_mapping = (void *)ptr;
 | |
| -	ptr += ni->mapping_table_size;
 | |
| -
 | |
| -	ni->page_cache = (uint8_t *)ptr;
 | |
| -
 | |
| -	/* Initialize block state table */
 | |
| -	ni->block_state_changed = 0;
 | |
| -	memset(ni->block_state, 0xff, ni->state_table_size);
 | |
| -
 | |
| -	/* Initialize block mapping table */
 | |
| -	ni->block_mapping_changed = 0;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_attach - Attach to a lower device
 | |
| - * @nld: NMBM lower device structure
 | |
| - * @ni: NMBM instance structure
 | |
| - */
 | |
| -int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni)
 | |
| -{
 | |
| -	bool success;
 | |
| -
 | |
| -	if (!nld || !ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	/* Set default log level */
 | |
| -	ni->log_display_level = NMBM_DEFAULT_LOG_LEVEL;
 | |
| -
 | |
| -	/* Check lower members */
 | |
| -	success = nmbm_check_lower_members(nld);
 | |
| -	if (!success)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	/* Initialize NMBM instance */
 | |
| -	memcpy(&ni->lower, nld, sizeof(struct nmbm_lower_device));
 | |
| -	nmbm_init_structure(ni);
 | |
| -
 | |
| -	success = nmbm_find_signature(ni, &ni->signature, &ni->signature_ba);
 | |
| -	if (!success) {
 | |
| -		if (!(nld->flags & NMBM_F_CREATE)) {
 | |
| -			nlog_err(ni, "Signature not found\n");
 | |
| -			return -ENODEV;
 | |
| -		}
 | |
| -
 | |
| -		success = nmbm_create_new(ni);
 | |
| -		if (!success)
 | |
| -			return -ENODEV;
 | |
| -
 | |
| -		return 0;
 | |
| -	}
 | |
| -
 | |
| -	nlog_info(ni, "Signature found at block %u [0x%08llx]\n",
 | |
| -		 ni->signature_ba, ba2addr(ni, ni->signature_ba));
 | |
| -	nmbm_mark_block_color_signature(ni, ni->signature_ba);
 | |
| -
 | |
| -	if (ni->signature.header.version != NMBM_VER) {
 | |
| -		nlog_err(ni, "NMBM version %u.%u is not supported\n",
 | |
| -			NMBM_VERSION_MAJOR_GET(ni->signature.header.version),
 | |
| -			NMBM_VERSION_MINOR_GET(ni->signature.header.version));
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (ni->signature.nand_size != nld->size ||
 | |
| -	    ni->signature.block_size != nld->erasesize ||
 | |
| -	    ni->signature.page_size != nld->writesize ||
 | |
| -	    ni->signature.spare_size != nld->oobsize) {
 | |
| -		nlog_err(ni, "NMBM configuration mismatch\n");
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	success = nmbm_load_existing(ni);
 | |
| -	if (!success)
 | |
| -		return -ENODEV;
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_detach - Detach from a lower device, and save all tables
 | |
| - * @ni: NMBM instance structure
 | |
| - */
 | |
| -int nmbm_detach(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	if (!(ni->lower.flags & NMBM_F_READ_ONLY))
 | |
| -		nmbm_update_info_table(ni);
 | |
| -
 | |
| -	nmbm_mark_block_color_normal(ni, 0, ni->block_count - 1);
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_erase_logic_block - Erase a logic block
 | |
| - * @ni: NMBM instance structure
 | |
| - * @nmbm_erase_logic_block: logic block address
 | |
| - *
 | |
| - * Logic block will be mapped to physical block before erasing.
 | |
| - * Bad block found during erasinh will be remapped to a good block if there is
 | |
| - * still at least one good spare block available.
 | |
| - */
 | |
| -static int nmbm_erase_logic_block(struct nmbm_instance *ni, uint32_t block_addr)
 | |
| -{
 | |
| -	uint32_t pb;
 | |
| -	bool success;
 | |
| -
 | |
| -retry:
 | |
| -	/* Map logic block to physical block */
 | |
| -	pb = ni->block_mapping[block_addr];
 | |
| -
 | |
| -	/* Whether the logic block is good (has valid mapping) */
 | |
| -	if ((int32_t)pb < 0) {
 | |
| -		nlog_debug(ni, "Logic block %u is a bad block\n", block_addr);
 | |
| -		return -EIO;
 | |
| -	}
 | |
| -
 | |
| -	/* Remap logic block if current physical block is a bad block */
 | |
| -	if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD ||
 | |
| -	    nmbm_get_block_state(ni, pb) == BLOCK_ST_NEED_REMAP)
 | |
| -		goto remap_logic_block;
 | |
| -
 | |
| -	/* Insurance to detect unexpected bad block marked by user */
 | |
| -	if (nmbm_check_bad_phys_block(ni, pb)) {
 | |
| -		nlog_warn(ni, "Found unexpected bad block possibly marked by user\n");
 | |
| -		nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
 | |
| -		goto remap_logic_block;
 | |
| -	}
 | |
| -
 | |
| -	success = nmbm_erase_block_and_check(ni, pb);
 | |
| -	if (success)
 | |
| -		return 0;
 | |
| -
 | |
| -	/* Mark bad block */
 | |
| -	nmbm_mark_phys_bad_block(ni, pb);
 | |
| -	nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
 | |
| -
 | |
| -remap_logic_block:
 | |
| -	/* Try to assign a new block */
 | |
| -	success = nmbm_map_block(ni, block_addr);
 | |
| -	if (!success) {
 | |
| -		/* Mark logic block unusable, and update info table */
 | |
| -		ni->block_mapping[block_addr] = -1;
 | |
| -		if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP)
 | |
| -			nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
 | |
| -		nmbm_update_info_table(ni);
 | |
| -		return -EIO;
 | |
| -	}
 | |
| -
 | |
| -	/* Update info table before erasing */
 | |
| -	if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP)
 | |
| -		nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
 | |
| -	nmbm_update_info_table(ni);
 | |
| -
 | |
| -	goto retry;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_erase_block_range - Erase logic blocks
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - * @size: erase range
 | |
| - * @failed_addr: return failed block address if error occurs
 | |
| - */
 | |
| -int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr,
 | |
| -			   uint64_t size, uint64_t *failed_addr)
 | |
| -{
 | |
| -	uint32_t start_ba, end_ba;
 | |
| -	int ret;
 | |
| -
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	/* Sanity check */
 | |
| -	if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
 | |
| -		nlog_debug(ni, "Device is forced read-only\n");
 | |
| -		return -EROFS;
 | |
| -	}
 | |
| -
 | |
| -	if (addr >= ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Address 0x%llx is invalid\n", addr);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (addr + size > ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Erase range 0xllxu is too large\n", size);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (!size) {
 | |
| -		nlog_warn(ni, "No blocks to be erased\n");
 | |
| -		return 0;
 | |
| -	}
 | |
| -
 | |
| -	start_ba = addr2ba(ni, addr);
 | |
| -	end_ba = addr2ba(ni, addr + size - 1);
 | |
| -
 | |
| -	while (start_ba <= end_ba) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		ret = nmbm_erase_logic_block(ni, start_ba);
 | |
| -		if (ret) {
 | |
| -			if (failed_addr)
 | |
| -				*failed_addr = ba2addr(ni, start_ba);
 | |
| -			return ret;
 | |
| -		}
 | |
| -
 | |
| -		start_ba++;
 | |
| -	}
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_read_logic_page - Read page based on logic address
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - * @data: buffer to store main data. optional.
 | |
| - * @oob: buffer to store oob data. optional.
 | |
| - * @mode: read mode
 | |
| - *
 | |
| - * Return 0 for success, positive value for corrected bitflip count,
 | |
| - * -EBADMSG for ecc error, other negative values for other errors
 | |
| - */
 | |
| -static int nmbm_read_logic_page(struct nmbm_instance *ni, uint64_t addr,
 | |
| -				void *data, void *oob, enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	uint32_t lb, pb, offset;
 | |
| -	uint64_t paddr;
 | |
| -
 | |
| -	/* Extract block address and in-block offset */
 | |
| -	lb = addr2ba(ni, addr);
 | |
| -	offset = addr & ni->erasesize_mask;
 | |
| -
 | |
| -	/* Map logic block to physical block */
 | |
| -	pb = ni->block_mapping[lb];
 | |
| -
 | |
| -	/* Whether the logic block is good (has valid mapping) */
 | |
| -	if ((int32_t)pb < 0) {
 | |
| -		nlog_debug(ni, "Logic block %u is a bad block\n", lb);
 | |
| -		return -EIO;
 | |
| -	}
 | |
| -
 | |
| -	/* Fail if physical block is marked bad */
 | |
| -	if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
 | |
| -		return -EIO;
 | |
| -
 | |
| -	/* Assemble new address */
 | |
| -	paddr = ba2addr(ni, pb) + offset;
 | |
| -
 | |
| -	return nmbm_read_phys_page(ni, paddr, data, oob, mode);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_read_single_page - Read one page based on logic address
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - * @data: buffer to store main data. optional.
 | |
| - * @oob: buffer to store oob data. optional.
 | |
| - * @mode: read mode
 | |
| - *
 | |
| - * Return 0 for success, positive value for corrected bitflip count,
 | |
| - * -EBADMSG for ecc error, other negative values for other errors
 | |
| - */
 | |
| -int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data,
 | |
| -			  void *oob, enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	/* Sanity check */
 | |
| -	if (ni->protected) {
 | |
| -		nlog_debug(ni, "Device is forced read-only\n");
 | |
| -		return -EROFS;
 | |
| -	}
 | |
| -
 | |
| -	if (addr >= ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Address 0x%llx is invalid\n", addr);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	return nmbm_read_logic_page(ni, addr, data, oob, mode);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_read_range - Read data without oob
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - * @size: data size to read
 | |
| - * @data: buffer to store main data to be read
 | |
| - * @mode: read mode
 | |
| - * @retlen: return actual data size read
 | |
| - *
 | |
| - * Return 0 for success, positive value for corrected bitflip count,
 | |
| - * -EBADMSG for ecc error, other negative values for other errors
 | |
| - */
 | |
| -int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
 | |
| -		    void *data, enum nmbm_oob_mode mode, size_t *retlen)
 | |
| -{
 | |
| -	uint64_t off = addr;
 | |
| -	uint8_t *ptr = data;
 | |
| -	size_t sizeremain = size, chunksize, leading;
 | |
| -	bool has_ecc_err = false;
 | |
| -	int ret, max_bitflips = 0;
 | |
| -
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	/* Sanity check */
 | |
| -	if (ni->protected) {
 | |
| -		nlog_debug(ni, "Device is forced read-only\n");
 | |
| -		return -EROFS;
 | |
| -	}
 | |
| -
 | |
| -	if (addr >= ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Address 0x%llx is invalid\n", addr);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (addr + size > ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Read range 0x%llx is too large\n", size);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (!size) {
 | |
| -		nlog_warn(ni, "No data to be read\n");
 | |
| -		return 0;
 | |
| -	}
 | |
| -
 | |
| -	while (sizeremain) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		leading = off & ni->writesize_mask;
 | |
| -		chunksize = ni->lower.writesize - leading;
 | |
| -		if (chunksize > sizeremain)
 | |
| -			chunksize = sizeremain;
 | |
| -
 | |
| -		if (chunksize == ni->lower.writesize) {
 | |
| -			ret = nmbm_read_logic_page(ni, off - leading, ptr,
 | |
| -							NULL, mode);
 | |
| -			if (ret < 0 && ret != -EBADMSG)
 | |
| -				break;
 | |
| -		} else {
 | |
| -			ret = nmbm_read_logic_page(ni, off - leading,
 | |
| -							ni->page_cache, NULL,
 | |
| -							mode);
 | |
| -			if (ret < 0 && ret != -EBADMSG)
 | |
| -				break;
 | |
| -
 | |
| -			memcpy(ptr, ni->page_cache + leading, chunksize);
 | |
| -		}
 | |
| -
 | |
| -		if (ret == -EBADMSG)
 | |
| -			has_ecc_err = true;
 | |
| -
 | |
| -		if (ret > max_bitflips)
 | |
| -			max_bitflips = ret;
 | |
| -
 | |
| -		off += chunksize;
 | |
| -		ptr += chunksize;
 | |
| -		sizeremain -= chunksize;
 | |
| -	}
 | |
| -
 | |
| -	if (retlen)
 | |
| -		*retlen = size - sizeremain;
 | |
| -
 | |
| -	if (ret < 0 && ret != -EBADMSG)
 | |
| -		return ret;
 | |
| -
 | |
| -	if (has_ecc_err)
 | |
| -		return -EBADMSG;
 | |
| -
 | |
| -	return max_bitflips;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_write_logic_page - Read page based on logic address
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - * @data: buffer contains main data. optional.
 | |
| - * @oob: buffer contains oob data. optional.
 | |
| - * @mode: write mode
 | |
| - */
 | |
| -static int nmbm_write_logic_page(struct nmbm_instance *ni, uint64_t addr,
 | |
| -				  const void *data, const void *oob,
 | |
| -				  enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	uint32_t lb, pb, offset;
 | |
| -	uint64_t paddr;
 | |
| -	bool success;
 | |
| -
 | |
| -	/* Extract block address and in-block offset */
 | |
| -	lb = addr2ba(ni, addr);
 | |
| -	offset = addr & ni->erasesize_mask;
 | |
| -
 | |
| -	/* Map logic block to physical block */
 | |
| -	pb = ni->block_mapping[lb];
 | |
| -
 | |
| -	/* Whether the logic block is good (has valid mapping) */
 | |
| -	if ((int32_t)pb < 0) {
 | |
| -		nlog_debug(ni, "Logic block %u is a bad block\n", lb);
 | |
| -		return -EIO;
 | |
| -	}
 | |
| -
 | |
| -	/* Fail if physical block is marked bad */
 | |
| -	if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
 | |
| -		return -EIO;
 | |
| -
 | |
| -	/* Assemble new address */
 | |
| -	paddr = ba2addr(ni, pb) + offset;
 | |
| -
 | |
| -	success = nmbm_write_phys_page(ni, paddr, data, oob, mode);
 | |
| -	if (success)
 | |
| -		return 0;
 | |
| -
 | |
| -	/*
 | |
| -	 * Do not remap bad block here. Just mark this block in state table.
 | |
| -	 * Remap this block on erasing.
 | |
| -	 */
 | |
| -	nmbm_set_block_state(ni, pb, BLOCK_ST_NEED_REMAP);
 | |
| -	nmbm_update_info_table(ni);
 | |
| -
 | |
| -	return -EIO;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_write_single_page - Write one page based on logic address
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - * @data: buffer contains main data. optional.
 | |
| - * @oob: buffer contains oob data. optional.
 | |
| - * @mode: write mode
 | |
| - */
 | |
| -int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr,
 | |
| -			   const void *data, const void *oob,
 | |
| -			   enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	/* Sanity check */
 | |
| -	if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
 | |
| -		nlog_debug(ni, "Device is forced read-only\n");
 | |
| -		return -EROFS;
 | |
| -	}
 | |
| -
 | |
| -	if (addr >= ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Address 0x%llx is invalid\n", addr);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	return nmbm_write_logic_page(ni, addr, data, oob, mode);
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_write_range - Write data without oob
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - * @size: data size to write
 | |
| - * @data: buffer contains data to be written
 | |
| - * @mode: write mode
 | |
| - * @retlen: return actual data size written
 | |
| - */
 | |
| -int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
 | |
| -		     const void *data, enum nmbm_oob_mode mode,
 | |
| -		     size_t *retlen)
 | |
| -{
 | |
| -	uint64_t off = addr;
 | |
| -	const uint8_t *ptr = data;
 | |
| -	size_t sizeremain = size, chunksize, leading;
 | |
| -	int ret;
 | |
| -
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	/* Sanity check */
 | |
| -	if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
 | |
| -		nlog_debug(ni, "Device is forced read-only\n");
 | |
| -		return -EROFS;
 | |
| -	}
 | |
| -
 | |
| -	if (addr >= ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Address 0x%llx is invalid\n", addr);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (addr + size > ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Write size 0x%zx is too large\n", size);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (!size) {
 | |
| -		nlog_warn(ni, "No data to be written\n");
 | |
| -		return 0;
 | |
| -	}
 | |
| -
 | |
| -	while (sizeremain) {
 | |
| -		WATCHDOG_RESET();
 | |
| -
 | |
| -		leading = off & ni->writesize_mask;
 | |
| -		chunksize = ni->lower.writesize - leading;
 | |
| -		if (chunksize > sizeremain)
 | |
| -			chunksize = sizeremain;
 | |
| -
 | |
| -		if (chunksize == ni->lower.writesize) {
 | |
| -			ret = nmbm_write_logic_page(ni, off - leading, ptr,
 | |
| -							 NULL, mode);
 | |
| -			if (ret)
 | |
| -				break;
 | |
| -		} else {
 | |
| -			memset(ni->page_cache, 0xff, leading);
 | |
| -			memcpy(ni->page_cache + leading, ptr, chunksize);
 | |
| -
 | |
| -			ret = nmbm_write_logic_page(ni, off - leading,
 | |
| -							 ni->page_cache, NULL,
 | |
| -							 mode);
 | |
| -			if (ret)
 | |
| -				break;
 | |
| -		}
 | |
| -
 | |
| -		off += chunksize;
 | |
| -		ptr += chunksize;
 | |
| -		sizeremain -= chunksize;
 | |
| -	}
 | |
| -
 | |
| -	if (retlen)
 | |
| -		*retlen = size - sizeremain;
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_check_bad_block - Check whether a logic block is usable
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - */
 | |
| -int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr)
 | |
| -{
 | |
| -	uint32_t lb, pb;
 | |
| -
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	if (addr >= ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Address 0x%llx is invalid\n", addr);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	lb = addr2ba(ni, addr);
 | |
| -
 | |
| -	/* Map logic block to physical block */
 | |
| -	pb = ni->block_mapping[lb];
 | |
| -
 | |
| -	if ((int32_t)pb < 0)
 | |
| -		return 1;
 | |
| -
 | |
| -	if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
 | |
| -		return 1;
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_mark_bad_block - Mark a logic block unusable
 | |
| - * @ni: NMBM instance structure
 | |
| - * @addr: logic linear address
 | |
| - */
 | |
| -int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr)
 | |
| -{
 | |
| -	uint32_t lb, pb;
 | |
| -
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	/* Sanity check */
 | |
| -	if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
 | |
| -		nlog_debug(ni, "Device is forced read-only\n");
 | |
| -		return -EROFS;
 | |
| -	}
 | |
| -
 | |
| -	if (addr >= ba2addr(ni, ni->data_block_count)) {
 | |
| -		nlog_err(ni, "Address 0x%llx is invalid\n", addr);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	lb = addr2ba(ni, addr);
 | |
| -
 | |
| -	/* Map logic block to physical block */
 | |
| -	pb = ni->block_mapping[lb];
 | |
| -
 | |
| -	if ((int32_t)pb < 0)
 | |
| -		return 0;
 | |
| -
 | |
| -	ni->block_mapping[lb] = -1;
 | |
| -	nmbm_mark_phys_bad_block(ni, pb);
 | |
| -	nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
 | |
| -	nmbm_update_info_table(ni);
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_get_avail_size - Get available user data size
 | |
| - * @ni: NMBM instance structure
 | |
| - */
 | |
| -uint64_t nmbm_get_avail_size(struct nmbm_instance *ni)
 | |
| -{
 | |
| -	if (!ni)
 | |
| -		return 0;
 | |
| -
 | |
| -	return (uint64_t)ni->data_block_count << ni->erasesize_shift;
 | |
| -}
 | |
| -
 | |
| -/*
 | |
| - * nmbm_get_lower_device - Get lower device structure
 | |
| - * @ni: NMBM instance structure
 | |
| - * @nld: pointer to hold the data of lower device structure
 | |
| - */
 | |
| -int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld)
 | |
| -{
 | |
| -	if (!ni)
 | |
| -		return -EINVAL;
 | |
| -
 | |
| -	if (nld)
 | |
| -		memcpy(nld, &ni->lower, sizeof(*nld));
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -#include "nmbm-debug.inl"
 | |
| diff --git a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-debug.h b/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-debug.h
 | |
| deleted file mode 100644
 | |
| index 7f9dfed9e1..0000000000
 | |
| --- a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-debug.h
 | |
| +++ /dev/null
 | |
| @@ -1,20 +0,0 @@
 | |
| -/* SPDX-License-Identifier: GPL-2.0 */
 | |
| -/*
 | |
| - * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
 | |
| - *
 | |
| - * Debug addons for NAND Mapped-block Management (NMBM)
 | |
| - *
 | |
| - * Author: Weijie Gao <weijie.gao@mediatek.com>
 | |
| - */
 | |
| -
 | |
| -#ifndef _NMBM_DEBUG_H_
 | |
| -#define _NMBM_DEBUG_H_
 | |
| -
 | |
| -#define nmbm_mark_block_color_normal(ni, start_ba, end_ba)
 | |
| -#define nmbm_mark_block_color_bad(ni, ba)
 | |
| -#define nmbm_mark_block_color_mgmt(ni, start_ba, end_ba)
 | |
| -#define nmbm_mark_block_color_signature(ni, ba)
 | |
| -#define nmbm_mark_block_color_info_table(ni, start_ba, end_ba)
 | |
| -#define nmbm_mark_block_color_mapped(ni, ba)
 | |
| -
 | |
| -#endif /* _NMBM_DEBUG_H_ */
 | |
| diff --git a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-debug.inl b/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-debug.inl
 | |
| deleted file mode 100644
 | |
| index e69de29bb2..0000000000
 | |
| diff --git a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-mtd.c b/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-mtd.c
 | |
| deleted file mode 100644
 | |
| index a3e9e1832d..0000000000
 | |
| --- a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-mtd.c
 | |
| +++ /dev/null
 | |
| @@ -1,795 +0,0 @@
 | |
| -// SPDX-License-Identifier: GPL-2.0
 | |
| -/*
 | |
| - * MTD layer for NAND Mapped-block Management (NMBM)
 | |
| - *
 | |
| - * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
 | |
| - *
 | |
| - * Author: Weijie Gao <weijie.gao@mediatek.com>
 | |
| - */
 | |
| -
 | |
| -#include <linux/kernel.h>
 | |
| -#include <linux/module.h>
 | |
| -#include <linux/init.h>
 | |
| -#include <linux/device.h>
 | |
| -#include <linux/slab.h>
 | |
| -#include <linux/interrupt.h>
 | |
| -#include <linux/sched.h>
 | |
| -#include <linux/wait.h>
 | |
| -#include <linux/spinlock.h>
 | |
| -#include <linux/mtd/mtd.h>
 | |
| -#include <linux/mtd/flashchip.h>
 | |
| -#include <linux/mtd/partitions.h>
 | |
| -#include <linux/of_platform.h>
 | |
| -#include <linux/kern_levels.h>
 | |
| -
 | |
| -#include "nmbm-private.h"
 | |
| -#include "nmbm-debug.h"
 | |
| -
 | |
| -#define NMBM_MAX_RATIO_DEFAULT			1
 | |
| -#define NMBM_MAX_BLOCKS_DEFAULT			256
 | |
| -
 | |
| -struct nmbm_mtd {
 | |
| -	struct mtd_info upper;
 | |
| -	struct mtd_info *lower;
 | |
| -
 | |
| -	struct nmbm_instance *ni;
 | |
| -	uint8_t *page_cache;
 | |
| -
 | |
| -	flstate_t state;
 | |
| -	spinlock_t lock;
 | |
| -	wait_queue_head_t wq;
 | |
| -
 | |
| -	struct device *dev;
 | |
| -	struct list_head node;
 | |
| -};
 | |
| -
 | |
| -struct list_head nmbm_devs;
 | |
| -static DEFINE_MUTEX(nmbm_devs_lock);
 | |
| -
 | |
| -static int nmbm_lower_read_page(void *arg, uint64_t addr, void *buf, void *oob,
 | |
| -				enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = arg;
 | |
| -	struct mtd_oob_ops ops;
 | |
| -	int ret;
 | |
| -
 | |
| -	memset(&ops, 0, sizeof(ops));
 | |
| -
 | |
| -	switch (mode) {
 | |
| -	case NMBM_MODE_PLACE_OOB:
 | |
| -		ops.mode = MTD_OPS_PLACE_OOB;
 | |
| -		break;
 | |
| -	case NMBM_MODE_AUTO_OOB:
 | |
| -		ops.mode = MTD_OPS_AUTO_OOB;
 | |
| -		break;
 | |
| -	case NMBM_MODE_RAW:
 | |
| -		ops.mode = MTD_OPS_RAW;
 | |
| -		break;
 | |
| -	default:
 | |
| -		pr_debug("%s: unsupported NMBM mode: %u\n", __func__, mode);
 | |
| -		return -ENOTSUPP;
 | |
| -	}
 | |
| -
 | |
| -	if (buf) {
 | |
| -		ops.datbuf = buf;
 | |
| -		ops.len = nm->lower->writesize;
 | |
| -	}
 | |
| -
 | |
| -	if (oob) {
 | |
| -		ops.oobbuf = oob;
 | |
| -		ops.ooblen = mtd_oobavail(nm->lower, &ops);
 | |
| -	}
 | |
| -
 | |
| -	ret = mtd_read_oob(nm->lower, addr, &ops);
 | |
| -	nm->upper.ecc_stats.corrected = nm->lower->ecc_stats.corrected;
 | |
| -	nm->upper.ecc_stats.failed = nm->lower->ecc_stats.failed;
 | |
| -
 | |
| -	/* Report error on failure (including ecc error) */
 | |
| -	if (ret < 0 && ret != -EUCLEAN)
 | |
| -		return ret;
 | |
| -
 | |
| -	/*
 | |
| -	 * Since mtd_read_oob() won't report exact bitflips, what we can know
 | |
| -	 * is whether bitflips exceeds the threshold.
 | |
| -	 * We want the -EUCLEAN to be passed to the upper layer, but not the
 | |
| -	 * error value itself. To achieve this, report bitflips above the
 | |
| -	 * threshold.
 | |
| -	 */
 | |
| -
 | |
| -	if (ret == -EUCLEAN) {
 | |
| -		return min_t(u32, nm->lower->bitflip_threshold + 1,
 | |
| -			     nm->lower->ecc_strength);
 | |
| -	}
 | |
| -
 | |
| -	/* For bitflips less than the threshold, return 0 */
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -static int nmbm_lower_write_page(void *arg, uint64_t addr, const void *buf,
 | |
| -				 const void *oob, enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = arg;
 | |
| -	struct mtd_oob_ops ops;
 | |
| -
 | |
| -	memset(&ops, 0, sizeof(ops));
 | |
| -
 | |
| -	switch (mode) {
 | |
| -	case NMBM_MODE_PLACE_OOB:
 | |
| -		ops.mode = MTD_OPS_PLACE_OOB;
 | |
| -		break;
 | |
| -	case NMBM_MODE_AUTO_OOB:
 | |
| -		ops.mode = MTD_OPS_AUTO_OOB;
 | |
| -		break;
 | |
| -	case NMBM_MODE_RAW:
 | |
| -		ops.mode = MTD_OPS_RAW;
 | |
| -		break;
 | |
| -	default:
 | |
| -		pr_debug("%s: unsupported NMBM mode: %u\n", __func__, mode);
 | |
| -		return -ENOTSUPP;
 | |
| -	}
 | |
| -
 | |
| -	if (buf) {
 | |
| -		ops.datbuf = (uint8_t *)buf;
 | |
| -		ops.len = nm->lower->writesize;
 | |
| -	}
 | |
| -
 | |
| -	if (oob) {
 | |
| -		ops.oobbuf = (uint8_t *)oob;
 | |
| -		ops.ooblen = mtd_oobavail(nm->lower, &ops);
 | |
| -	}
 | |
| -
 | |
| -	return mtd_write_oob(nm->lower, addr, &ops);
 | |
| -}
 | |
| -
 | |
| -static int nmbm_lower_erase_block(void *arg, uint64_t addr)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = arg;
 | |
| -	struct erase_info ei;
 | |
| -
 | |
| -	memset(&ei, 0, sizeof(ei));
 | |
| -
 | |
| -	ei.addr = addr;
 | |
| -	ei.len = nm->lower->erasesize;
 | |
| -
 | |
| -	return mtd_erase(nm->lower, &ei);
 | |
| -}
 | |
| -
 | |
| -static int nmbm_lower_is_bad_block(void *arg, uint64_t addr)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = arg;
 | |
| -
 | |
| -	return mtd_block_isbad(nm->lower, addr);
 | |
| -}
 | |
| -
 | |
| -static int nmbm_lower_mark_bad_block(void *arg, uint64_t addr)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = arg;
 | |
| -
 | |
| -	return mtd_block_markbad(nm->lower, addr);
 | |
| -}
 | |
| -
 | |
| -static void nmbm_lower_log(void *arg, enum nmbm_log_category level,
 | |
| -			   const char *fmt, va_list ap)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = arg;
 | |
| -	char *msg;
 | |
| -	char *kl;
 | |
| -
 | |
| -	msg = kvasprintf(GFP_KERNEL, fmt, ap);
 | |
| -	if (!msg) {
 | |
| -		dev_warn(nm->dev, "unable to print log\n");
 | |
| -		return;
 | |
| -	}
 | |
| -
 | |
| -	switch (level) {
 | |
| -	case NMBM_LOG_DEBUG:
 | |
| -		kl = KERN_DEBUG;
 | |
| -		break;
 | |
| -	case NMBM_LOG_WARN:
 | |
| -		kl = KERN_WARNING;
 | |
| -		break;
 | |
| -	case NMBM_LOG_ERR:
 | |
| -		kl = KERN_ERR;
 | |
| -		break;
 | |
| -	case NMBM_LOG_EMERG:
 | |
| -		kl = KERN_EMERG;
 | |
| -		break;
 | |
| -	default:
 | |
| -		kl = KERN_INFO ;
 | |
| -	}
 | |
| -
 | |
| -	dev_printk(kl, nm->dev, "%s", msg);
 | |
| -
 | |
| -	kfree(msg);
 | |
| -}
 | |
| -
 | |
| -static int nmbm_get_device(struct nmbm_mtd *nm, int new_state)
 | |
| -{
 | |
| -	DECLARE_WAITQUEUE(wait, current);
 | |
| -
 | |
| -retry:
 | |
| -	spin_lock(&nm->lock);
 | |
| -
 | |
| -	if (nm->state == FL_READY) {
 | |
| -		nm->state = new_state;
 | |
| -		spin_unlock(&nm->lock);
 | |
| -		return 0;
 | |
| -	}
 | |
| -
 | |
| -	if (new_state == FL_PM_SUSPENDED) {
 | |
| -		if (nm->state == FL_PM_SUSPENDED) {
 | |
| -			spin_unlock(&nm->lock);
 | |
| -			return 0;
 | |
| -		}
 | |
| -	}
 | |
| -
 | |
| -	set_current_state(TASK_UNINTERRUPTIBLE);
 | |
| -	add_wait_queue(&nm->wq, &wait);
 | |
| -	spin_unlock(&nm->lock);
 | |
| -	schedule();
 | |
| -	remove_wait_queue(&nm->wq, &wait);
 | |
| -	goto retry;
 | |
| -}
 | |
| -
 | |
| -static void nmbm_release_device(struct nmbm_mtd *nm)
 | |
| -{
 | |
| -	spin_lock(&nm->lock);
 | |
| -	nm->state = FL_READY;
 | |
| -	wake_up(&nm->wq);
 | |
| -	spin_unlock(&nm->lock);
 | |
| -}
 | |
| -
 | |
| -static int nmbm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
 | |
| -	int ret;
 | |
| -
 | |
| -	nmbm_get_device(nm, FL_ERASING);
 | |
| -
 | |
| -	ret = nmbm_erase_block_range(nm->ni, instr->addr, instr->len,
 | |
| -				     &instr->fail_addr);
 | |
| -
 | |
| -	nmbm_release_device(nm);
 | |
| -
 | |
| -	if (!ret)
 | |
| -		return 0;
 | |
| -
 | |
| -	return -EIO;
 | |
| -}
 | |
| -
 | |
| -static int nmbm_mtd_read_data(struct nmbm_mtd *nm, uint64_t addr,
 | |
| -			      struct mtd_oob_ops *ops, enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	size_t len, ooblen, maxooblen, chklen;
 | |
| -	uint32_t col, ooboffs;
 | |
| -	uint8_t *datcache, *oobcache;
 | |
| -	bool has_ecc_err = false;
 | |
| -	int ret, max_bitflips = 0;
 | |
| -
 | |
| -	col = addr & nm->lower->writesize_mask;
 | |
| -	addr &= ~nm->lower->writesize_mask;
 | |
| -	maxooblen = mtd_oobavail(nm->lower, ops);
 | |
| -	ooboffs = ops->ooboffs;
 | |
| -	ooblen = ops->ooblen;
 | |
| -	len = ops->len;
 | |
| -
 | |
| -	datcache = len ? nm->page_cache : NULL;
 | |
| -	oobcache = ooblen ? nm->page_cache + nm->lower->writesize : NULL;
 | |
| -
 | |
| -	ops->oobretlen = 0;
 | |
| -	ops->retlen = 0;
 | |
| -
 | |
| -	while (len || ooblen) {
 | |
| -		ret = nmbm_read_single_page(nm->ni, addr, datcache, oobcache,
 | |
| -					    mode);
 | |
| -		if (ret < 0 && ret != -EBADMSG)
 | |
| -			return ret;
 | |
| -
 | |
| -		/* Continue reading on ecc error */
 | |
| -		if (ret == -EBADMSG)
 | |
| -			has_ecc_err = true;
 | |
| -
 | |
| -		/* Record the maximum bitflips between pages */
 | |
| -		if (ret > max_bitflips)
 | |
| -			max_bitflips = ret;
 | |
| -
 | |
| -		if (len) {
 | |
| -			/* Move data */
 | |
| -			chklen = nm->lower->writesize - col;
 | |
| -			if (chklen > len)
 | |
| -				chklen = len;
 | |
| -
 | |
| -			memcpy(ops->datbuf + ops->retlen, datcache + col,
 | |
| -			       chklen);
 | |
| -			len -= chklen;
 | |
| -			col = 0; /* (col + chklen) %  */
 | |
| -			ops->retlen += chklen;
 | |
| -		}
 | |
| -
 | |
| -		if (ooblen) {
 | |
| -			/* Move oob */
 | |
| -			chklen = maxooblen - ooboffs;
 | |
| -			if (chklen > ooblen)
 | |
| -				chklen = ooblen;
 | |
| -
 | |
| -			memcpy(ops->oobbuf + ops->oobretlen, oobcache + ooboffs,
 | |
| -			       chklen);
 | |
| -			ooblen -= chklen;
 | |
| -			ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */
 | |
| -			ops->oobretlen += chklen;
 | |
| -		}
 | |
| -
 | |
| -		addr += nm->lower->writesize;
 | |
| -	}
 | |
| -
 | |
| -	if (has_ecc_err)
 | |
| -		return -EBADMSG;
 | |
| -
 | |
| -	return max_bitflips;
 | |
| -}
 | |
| -
 | |
| -static int nmbm_mtd_read_oob(struct mtd_info *mtd, loff_t from,
 | |
| -			     struct mtd_oob_ops *ops)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
 | |
| -	uint32_t maxooblen;
 | |
| -	enum nmbm_oob_mode mode;
 | |
| -	int ret;
 | |
| -
 | |
| -	if (!ops->oobbuf && !ops->datbuf) {
 | |
| -		if (ops->ooblen || ops->len)
 | |
| -			return -EINVAL;
 | |
| -
 | |
| -		return 0;
 | |
| -	}
 | |
| -
 | |
| -	switch (ops->mode) {
 | |
| -	case MTD_OPS_PLACE_OOB:
 | |
| -		mode = NMBM_MODE_PLACE_OOB;
 | |
| -		break;
 | |
| -	case MTD_OPS_AUTO_OOB:
 | |
| -		mode = NMBM_MODE_AUTO_OOB;
 | |
| -		break;
 | |
| -	case MTD_OPS_RAW:
 | |
| -		mode = NMBM_MODE_RAW;
 | |
| -		break;
 | |
| -	default:
 | |
| -		pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode);
 | |
| -		return -ENOTSUPP;
 | |
| -	}
 | |
| -
 | |
| -	maxooblen = mtd_oobavail(mtd, ops);
 | |
| -
 | |
| -	/* Do not allow read past end of device */
 | |
| -	if (ops->datbuf && (from + ops->len) > mtd->size) {
 | |
| -		pr_debug("%s: attempt to read beyond end of device\n",
 | |
| -			 __func__);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (!ops->oobbuf) {
 | |
| -		nmbm_get_device(nm, FL_READING);
 | |
| -
 | |
| -		/* Optimized for reading data only */
 | |
| -		ret = nmbm_read_range(nm->ni, from, ops->len, ops->datbuf,
 | |
| -				      mode, &ops->retlen);
 | |
| -
 | |
| -		nmbm_release_device(nm);
 | |
| -
 | |
| -		return ret;
 | |
| -	}
 | |
| -
 | |
| -	if (unlikely(ops->ooboffs >= maxooblen)) {
 | |
| -		pr_debug("%s: attempt to start read outside oob\n",
 | |
| -			__func__);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (unlikely(from >= mtd->size ||
 | |
| -	    ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) -
 | |
| -	    (from >> mtd->writesize_shift)) * maxooblen)) {
 | |
| -		pr_debug("%s: attempt to read beyond end of device\n",
 | |
| -				__func__);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	nmbm_get_device(nm, FL_READING);
 | |
| -	ret = nmbm_mtd_read_data(nm, from, ops, mode);
 | |
| -	nmbm_release_device(nm);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static int nmbm_mtd_write_data(struct nmbm_mtd *nm, uint64_t addr,
 | |
| -			       struct mtd_oob_ops *ops, enum nmbm_oob_mode mode)
 | |
| -{
 | |
| -	size_t len, ooblen, maxooblen, chklen;
 | |
| -	uint32_t col, ooboffs;
 | |
| -	uint8_t *datcache, *oobcache;
 | |
| -	int ret;
 | |
| -
 | |
| -	col = addr & nm->lower->writesize_mask;
 | |
| -	addr &= ~nm->lower->writesize_mask;
 | |
| -	maxooblen = mtd_oobavail(nm->lower, ops);
 | |
| -	ooboffs = ops->ooboffs;
 | |
| -	ooblen = ops->ooblen;
 | |
| -	len = ops->len;
 | |
| -
 | |
| -	datcache = len ? nm->page_cache : NULL;
 | |
| -	oobcache = ooblen ? nm->page_cache + nm->lower->writesize : NULL;
 | |
| -
 | |
| -	ops->oobretlen = 0;
 | |
| -	ops->retlen = 0;
 | |
| -
 | |
| -	while (len || ooblen) {
 | |
| -		if (len) {
 | |
| -			/* Move data */
 | |
| -			chklen = nm->lower->writesize - col;
 | |
| -			if (chklen > len)
 | |
| -				chklen = len;
 | |
| -
 | |
| -			memset(datcache, 0xff, col);
 | |
| -			memcpy(datcache + col, ops->datbuf + ops->retlen,
 | |
| -			       chklen);
 | |
| -			memset(datcache + col + chklen, 0xff,
 | |
| -			       nm->lower->writesize - col - chklen);
 | |
| -			len -= chklen;
 | |
| -			col = 0; /* (col + chklen) %  */
 | |
| -			ops->retlen += chklen;
 | |
| -		}
 | |
| -
 | |
| -		if (ooblen) {
 | |
| -			/* Move oob */
 | |
| -			chklen = maxooblen - ooboffs;
 | |
| -			if (chklen > ooblen)
 | |
| -				chklen = ooblen;
 | |
| -
 | |
| -			memset(oobcache, 0xff, ooboffs);
 | |
| -			memcpy(oobcache + ooboffs,
 | |
| -			       ops->oobbuf + ops->oobretlen, chklen);
 | |
| -			memset(oobcache + ooboffs + chklen, 0xff,
 | |
| -			       nm->lower->oobsize - ooboffs - chklen);
 | |
| -			ooblen -= chklen;
 | |
| -			ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */
 | |
| -			ops->oobretlen += chklen;
 | |
| -		}
 | |
| -
 | |
| -		ret = nmbm_write_single_page(nm->ni, addr, datcache, oobcache,
 | |
| -					     mode);
 | |
| -		if (ret)
 | |
| -			return ret;
 | |
| -
 | |
| -		addr += nm->lower->writesize;
 | |
| -	}
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -static int nmbm_mtd_write_oob(struct mtd_info *mtd, loff_t to,
 | |
| -			      struct mtd_oob_ops *ops)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
 | |
| -	enum nmbm_oob_mode mode;
 | |
| -	uint32_t maxooblen;
 | |
| -	int ret;
 | |
| -
 | |
| -	if (!ops->oobbuf && !ops->datbuf) {
 | |
| -		if (ops->ooblen || ops->len)
 | |
| -			return -EINVAL;
 | |
| -
 | |
| -		return 0;
 | |
| -	}
 | |
| -
 | |
| -	switch (ops->mode) {
 | |
| -	case MTD_OPS_PLACE_OOB:
 | |
| -		mode = NMBM_MODE_PLACE_OOB;
 | |
| -		break;
 | |
| -	case MTD_OPS_AUTO_OOB:
 | |
| -		mode = NMBM_MODE_AUTO_OOB;
 | |
| -		break;
 | |
| -	case MTD_OPS_RAW:
 | |
| -		mode = NMBM_MODE_RAW;
 | |
| -		break;
 | |
| -	default:
 | |
| -		pr_debug("%s: unsupported oob mode: %u\n", __func__,
 | |
| -			 ops->mode);
 | |
| -		return -ENOTSUPP;
 | |
| -	}
 | |
| -
 | |
| -	maxooblen = mtd_oobavail(mtd, ops);
 | |
| -
 | |
| -	/* Do not allow write past end of device */
 | |
| -	if (ops->datbuf && (to + ops->len) > mtd->size) {
 | |
| -		pr_debug("%s: attempt to write beyond end of device\n",
 | |
| -			 __func__);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (!ops->oobbuf) {
 | |
| -		nmbm_get_device(nm, FL_WRITING);
 | |
| -
 | |
| -		/* Optimized for writing data only */
 | |
| -		ret = nmbm_write_range(nm->ni, to, ops->len, ops->datbuf,
 | |
| -				       mode, &ops->retlen);
 | |
| -
 | |
| -		nmbm_release_device(nm);
 | |
| -
 | |
| -		return ret;
 | |
| -	}
 | |
| -
 | |
| -	if (unlikely(ops->ooboffs >= maxooblen)) {
 | |
| -		pr_debug("%s: attempt to start write outside oob\n",
 | |
| -			__func__);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	if (unlikely(to >= mtd->size ||
 | |
| -	    ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) -
 | |
| -	    (to >> mtd->writesize_shift)) * maxooblen)) {
 | |
| -		pr_debug("%s: attempt to write beyond end of device\n",
 | |
| -				__func__);
 | |
| -		return -EINVAL;
 | |
| -	}
 | |
| -
 | |
| -	nmbm_get_device(nm, FL_WRITING);
 | |
| -	ret = nmbm_mtd_write_data(nm, to, ops, mode);
 | |
| -	nmbm_release_device(nm);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static int nmbm_mtd_block_isbad(struct mtd_info *mtd, loff_t offs)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
 | |
| -	int ret;
 | |
| -
 | |
| -	nmbm_get_device(nm, FL_READING);
 | |
| -	ret = nmbm_check_bad_block(nm->ni, offs);
 | |
| -	nmbm_release_device(nm);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static int nmbm_mtd_block_markbad(struct mtd_info *mtd, loff_t offs)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
 | |
| -	int ret;
 | |
| -
 | |
| -	nmbm_get_device(nm, FL_WRITING);
 | |
| -	ret = nmbm_mark_bad_block(nm->ni, offs);
 | |
| -	nmbm_release_device(nm);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static void nmbm_mtd_shutdown(struct mtd_info *mtd)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
 | |
| -
 | |
| -	nmbm_get_device(nm, FL_PM_SUSPENDED);
 | |
| -}
 | |
| -
 | |
| -static int nmbm_probe(struct platform_device *pdev)
 | |
| -{
 | |
| -	struct device_node *mtd_np, *np = pdev->dev.of_node;
 | |
| -	uint32_t max_ratio, max_reserved_blocks, alloc_size;
 | |
| -	bool forced_create, empty_page_ecc_ok;
 | |
| -	struct nmbm_lower_device nld;
 | |
| -	struct mtd_info *lower, *mtd;
 | |
| -	struct nmbm_mtd *nm;
 | |
| -	const char *mtdname;
 | |
| -	int ret;
 | |
| -
 | |
| -	mtd_np = of_parse_phandle(np, "lower-mtd-device", 0);
 | |
| -	if (mtd_np) {
 | |
| -		lower = get_mtd_device_by_node(mtd_np);
 | |
| -		if (!IS_ERR(lower))
 | |
| -			goto do_attach_mtd;
 | |
| -
 | |
| -		dev_dbg(&pdev->dev, "failed to find mtd device by phandle\n");
 | |
| -		return -EPROBE_DEFER;
 | |
| -	}
 | |
| -
 | |
| -	ret = of_property_read_string(np, "lower-mtd-name", &mtdname);
 | |
| -	if (!ret) {
 | |
| -		lower = get_mtd_device_nm(mtdname);
 | |
| -		if (!IS_ERR(lower))
 | |
| -			goto do_attach_mtd;
 | |
| -
 | |
| -		dev_dbg(&pdev->dev, "failed to find mtd device by name '%s'\n",
 | |
| -			mtdname);
 | |
| -		return -EPROBE_DEFER;
 | |
| -	}
 | |
| -
 | |
| -do_attach_mtd:
 | |
| -	if (of_property_read_u32(np, "max-ratio", &max_ratio))
 | |
| -		max_ratio = NMBM_MAX_RATIO_DEFAULT;
 | |
| -
 | |
| -	if (of_property_read_u32(np, "max-reserved-blocks",
 | |
| -				 &max_reserved_blocks))
 | |
| -		max_reserved_blocks = NMBM_MAX_BLOCKS_DEFAULT;
 | |
| -
 | |
| -	forced_create = of_property_read_bool(np, "forced-create");
 | |
| -	empty_page_ecc_ok = of_property_read_bool(np,
 | |
| -						  "empty-page-ecc-protected");
 | |
| -
 | |
| -	memset(&nld, 0, sizeof(nld));
 | |
| -
 | |
| -	nld.flags = 0;
 | |
| -
 | |
| -	if (forced_create)
 | |
| -		nld.flags |= NMBM_F_CREATE;
 | |
| -
 | |
| -	if (empty_page_ecc_ok)
 | |
| -		nld.flags |= NMBM_F_EMPTY_PAGE_ECC_OK;
 | |
| -
 | |
| -	nld.max_ratio = max_ratio;
 | |
| -	nld.max_reserved_blocks = max_reserved_blocks;
 | |
| -
 | |
| -	nld.size = lower->size;
 | |
| -	nld.erasesize = lower->erasesize;
 | |
| -	nld.writesize = lower->writesize;
 | |
| -	nld.oobsize = lower->oobsize;
 | |
| -	nld.oobavail = lower->oobavail;
 | |
| -
 | |
| -	nld.read_page = nmbm_lower_read_page;
 | |
| -	nld.write_page = nmbm_lower_write_page;
 | |
| -	nld.erase_block = nmbm_lower_erase_block;
 | |
| -	nld.is_bad_block = nmbm_lower_is_bad_block;
 | |
| -	nld.mark_bad_block = nmbm_lower_mark_bad_block;
 | |
| -
 | |
| -	nld.logprint = nmbm_lower_log;
 | |
| -
 | |
| -	alloc_size = nmbm_calc_structure_size(&nld);
 | |
| -
 | |
| -	nm = devm_kzalloc(&pdev->dev, sizeof(*nm) + alloc_size +
 | |
| -			  lower->writesize + lower->oobsize, GFP_KERNEL);
 | |
| -	if (!nm) {
 | |
| -		ret = -ENOMEM;
 | |
| -		goto out;
 | |
| -	}
 | |
| -
 | |
| -	nm->ni = (void *)nm + sizeof(*nm);
 | |
| -	nm->page_cache = (uint8_t *)nm->ni + alloc_size;
 | |
| -	nm->lower = lower;
 | |
| -	nm->dev = &pdev->dev;
 | |
| -
 | |
| -	INIT_LIST_HEAD(&nm->node);
 | |
| -	spin_lock_init(&nm->lock);
 | |
| -	init_waitqueue_head(&nm->wq);
 | |
| -
 | |
| -	nld.arg = nm;
 | |
| -
 | |
| -	ret = nmbm_attach(&nld, nm->ni);
 | |
| -	if (ret)
 | |
| -		goto out;
 | |
| -
 | |
| -	/* Initialize upper mtd */
 | |
| -	mtd = &nm->upper;
 | |
| -
 | |
| -	mtd->owner = THIS_MODULE;
 | |
| -	mtd->dev.parent = &pdev->dev;
 | |
| -	mtd->type = lower->type;
 | |
| -	mtd->flags = lower->flags;
 | |
| -
 | |
| -	mtd->size = (uint64_t)nm->ni->data_block_count * lower->erasesize;
 | |
| -	mtd->erasesize = lower->erasesize;
 | |
| -	mtd->writesize = lower->writesize;
 | |
| -	mtd->writebufsize = lower->writesize;
 | |
| -	mtd->oobsize = lower->oobsize;
 | |
| -	mtd->oobavail = lower->oobavail;
 | |
| -
 | |
| -	mtd->erasesize_shift = lower->erasesize_shift;
 | |
| -	mtd->writesize_shift = lower->writesize_shift;
 | |
| -	mtd->erasesize_mask = lower->erasesize_mask;
 | |
| -	mtd->writesize_mask = lower->writesize_mask;
 | |
| -
 | |
| -	mtd->bitflip_threshold = lower->bitflip_threshold;
 | |
| -
 | |
| -	mtd->ooblayout = lower->ooblayout;
 | |
| -
 | |
| -	mtd->ecc_step_size = lower->ecc_step_size;
 | |
| -	mtd->ecc_strength = lower->ecc_strength;
 | |
| -
 | |
| -	mtd->numeraseregions = lower->numeraseregions;
 | |
| -	mtd->eraseregions = lower->eraseregions;
 | |
| -
 | |
| -	mtd->_erase = nmbm_mtd_erase;
 | |
| -	mtd->_read_oob = nmbm_mtd_read_oob;
 | |
| -	mtd->_write_oob = nmbm_mtd_write_oob;
 | |
| -	mtd->_block_isbad = nmbm_mtd_block_isbad;
 | |
| -	mtd->_block_markbad = nmbm_mtd_block_markbad;
 | |
| -	mtd->_reboot = nmbm_mtd_shutdown;
 | |
| -
 | |
| -	mtd_set_of_node(mtd, np);
 | |
| -
 | |
| -	ret = mtd_device_register(mtd, NULL, 0);
 | |
| -	if (ret) {
 | |
| -		dev_err(&pdev->dev, "failed to register mtd device\n");
 | |
| -		nmbm_detach(nm->ni);
 | |
| -		goto out;
 | |
| -	}
 | |
| -
 | |
| -	platform_set_drvdata(pdev, nm);
 | |
| -
 | |
| -	mutex_lock(&nmbm_devs_lock);
 | |
| -	list_add_tail(&nm->node, &nmbm_devs);
 | |
| -	mutex_unlock(&nmbm_devs_lock);
 | |
| -
 | |
| -	return 0;
 | |
| -
 | |
| -out:
 | |
| -	if (nm)
 | |
| -		devm_kfree(&pdev->dev, nm);
 | |
| -
 | |
| -	put_mtd_device(lower);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static int nmbm_remove(struct platform_device *pdev)
 | |
| -{
 | |
| -	struct nmbm_mtd *nm = platform_get_drvdata(pdev);
 | |
| -	struct mtd_info *lower = nm->lower;
 | |
| -	int ret;
 | |
| -
 | |
| -	ret = mtd_device_unregister(&nm->upper);
 | |
| -	if (ret)
 | |
| -		return ret;
 | |
| -
 | |
| -	nmbm_detach(nm->ni);
 | |
| -
 | |
| -	mutex_lock(&nmbm_devs_lock);
 | |
| -	list_add_tail(&nm->node, &nmbm_devs);
 | |
| -	mutex_unlock(&nmbm_devs_lock);
 | |
| -
 | |
| -	devm_kfree(&pdev->dev, nm);
 | |
| -
 | |
| -	put_mtd_device(lower);
 | |
| -
 | |
| -	platform_set_drvdata(pdev, NULL);
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -static const struct of_device_id nmbm_ids[] = {
 | |
| -	{ .compatible = "generic,nmbm" },
 | |
| -	{ },
 | |
| -};
 | |
| -
 | |
| -MODULE_DEVICE_TABLE(of, nmbm_ids);
 | |
| -
 | |
| -static struct platform_driver nmbm_driver = {
 | |
| -	.probe = nmbm_probe,
 | |
| -	.remove = nmbm_remove,
 | |
| -	.driver = {
 | |
| -		.name = "nmbm",
 | |
| -		.of_match_table = nmbm_ids,
 | |
| -	},
 | |
| -};
 | |
| -
 | |
| -static int __init nmbm_init(void)
 | |
| -{
 | |
| -	int ret;
 | |
| -
 | |
| -	INIT_LIST_HEAD(&nmbm_devs);
 | |
| -
 | |
| -	ret = platform_driver_register(&nmbm_driver);
 | |
| -	if (ret) {
 | |
| -		pr_err("failed to register nmbm driver\n");
 | |
| -		return ret;
 | |
| -	}
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -module_init(nmbm_init);
 | |
| -
 | |
| -static void __exit nmbm_exit(void)
 | |
| -{
 | |
| -	platform_driver_unregister(&nmbm_driver);
 | |
| -}
 | |
| -module_exit(nmbm_exit);
 | |
| -
 | |
| -MODULE_LICENSE("GPL");
 | |
| -MODULE_AUTHOR("Weijie Gao <weijie.gao@mediatek.com>");
 | |
| -MODULE_DESCRIPTION("NAND mapping block management");
 | |
| diff --git a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-private.h b/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-private.h
 | |
| deleted file mode 100644
 | |
| index c285aeb9dd..0000000000
 | |
| --- a/target/linux/ramips/files/drivers/mtd/nmbm/nmbm-private.h
 | |
| +++ /dev/null
 | |
| @@ -1,137 +0,0 @@
 | |
| -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 | |
| -/*
 | |
| - * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
 | |
| - *
 | |
| - * Definitions for NAND Mapped-block Management (NMBM)
 | |
| - *
 | |
| - * Author: Weijie Gao <weijie.gao@mediatek.com>
 | |
| - */
 | |
| -
 | |
| -#ifndef _NMBM_PRIVATE_H_
 | |
| -#define _NMBM_PRIVATE_H_
 | |
| -
 | |
| -#include <nmbm/nmbm.h>
 | |
| -
 | |
| -#define NMBM_MAGIC_SIGNATURE			0x304d4d4e	/* NMM0 */
 | |
| -#define NMBM_MAGIC_INFO_TABLE			0x314d4d4e	/* NMM1 */
 | |
| -
 | |
| -#define NMBM_VERSION_MAJOR_S			0
 | |
| -#define NMBM_VERSION_MAJOR_M			0xffff
 | |
| -#define NMBM_VERSION_MINOR_S			16
 | |
| -#define NMBM_VERSION_MINOR_M			0xffff
 | |
| -#define NMBM_VERSION_MAKE(major, minor)		(((major) & NMBM_VERSION_MAJOR_M) | \
 | |
| -						(((minor) & NMBM_VERSION_MINOR_M) << \
 | |
| -						NMBM_VERSION_MINOR_S))
 | |
| -#define NMBM_VERSION_MAJOR_GET(ver)		(((ver) >> NMBM_VERSION_MAJOR_S) & \
 | |
| -						NMBM_VERSION_MAJOR_M)
 | |
| -#define NMBM_VERSION_MINOR_GET(ver)		(((ver) >> NMBM_VERSION_MINOR_S) & \
 | |
| -						NMBM_VERSION_MINOR_M)
 | |
| -
 | |
| -typedef uint32_t				nmbm_bitmap_t;
 | |
| -#define NMBM_BITMAP_UNIT_SIZE			(sizeof(nmbm_bitmap_t))
 | |
| -#define NMBM_BITMAP_BITS_PER_BLOCK		2
 | |
| -#define NMBM_BITMAP_BITS_PER_UNIT		(8 * sizeof(nmbm_bitmap_t))
 | |
| -#define NMBM_BITMAP_BLOCKS_PER_UNIT		(NMBM_BITMAP_BITS_PER_UNIT / \
 | |
| -						 NMBM_BITMAP_BITS_PER_BLOCK)
 | |
| -
 | |
| -#define NMBM_SPARE_BLOCK_MULTI			1
 | |
| -#define NMBM_SPARE_BLOCK_DIV			2
 | |
| -#define NMBM_SPARE_BLOCK_MIN			2
 | |
| -
 | |
| -#define NMBM_MGMT_DIV				16
 | |
| -#define NMBM_MGMT_BLOCKS_MIN			32
 | |
| -
 | |
| -#define NMBM_TRY_COUNT				3
 | |
| -
 | |
| -#define BLOCK_ST_BAD				0
 | |
| -#define BLOCK_ST_NEED_REMAP			2
 | |
| -#define BLOCK_ST_GOOD				3
 | |
| -#define BLOCK_ST_MASK				3
 | |
| -
 | |
| -struct nmbm_header {
 | |
| -	uint32_t magic;
 | |
| -	uint32_t version;
 | |
| -	uint32_t size;
 | |
| -	uint32_t checksum;
 | |
| -};
 | |
| -
 | |
| -struct nmbm_signature {
 | |
| -	struct nmbm_header header;
 | |
| -	uint64_t nand_size;
 | |
| -	uint32_t block_size;
 | |
| -	uint32_t page_size;
 | |
| -	uint32_t spare_size;
 | |
| -	uint32_t mgmt_start_pb;
 | |
| -	uint8_t max_try_count;
 | |
| -	uint8_t padding[3];
 | |
| -};
 | |
| -
 | |
| -struct nmbm_info_table_header {
 | |
| -	struct nmbm_header header;
 | |
| -	uint32_t write_count;
 | |
| -	uint32_t state_table_off;
 | |
| -	uint32_t mapping_table_off;
 | |
| -	uint32_t padding;
 | |
| -};
 | |
| -
 | |
| -struct nmbm_instance {
 | |
| -	struct nmbm_lower_device lower;
 | |
| -
 | |
| -	uint32_t rawpage_size;
 | |
| -	uint32_t rawblock_size;
 | |
| -	uint32_t rawchip_size;
 | |
| -
 | |
| -	uint32_t writesize_mask;
 | |
| -	uint32_t erasesize_mask;
 | |
| -	uint16_t writesize_shift;
 | |
| -	uint16_t erasesize_shift;
 | |
| -
 | |
| -	struct nmbm_signature signature;
 | |
| -
 | |
| -	uint8_t *info_table_cache;
 | |
| -	uint32_t info_table_size;
 | |
| -	uint32_t info_table_spare_blocks;
 | |
| -	struct nmbm_info_table_header info_table;
 | |
| -
 | |
| -	nmbm_bitmap_t *block_state;
 | |
| -	uint32_t block_state_changed;
 | |
| -	uint32_t state_table_size;
 | |
| -
 | |
| -	int32_t *block_mapping;
 | |
| -	uint32_t block_mapping_changed;
 | |
| -	uint32_t mapping_table_size;
 | |
| -
 | |
| -	uint8_t *page_cache;
 | |
| -
 | |
| -	int protected;
 | |
| -
 | |
| -	uint32_t block_count;
 | |
| -	uint32_t data_block_count;
 | |
| -
 | |
| -	uint32_t mgmt_start_ba;
 | |
| -	uint32_t main_table_ba;
 | |
| -	uint32_t backup_table_ba;
 | |
| -	uint32_t mapping_blocks_ba;
 | |
| -	uint32_t mapping_blocks_top_ba;
 | |
| -	uint32_t signature_ba;
 | |
| -
 | |
| -	enum nmbm_log_category log_display_level;
 | |
| -};
 | |
| -
 | |
| -/* Log utilities */
 | |
| -#define nlog_debug(ni, fmt, ...) \
 | |
| -	nmbm_log(ni, NMBM_LOG_DEBUG, fmt, ##__VA_ARGS__)
 | |
| -
 | |
| -#define nlog_info(ni, fmt, ...) \
 | |
| -	nmbm_log(ni, NMBM_LOG_INFO, fmt, ##__VA_ARGS__)
 | |
| -
 | |
| -#define nlog_warn(ni, fmt, ...) \
 | |
| -	nmbm_log(ni, NMBM_LOG_WARN, fmt, ##__VA_ARGS__)
 | |
| -
 | |
| -#define nlog_err(ni, fmt, ...) \
 | |
| -	nmbm_log(ni, NMBM_LOG_ERR, fmt, ##__VA_ARGS__)
 | |
| -
 | |
| -#define nlog_emerg(ni, fmt, ...) \
 | |
| -	nmbm_log(ni, NMBM_LOG_EMERG, fmt, ##__VA_ARGS__)
 | |
| -
 | |
| -#endif /* _NMBM_PRIVATE_H_ */
 | |
| diff --git a/target/linux/ramips/files/include/nmbm/nmbm-os.h b/target/linux/ramips/files/include/nmbm/nmbm-os.h
 | |
| deleted file mode 100644
 | |
| index 1cae854df1..0000000000
 | |
| --- a/target/linux/ramips/files/include/nmbm/nmbm-os.h
 | |
| +++ /dev/null
 | |
| @@ -1,69 +0,0 @@
 | |
| -/* SPDX-License-Identifier: GPL-2.0 */
 | |
| -/*
 | |
| - * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
 | |
| - *
 | |
| - * OS-dependent definitions for NAND Mapped-block Management (NMBM)
 | |
| - *
 | |
| - * Author: Weijie Gao <weijie.gao@mediatek.com>
 | |
| - */
 | |
| -
 | |
| -#ifndef _NMBM_OS_H_
 | |
| -#define _NMBM_OS_H_
 | |
| -
 | |
| -#include <linux/kernel.h>
 | |
| -#include <linux/limits.h>
 | |
| -#include <linux/string.h>
 | |
| -#include <linux/errno.h>
 | |
| -#include <linux/types.h>
 | |
| -#include <linux/crc32.h>
 | |
| -#include <linux/log2.h>
 | |
| -#include <asm/div64.h>
 | |
| -
 | |
| -static inline uint32_t nmbm_crc32(uint32_t crcval, const void *buf, size_t size)
 | |
| -{
 | |
| -	uint chksz;
 | |
| -	const unsigned char *p = buf;
 | |
| -
 | |
| -	while (size) {
 | |
| -		if (size > UINT_MAX)
 | |
| -			chksz = UINT_MAX;
 | |
| -		else
 | |
| -			chksz = (uint)size;
 | |
| -
 | |
| -		crcval = crc32_le(crcval, p, chksz);
 | |
| -		size -= chksz;
 | |
| -		p += chksz;
 | |
| -	}
 | |
| -
 | |
| -	return crcval;
 | |
| -}
 | |
| -
 | |
| -static inline uint32_t nmbm_lldiv(uint64_t dividend, uint32_t divisor)
 | |
| -{
 | |
| -#if BITS_PER_LONG == 64
 | |
| -	return dividend / divisor;
 | |
| -#else
 | |
| -	do_div(dividend, divisor);
 | |
| -	return dividend;
 | |
| -#endif
 | |
| -}
 | |
| -
 | |
| -#define WATCHDOG_RESET()
 | |
| -
 | |
| -#ifdef CONFIG_NMBM_LOG_LEVEL_DEBUG
 | |
| -#define NMBM_DEFAULT_LOG_LEVEL		0
 | |
| -#elif defined(NMBM_LOG_LEVEL_INFO)
 | |
| -#define NMBM_DEFAULT_LOG_LEVEL		1
 | |
| -#elif defined(NMBM_LOG_LEVEL_WARN)
 | |
| -#define NMBM_DEFAULT_LOG_LEVEL		2
 | |
| -#elif defined(NMBM_LOG_LEVEL_ERR)
 | |
| -#define NMBM_DEFAULT_LOG_LEVEL		3
 | |
| -#elif defined(NMBM_LOG_LEVEL_EMERG)
 | |
| -#define NMBM_DEFAULT_LOG_LEVEL		4
 | |
| -#elif defined(NMBM_LOG_LEVEL_NONE)
 | |
| -#define NMBM_DEFAULT_LOG_LEVEL		5
 | |
| -#else
 | |
| -#define NMBM_DEFAULT_LOG_LEVEL		1
 | |
| -#endif
 | |
| -
 | |
| -#endif /* _NMBM_OS_H_ */
 | |
| diff --git a/target/linux/ramips/files/include/nmbm/nmbm.h b/target/linux/ramips/files/include/nmbm/nmbm.h
 | |
| deleted file mode 100644
 | |
| index c0400988b1..0000000000
 | |
| --- a/target/linux/ramips/files/include/nmbm/nmbm.h
 | |
| +++ /dev/null
 | |
| @@ -1,102 +0,0 @@
 | |
| -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 | |
| -/*
 | |
| - * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
 | |
| - *
 | |
| - * Definitions for NAND Mapped-block Management (NMBM)
 | |
| - *
 | |
| - * Author: Weijie Gao <weijie.gao@mediatek.com>
 | |
| - */
 | |
| -
 | |
| -#ifndef _NMBM_H_
 | |
| -#define _NMBM_H_
 | |
| -
 | |
| -#include <nmbm/nmbm-os.h>
 | |
| -
 | |
| -enum nmbm_log_category {
 | |
| -	NMBM_LOG_DEBUG,
 | |
| -	NMBM_LOG_INFO,
 | |
| -	NMBM_LOG_WARN,
 | |
| -	NMBM_LOG_ERR,
 | |
| -	NMBM_LOG_EMERG,
 | |
| -
 | |
| -	__NMBM_LOG_MAX
 | |
| -};
 | |
| -
 | |
| -enum nmbm_oob_mode {
 | |
| -	NMBM_MODE_PLACE_OOB,
 | |
| -	NMBM_MODE_AUTO_OOB,
 | |
| -	NMBM_MODE_RAW,
 | |
| -
 | |
| -	__NMBM_MODE_MAX
 | |
| -};
 | |
| -
 | |
| -struct nmbm_lower_device {
 | |
| -	uint32_t max_ratio;
 | |
| -	uint32_t max_reserved_blocks;
 | |
| -	int flags;
 | |
| -
 | |
| -	uint64_t size;
 | |
| -	uint32_t erasesize;
 | |
| -	uint32_t writesize;
 | |
| -	uint32_t oobsize;
 | |
| -	uint32_t oobavail;
 | |
| -
 | |
| -	void *arg;
 | |
| -	int (*reset_chip)(void *arg);
 | |
| -
 | |
| -	/*
 | |
| -	 * read_page:
 | |
| -	 *    return 0 if succeeds
 | |
| -	 *    return positive number for ecc error
 | |
| -	 *    return negative number for other errors
 | |
| -	 */
 | |
| -	int (*read_page)(void *arg, uint64_t addr, void *buf, void *oob, enum nmbm_oob_mode mode);
 | |
| -	int (*write_page)(void *arg, uint64_t addr, const void *buf, const void *oob, enum nmbm_oob_mode mode);
 | |
| -	int (*erase_block)(void *arg, uint64_t addr);
 | |
| -
 | |
| -	int (*is_bad_block)(void *arg, uint64_t addr);
 | |
| -	int (*mark_bad_block)(void *arg, uint64_t addr);
 | |
| -
 | |
| -	/* OS-dependent logging function */
 | |
| -	void (*logprint)(void *arg, enum nmbm_log_category level, const char *fmt, va_list ap);
 | |
| -};
 | |
| -
 | |
| -struct nmbm_instance;
 | |
| -
 | |
| -/* Create NMBM if management area not found, or not complete */
 | |
| -#define NMBM_F_CREATE			0x01
 | |
| -
 | |
| -/* Empty page is also protected by ECC, and bitflip(s) can be corrected */
 | |
| -#define NMBM_F_EMPTY_PAGE_ECC_OK	0x02
 | |
| -
 | |
| -/* Do not write anything back to flash */
 | |
| -#define NMBM_F_READ_ONLY		0x04
 | |
| -
 | |
| -size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld);
 | |
| -int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni);
 | |
| -int nmbm_detach(struct nmbm_instance *ni);
 | |
| -
 | |
| -enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni,
 | |
| -					  enum nmbm_log_category level);
 | |
| -
 | |
| -int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr,
 | |
| -			   uint64_t size, uint64_t *failed_addr);
 | |
| -int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data,
 | |
| -			  void *oob, enum nmbm_oob_mode mode);
 | |
| -int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
 | |
| -		    void *data, enum nmbm_oob_mode mode, size_t *retlen);
 | |
| -int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr,
 | |
| -			   const void *data, const void *oob,
 | |
| -			   enum nmbm_oob_mode mode);
 | |
| -int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
 | |
| -		     const void *data, enum nmbm_oob_mode mode,
 | |
| -		     size_t *retlen);
 | |
| -
 | |
| -int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr);
 | |
| -int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr);
 | |
| -
 | |
| -uint64_t nmbm_get_avail_size(struct nmbm_instance *ni);
 | |
| -
 | |
| -int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld);
 | |
| -
 | |
| -#endif /* _NMBM_H_ */
 | |
| diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk
 | |
| index 57c3efcf2b..81ef6bbd14 100644
 | |
| --- a/target/linux/ramips/image/mt7621.mk
 | |
| +++ b/target/linux/ramips/image/mt7621.mk
 | |
| @@ -111,11 +111,20 @@ define Device/dsa-migration
 | |
|  endef
 | |
|  
 | |
|  define Device/actiontec_web7200
 | |
| +  $(Device/dsa-migration)
 | |
|    DEVICE_VENDOR := Actiontec
 | |
|    DEVICE_MODEL := EB7200
 | |
|    DEVICE_PACKAGES += kmod-mt7603 kmod-mt7915e kmod-usb3 uboot-envtools kmod-i2c-core
 | |
| +  LOADER_TYPE := bin
 | |
| +  KERNEL_SIZE := 4096k
 | |
| +  BLOCKSIZE := 128k
 | |
| +  PAGESIZE := 2048
 | |
| +  UBINIZE_OPTS := -E 5
 | |
| +  KERNEL_INITRAMFS := kernel-bin | lzma | loader-kernel | gzip | fit-relocate gzip $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb
 | |
|    KERNEL := kernel-bin | relocate-kernel | lzma | fit-relocate lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb
 | |
| -  IMAGE_SIZE := 15552k
 | |
| +  IMAGES += factory.bin
 | |
| +  IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
 | |
| +  IMAGE/factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi
 | |
|  endef
 | |
|  TARGET_DEVICES += actiontec_web7200
 | |
|  
 | |
| @@ -1404,6 +1413,15 @@ define Device/ubnt_unifi-6-lite
 | |
|  endef
 | |
|  TARGET_DEVICES += ubnt_unifi-6-lite
 | |
|  
 | |
| +define Device/ubnt_unifi-nanohd
 | |
| +  $(Device/dsa-migration)
 | |
| +  DEVICE_VENDOR := Ubiquiti
 | |
| +  DEVICE_MODEL := UniFi nanoHD
 | |
| +  DEVICE_PACKAGES += kmod-mt7603 kmod-mt7615e kmod-mt7615-firmware
 | |
| +  IMAGE_SIZE := 15552k
 | |
| +endef
 | |
| +TARGET_DEVICES += ubnt_unifi-nanohd
 | |
| +
 | |
|  define Device/unielec_u7621-01-16m
 | |
|    $(Device/dsa-migration)
 | |
|    $(Device/uimage-lzma-loader)
 | |
| diff --git a/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/10_fix_wifi_mac b/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/10_fix_wifi_mac
 | |
| index bd0f0254c6..7109a4647e 100644
 | |
| --- a/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/10_fix_wifi_mac
 | |
| +++ b/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/10_fix_wifi_mac
 | |
| @@ -10,6 +10,11 @@ PHYNBR=${DEVPATH##*/phy}
 | |
|  board=$(board_name)
 | |
|  
 | |
|  case "$board" in
 | |
| +	actiontec,web7200)
 | |
| +		wan_mac=$(mtd_get_mac_ascii u-boot-env "AeiBaseMACAddr")
 | |
| +		[ "$PHYNBR" = "0" ] && macaddr_add $wan_mac 2 > /sys${DEVPATH}/macaddress
 | |
| +		[ "$PHYNBR" = "1" ] && macaddr_add $wan_mac 3 > /sys${DEVPATH}/macaddress
 | |
| +		;;
 | |
|  	glinet,gl-mt1300)
 | |
|  		[ "$PHYNBR" = "1" ] && \
 | |
|  			macaddr_add "$(mtd_get_mac_binary factory 0x4)" 1 > /sys${DEVPATH}/macaddress
 | |
| diff --git a/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh b/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh
 | |
| index 924f259e25..93f6e836dc 100755
 | |
| --- a/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh
 | |
| +++ b/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh
 | |
| @@ -48,16 +48,19 @@ platform_do_upgrade() {
 | |
|  		;;
 | |
|  	actiontec,web7200)
 | |
|  		if grep -q bootselect=0 /proc/cmdline; then
 | |
| -			PART_NAME=firmware2
 | |
| +			CI_KERNPART=firmware2
 | |
| +			CI_UBIPART=ubi_2
 | |
|  			fw_setenv bootselect 1 || exit 1
 | |
|  		else
 | |
| -			PART_NAME=firmware1
 | |
| +			CI_KERNPART=firmware
 | |
| +			CI_UBIPART=ubi_1
 | |
|  			fw_setenv bootselect 0 || exit 1
 | |
|  		fi
 | |
|  		;;
 | |
|  	esac
 | |
|  
 | |
|  	case "$board" in
 | |
| +	actiontec,web7200|\
 | |
|  	ampedwireless,ally-00x19k|\
 | |
|  	ampedwireless,ally-r1900k|\
 | |
|  	asus,rt-ac65p|\
 | |
| diff --git a/target/linux/ramips/mt7621/config-5.4 b/target/linux/ramips/mt7621/config-5.4
 | |
| index ae4a0679bd..eada0ff602 100644
 | |
| --- a/target/linux/ramips/mt7621/config-5.4
 | |
| +++ b/target/linux/ramips/mt7621/config-5.4
 | |
| @@ -185,14 +185,6 @@ CONFIG_NET_MEDIATEK_SOC=y
 | |
|  CONFIG_NET_SWITCHDEV=y
 | |
|  CONFIG_NET_VENDOR_MEDIATEK=y
 | |
|  # CONFIG_NET_VENDOR_RALINK is not set
 | |
| -CONFIG_NMBM=y
 | |
| -# CONFIG_NMBM_LOG_LEVEL_DEBUG is not set
 | |
| -# CONFIG_NMBM_LOG_LEVEL_EMERG is not set
 | |
| -# CONFIG_NMBM_LOG_LEVEL_ERR is not set
 | |
| -CONFIG_NMBM_LOG_LEVEL_INFO=y
 | |
| -# CONFIG_NMBM_LOG_LEVEL_NONE is not set
 | |
| -# CONFIG_NMBM_LOG_LEVEL_WARN is not set
 | |
| -CONFIG_NMBM_MTD=y
 | |
|  CONFIG_NR_CPUS=4
 | |
|  CONFIG_OF=y
 | |
|  CONFIG_OF_ADDRESS=y
 | |
| diff --git a/target/linux/ramips/patches-5.4/499-mtd-add-nmbm-support.patch b/target/linux/ramips/patches-5.4/499-mtd-add-nmbm-support.patch
 | |
| deleted file mode 100644
 | |
| index 5cbaae2b7b..0000000000
 | |
| --- a/target/linux/ramips/patches-5.4/499-mtd-add-nmbm-support.patch
 | |
| +++ /dev/null
 | |
| @@ -1,21 +0,0 @@
 | |
| ---- a/drivers/mtd/Kconfig
 | |
| -+++ b/drivers/mtd/Kconfig
 | |
| -@@ -228,6 +228,8 @@ source "drivers/mtd/ubi/Kconfig"
 | |
| - 
 | |
| - source "drivers/mtd/hyperbus/Kconfig"
 | |
| - 
 | |
| -+source "drivers/mtd/nmbm/Kconfig"
 | |
| -+
 | |
| - source "drivers/mtd/composite/Kconfig"
 | |
| - 
 | |
| - endif # MTD
 | |
| ---- a/drivers/mtd/Makefile
 | |
| -+++ b/drivers/mtd/Makefile
 | |
| -@@ -33,5 +33,7 @@ obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor/
 | |
| - obj-$(CONFIG_MTD_UBI)		+= ubi/
 | |
| - obj-$(CONFIG_MTD_HYPERBUS)	+= hyperbus/
 | |
| - 
 | |
| -+obj-y				+= nmbm/
 | |
| -+
 | |
| - # Composite drivers must be loaded last
 | |
| - obj-y				+= composite/
 | |
| -- 
 | |
| 2.25.1
 | |
| 
 | 
