diff --git a/include/kernel-5.10 b/include/kernel-5.10 new file mode 100644 index 0000000000..e816698dec --- /dev/null +++ b/include/kernel-5.10 @@ -0,0 +1,2 @@ +LINUX_VERSION-5.10 = .90 +LINUX_KERNEL_HASH-5.10.90 = 945e4264c014a3d9dfc0a4639309dd1ec2fb545416556421f931b95da78c2725 diff --git a/include/kernel-5.4 b/include/kernel-5.4 new file mode 100644 index 0000000000..e85b7ad68e --- /dev/null +++ b/include/kernel-5.4 @@ -0,0 +1,2 @@ +LINUX_VERSION-5.4 = .170 +LINUX_KERNEL_HASH-5.4.170 = b09f74e0cf5fc7cf5de6aa932fe654c962cb10118bdbbdddb397022c6e6d382c diff --git a/include/kernel-version.mk b/include/kernel-version.mk index 677693ad08..1edb01df27 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -6,11 +6,12 @@ ifdef CONFIG_TESTING_KERNEL KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER) endif -LINUX_VERSION-5.4 = .170 -LINUX_VERSION-5.10 = .90 +KERNEL_DETAILS_FILE=$(INCLUDE_DIR)/kernel-$(KERNEL_PATCHVER) +ifeq ($(wildcard $(KERNEL_DETAILS_FILE)),) + $(error Missing kernel version/hash file for $(KERNEL_PATCHVER). Please create $(KERNEL_DETAILS_FILE)) +endif -LINUX_KERNEL_HASH-5.4.170 = b09f74e0cf5fc7cf5de6aa932fe654c962cb10118bdbbdddb397022c6e6d382c -LINUX_KERNEL_HASH-5.10.90 = 945e4264c014a3d9dfc0a4639309dd1ec2fb545416556421f931b95da78c2725 +include $(KERNEL_DETAILS_FILE) remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1)))) sanitize_uri=$(call qstrip,$(subst @,_,$(subst :,_,$(subst .,_,$(subst -,_,$(subst /,_,$(1))))))) diff --git a/package/network/config/netifd/Makefile b/package/network/config/netifd/Makefile index 10bb115041..0969c7718f 100644 --- a/package/network/config/netifd/Makefile +++ b/package/network/config/netifd/Makefile @@ -5,9 +5,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/netifd.git -PKG_SOURCE_DATE:=2021-12-02 -PKG_SOURCE_VERSION:=5ca5e0b4d058a47d72ba4102acdcec826e203c41 -PKG_MIRROR_HASH:=8a1fd6b634a0390a5ee512483c924b3ea8fa3ea9fa863d0434e3e66949faccab +PKG_SOURCE_DATE:=2022-01-14 +PKG_SOURCE_VERSION:=3043206e94da412eb19dd72ea68edcaca545d84c +PKG_MIRROR_HASH:=2bf5a59e93968b00f69b8b7ebbdfd28353c36bc5e7f72225d725c24d0ac7265f PKG_MAINTAINER:=Felix Fietkau PKG_LICENSE:=GPL-2.0 diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c index bcb80f4a33..3c016124f9 100644 --- a/package/network/services/hostapd/src/src/ap/ubus.c +++ b/package/network/services/hostapd/src/src/ap/ubus.c @@ -411,9 +411,15 @@ hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj, char ssid[SSID_MAX_LEN + 1]; char phy_name[17]; size_t ssid_len = SSID_MAX_LEN; + u8 channel = 0, op_class = 0; if (hapd->conf->ssid.ssid_len < SSID_MAX_LEN) ssid_len = hapd->conf->ssid.ssid_len; + + ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hostapd_get_oper_chwidth(hapd->iconf), + &op_class, &channel); blob_buf_init(&b, 0); blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state)); @@ -424,7 +430,8 @@ hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj, blobmsg_add_string(&b, "ssid", ssid); blobmsg_add_u32(&b, "freq", hapd->iface->freq); - blobmsg_add_u32(&b, "channel", ieee80211_frequency_to_channel(hapd->iface->freq)); + blobmsg_add_u32(&b, "channel", channel); + blobmsg_add_u32(&b, "op_class", op_class); blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int); snprintf(phy_name, 17, "%s", hapd->iface->phy); @@ -1930,4 +1937,4 @@ int hostapd_ubus_notify_bss_transition_query( return ureq.resp; #endif -} \ No newline at end of file +} diff --git a/package/network/utils/iptables/Makefile b/package/network/utils/iptables/Makefile index 42d9af14b0..6aee5c6caf 100644 --- a/package/network/utils/iptables/Makefile +++ b/package/network/utils/iptables/Makefile @@ -10,7 +10,7 @@ include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=iptables PKG_VERSION:=1.8.7 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE_URL:=https://netfilter.org/projects/iptables/files PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 @@ -60,7 +60,7 @@ define Package/iptables/config config IPTABLES_NFTABLES bool "Enable Nftables support" - default n + default y help This enable nftables support in iptables. endef diff --git a/package/system/procd/Makefile b/package/system/procd/Makefile index 1a4d2c7096..27b235cea7 100644 --- a/package/system/procd/Makefile +++ b/package/system/procd/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/procd.git -PKG_MIRROR_HASH:=3ce6f5e2c12ae276af3ffe1755a495415f77184974206a44a3b083420aba52a8 -PKG_SOURCE_DATE:=2021-12-20 -PKG_SOURCE_VERSION:=129d050b9f5725ea4c54e1d906aba43eca95b860 +PKG_MIRROR_HASH:=0e506062a992f77979bd59eb51fb5d500e45197cc7bfba3a9302415d754dbfd1 +PKG_SOURCE_DATE:=2022-01-11 +PKG_SOURCE_VERSION:=ac2b8b365bdbcbf292f77409d180ec3c0963faf3 CMAKE_INSTALL:=1 PKG_LICENSE:=GPL-2.0 diff --git a/target/linux/ath79/dts/ar9342_mikrotik_routerboard-912uag-2hpnd.dts b/target/linux/ath79/dts/ar9342_mikrotik_routerboard-912uag-2hpnd.dts index ee2c12b4e0..77a0d29113 100644 --- a/target/linux/ath79/dts/ar9342_mikrotik_routerboard-912uag-2hpnd.dts +++ b/target/linux/ath79/dts/ar9342_mikrotik_routerboard-912uag-2hpnd.dts @@ -127,6 +127,10 @@ }; }; +&ref { + clock-frequency = <25000000>; +}; + &spi { status = "okay"; @@ -194,7 +198,6 @@ gmac-config { device = <&gmac>; rgmii-gmac0 = <1>; - rgmii-enabled = <1>; rxd-delay = <1>; txd-delay = <1>; }; diff --git a/target/linux/ath79/dts/ar9344_mikrotik_routerboard-sxt-5n.dtsi b/target/linux/ath79/dts/ar9344_mikrotik_routerboard-sxt-5n.dtsi index 3ed50abefa..86136289de 100644 --- a/target/linux/ath79/dts/ar9344_mikrotik_routerboard-sxt-5n.dtsi +++ b/target/linux/ath79/dts/ar9344_mikrotik_routerboard-sxt-5n.dtsi @@ -99,7 +99,6 @@ partition@0 { label = "RouterBoot"; reg = <0x0 0x20000>; - read-only; compatible = "mikrotik,routerboot-partitions"; #address-cells = <1>; #size-cells = <1>; diff --git a/target/linux/ath79/dts/qca9533_mikrotik_routerboard-16m.dtsi b/target/linux/ath79/dts/qca9533_mikrotik_routerboard-16m.dtsi index f0473c7497..dbbe67d33d 100644 --- a/target/linux/ath79/dts/qca9533_mikrotik_routerboard-16m.dtsi +++ b/target/linux/ath79/dts/qca9533_mikrotik_routerboard-16m.dtsi @@ -38,7 +38,6 @@ partition@0 { label = "RouterBoot"; reg = <0x0 0x20000>; - read-only; compatible = "mikrotik,routerboot-partitions"; #address-cells = <1>; #size-cells = <1>; diff --git a/target/linux/ath79/dts/qca9556_mikrotik_routerboard-wap-g-5hact2hnd.dts b/target/linux/ath79/dts/qca9556_mikrotik_routerboard-wap-g-5hact2hnd.dts index f5c6731bb7..f6e04ae0a4 100644 --- a/target/linux/ath79/dts/qca9556_mikrotik_routerboard-wap-g-5hact2hnd.dts +++ b/target/linux/ath79/dts/qca9556_mikrotik_routerboard-wap-g-5hact2hnd.dts @@ -73,7 +73,6 @@ partition@0 { label = "RouterBoot"; reg = <0x0 0x20000>; - read-only; compatible = "mikrotik,routerboot-partitions"; #address-cells = <1>; #size-cells = <1>; diff --git a/target/linux/ath79/dts/qca955x.dtsi b/target/linux/ath79/dts/qca955x.dtsi index fb820169d5..b6e08f9f12 100644 --- a/target/linux/ath79/dts/qca955x.dtsi +++ b/target/linux/ath79/dts/qca955x.dtsi @@ -112,8 +112,7 @@ }; pll: pll-controller@18050000 { - compatible = "qca,qca9550-pll", - "qca,qca9550-pll", "syscon"; + compatible = "qca,qca9550-pll", "syscon"; reg = <0x18050000 0x50>; #clock-cells = <1>; diff --git a/target/linux/ath79/mikrotik/config-default b/target/linux/ath79/mikrotik/config-default index 74cfdf0423..ee2e4ebe9a 100644 --- a/target/linux/ath79/mikrotik/config-default +++ b/target/linux/ath79/mikrotik/config-default @@ -19,6 +19,7 @@ CONFIG_MTD_NAND_RB91X=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_ROUTERBOOT_PARTS=y CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y CONFIG_MTD_SPLIT_MINOR_FW=y CONFIG_MTD_UBI=y CONFIG_MTD_UBI_BLOCK=y diff --git a/target/linux/generic/config-5.10 b/target/linux/generic/config-5.10 index f9e14d94e7..1eade30672 100644 --- a/target/linux/generic/config-5.10 +++ b/target/linux/generic/config-5.10 @@ -3661,6 +3661,7 @@ CONFIG_MTD_ROOTFS_ROOT_DEV=y # CONFIG_MTD_SPI_NOR is not set # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set CONFIG_MTD_SPI_NOR_USE_4K_SECTORS_LIMIT=4096 +# CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE is not set CONFIG_MTD_SPLIT=y # CONFIG_MTD_SPLIT_BCM63XX_FW is not set # CONFIG_MTD_SPLIT_BCM_WFI_FW is not set @@ -3688,6 +3689,7 @@ CONFIG_MTD_SPLIT_SUPPORT=y # CONFIG_MTD_UBI_GLUEBI is not set # CONFIG_MTD_UIMAGE_SPLIT is not set # CONFIG_MTD_VIRT_CONCAT is not set +# CONFIG_MTD_NAND_MTK_BMT is not set # CONFIG_MTK_MMC is not set # CONFIG_MTK_MMSYS is not set CONFIG_MULTIUSER=y diff --git a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c new file mode 100644 index 0000000000..7c06ee01f0 --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c @@ -0,0 +1,1201 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Xiangsheng Hou + * Copyright (c) 2020 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAIN_SIGNATURE_OFFSET 0 +#define OOB_SIGNATURE_OFFSET 1 +#define BBPOOL_RATIO 2 + +#define BBT_LOG(fmt, ...) pr_debug("[BBT][%s|%d] "fmt"\n", __func__, __LINE__, ##__VA_ARGS__) + +/* Maximum 8k blocks */ +#define BB_TABLE_MAX bmtd.table_size +#define BMT_TABLE_MAX (BB_TABLE_MAX * BBPOOL_RATIO / 100) +#define BMT_TBL_DEF_VAL 0x0 + +struct mtk_bmt_ops { + char *sig; + unsigned int sig_len; + int (*init)(struct device_node *np); + bool (*remap_block)(u16 block, u16 mapped_block, int copy_len); + void (*unmap_block)(u16 block); + u16 (*get_mapping_block)(int block); + int (*debug)(void *data, u64 val); +}; + +struct bbbt { + char signature[3]; + /* This version is used to distinguish the legacy and new algorithm */ +#define BBMT_VERSION 2 + unsigned char version; + /* Below 2 tables will be written in SLC */ + u16 bb_tbl[]; +}; + +struct bbmt { + u16 block; +#define NO_MAPPED 0 +#define NORMAL_MAPPED 1 +#define BMT_MAPPED 2 + u16 mapped; +}; + +static struct bmt_desc { + struct mtd_info *mtd; + + int (*_read_oob) (struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + int (*_write_oob) (struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + int (*_erase) (struct mtd_info *mtd, struct erase_info *instr); + int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs); + int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); + + const struct mtk_bmt_ops *ops; + + struct bbbt *bbt; + + struct dentry *debugfs_dir; + + u32 table_size; + u32 pg_size; + u32 blk_size; + u16 pg_shift; + u16 blk_shift; + /* bbt logical address */ + u16 pool_lba; + /* bbt physical address */ + u16 pool_pba; + /* Maximum count of bad blocks that the vendor guaranteed */ + u16 bb_max; + /* Total blocks of the Nand Chip */ + u16 total_blks; + /* The block(n) BMT is located at (bmt_tbl[n]) */ + u16 bmt_blk_idx; + /* How many pages needs to store 'struct bbbt' */ + u32 bmt_pgs; + + const __be32 *remap_range; + int remap_range_len; + + /* to compensate for driver level remapping */ + u8 oob_offset; +} bmtd = {0}; + +static unsigned char *nand_bbt_buf; +static unsigned char *nand_data_buf; + +/* -------- Unit conversions -------- */ +static inline u32 blk_pg(u16 block) +{ + return (u32)(block << (bmtd.blk_shift - bmtd.pg_shift)); +} + +/* -------- Nand operations wrapper -------- */ +static inline int +bbt_nand_read(u32 page, unsigned char *dat, int dat_len, + unsigned char *fdm, int fdm_len) +{ + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .ooboffs = bmtd.oob_offset, + .oobbuf = fdm, + .ooblen = fdm_len, + .datbuf = dat, + .len = dat_len, + }; + + return bmtd._read_oob(bmtd.mtd, page << bmtd.pg_shift, &ops); +} + +static inline int bbt_nand_erase(u16 block) +{ + struct mtd_info *mtd = bmtd.mtd; + struct erase_info instr = { + .addr = (loff_t)block << bmtd.blk_shift, + .len = bmtd.blk_size, + }; + + return bmtd._erase(mtd, &instr); +} + +static inline int bbt_nand_copy(u16 dest_blk, u16 src_blk, loff_t max_offset) +{ + int pages = bmtd.blk_size >> bmtd.pg_shift; + loff_t src = (loff_t)src_blk << bmtd.blk_shift; + loff_t dest = (loff_t)dest_blk << bmtd.blk_shift; + loff_t offset = 0; + uint8_t oob[64]; + int i, ret; + + for (i = 0; i < pages; i++) { + struct mtd_oob_ops rd_ops = { + .mode = MTD_OPS_PLACE_OOB, + .oobbuf = oob, + .ooblen = min_t(int, bmtd.mtd->oobsize / pages, sizeof(oob)), + .datbuf = nand_data_buf, + .len = bmtd.pg_size, + }; + struct mtd_oob_ops wr_ops = { + .mode = MTD_OPS_PLACE_OOB, + .oobbuf = oob, + .datbuf = nand_data_buf, + .len = bmtd.pg_size, + }; + + if (offset >= max_offset) + break; + + ret = bmtd._read_oob(bmtd.mtd, src + offset, &rd_ops); + if (ret < 0 && !mtd_is_bitflip(ret)) + return ret; + + if (!rd_ops.retlen) + break; + + ret = bmtd._write_oob(bmtd.mtd, dest + offset, &wr_ops); + if (ret < 0) + return ret; + + wr_ops.ooblen = rd_ops.oobretlen; + offset += rd_ops.retlen; + } + + return 0; +} + +/* -------- Bad Blocks Management -------- */ +static inline struct bbmt *bmt_tbl(struct bbbt *bbbt) +{ + return (struct bbmt *)&bbbt->bb_tbl[bmtd.table_size]; +} + +static int +read_bmt(u16 block, unsigned char *dat, unsigned char *fdm, int fdm_len) +{ + u32 len = bmtd.bmt_pgs << bmtd.pg_shift; + + return bbt_nand_read(blk_pg(block), dat, len, fdm, fdm_len); +} + +static int write_bmt(u16 block, unsigned char *dat) +{ + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .ooboffs = OOB_SIGNATURE_OFFSET + bmtd.oob_offset, + .oobbuf = bmtd.ops->sig, + .ooblen = bmtd.ops->sig_len, + .datbuf = dat, + .len = bmtd.bmt_pgs << bmtd.pg_shift, + }; + loff_t addr = (loff_t)block << bmtd.blk_shift; + + return bmtd._write_oob(bmtd.mtd, addr, &ops); +} + +static u16 find_valid_block(u16 block) +{ + u8 fdm[4]; + int ret; + int loop = 0; + +retry: + if (block >= bmtd.total_blks) + return 0; + + ret = bbt_nand_read(blk_pg(block), nand_data_buf, bmtd.pg_size, + fdm, sizeof(fdm)); + /* Read the 1st byte of FDM to judge whether it's a bad + * or not + */ + if (ret || fdm[0] != 0xff) { + pr_info("nand: found bad block 0x%x\n", block); + if (loop >= bmtd.bb_max) { + pr_info("nand: FATAL ERR: too many bad blocks!!\n"); + return 0; + } + + loop++; + block++; + goto retry; + } + + return block; +} + +/* Find out all bad blocks, and fill in the mapping table */ +static int scan_bad_blocks(struct bbbt *bbt) +{ + int i; + u16 block = 0; + + /* First time download, the block0 MUST NOT be a bad block, + * this is guaranteed by vendor + */ + bbt->bb_tbl[0] = 0; + + /* + * Construct the mapping table of Normal data area(non-PMT/BMTPOOL) + * G - Good block; B - Bad block + * --------------------------- + * physical |G|G|B|G|B|B|G|G|G|G|B|G|B| + * --------------------------- + * What bb_tbl[i] looks like: + * physical block(i): + * 0 1 2 3 4 5 6 7 8 9 a b c + * mapped block(bb_tbl[i]): + * 0 1 3 6 7 8 9 b ...... + * ATTENTION: + * If new bad block ocurred(n), search bmt_tbl to find + * a available block(x), and fill in the bb_tbl[n] = x; + */ + for (i = 1; i < bmtd.pool_lba; i++) { + bbt->bb_tbl[i] = find_valid_block(bbt->bb_tbl[i - 1] + 1); + BBT_LOG("bb_tbl[0x%x] = 0x%x", i, bbt->bb_tbl[i]); + if (bbt->bb_tbl[i] == 0) + return -1; + } + + /* Physical Block start Address of BMT pool */ + bmtd.pool_pba = bbt->bb_tbl[i - 1] + 1; + if (bmtd.pool_pba >= bmtd.total_blks - 2) { + pr_info("nand: FATAL ERR: Too many bad blocks!!\n"); + return -1; + } + + BBT_LOG("pool_pba=0x%x", bmtd.pool_pba); + i = 0; + block = bmtd.pool_pba; + /* + * The bmt table is used for runtime bad block mapping + * G - Good block; B - Bad block + * --------------------------- + * physical |G|G|B|G|B|B|G|G|G|G|B|G|B| + * --------------------------- + * block: 0 1 2 3 4 5 6 7 8 9 a b c + * What bmt_tbl[i] looks like in initial state: + * i: + * 0 1 2 3 4 5 6 7 + * bmt_tbl[i].block: + * 0 1 3 6 7 8 9 b + * bmt_tbl[i].mapped: + * N N N N N N N B + * N - Not mapped(Available) + * M - Mapped + * B - BMT + * ATTENTION: + * BMT always in the last valid block in pool + */ + while ((block = find_valid_block(block)) != 0) { + bmt_tbl(bbt)[i].block = block; + bmt_tbl(bbt)[i].mapped = NO_MAPPED; + BBT_LOG("bmt_tbl[%d].block = 0x%x", i, block); + block++; + i++; + } + + /* i - How many available blocks in pool, which is the length of bmt_tbl[] + * bmtd.bmt_blk_idx - bmt_tbl[bmtd.bmt_blk_idx].block => the BMT block + */ + bmtd.bmt_blk_idx = i - 1; + bmt_tbl(bbt)[bmtd.bmt_blk_idx].mapped = BMT_MAPPED; + + if (i < 1) { + pr_info("nand: FATAL ERR: no space to store BMT!!\n"); + return -1; + } + + pr_info("[BBT] %d available blocks in BMT pool\n", i); + + return 0; +} + +static bool is_valid_bmt(unsigned char *buf, unsigned char *fdm) +{ + struct bbbt *bbt = (struct bbbt *)buf; + u8 *sig = (u8*)bbt->signature + MAIN_SIGNATURE_OFFSET; + + + if (memcmp(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3) == 0 && + memcmp(fdm + OOB_SIGNATURE_OFFSET, "bmt", 3) == 0) { + if (bbt->version == BBMT_VERSION) + return true; + } + BBT_LOG("[BBT] BMT Version not match,upgrage preloader and uboot please! sig=%02x%02x%02x, fdm=%02x%02x%02x", + sig[0], sig[1], sig[2], + fdm[1], fdm[2], fdm[3]); + return false; +} + +static u16 get_bmt_index(struct bbmt *bmt) +{ + int i = 0; + + while (bmt[i].block != BMT_TBL_DEF_VAL) { + if (bmt[i].mapped == BMT_MAPPED) + return i; + i++; + } + return 0; +} + +static struct bbbt *scan_bmt(u16 block) +{ + u8 fdm[4]; + + if (block < bmtd.pool_lba) + return NULL; + + if (read_bmt(block, nand_bbt_buf, fdm, sizeof(fdm))) + return scan_bmt(block - 1); + + if (is_valid_bmt(nand_bbt_buf, fdm)) { + bmtd.bmt_blk_idx = get_bmt_index(bmt_tbl((struct bbbt *)nand_bbt_buf)); + if (bmtd.bmt_blk_idx == 0) { + pr_info("[BBT] FATAL ERR: bmt block index is wrong!\n"); + return NULL; + } + pr_info("[BBT] BMT.v2 is found at 0x%x\n", block); + return (struct bbbt *)nand_bbt_buf; + } else + return scan_bmt(block - 1); +} + +/* Write the Burner Bad Block Table to Nand Flash + * n - write BMT to bmt_tbl[n] + */ +static u16 upload_bmt(struct bbbt *bbt, int n) +{ + u16 block; + +retry: + if (n < 0 || bmt_tbl(bbt)[n].mapped == NORMAL_MAPPED) { + pr_info("nand: FATAL ERR: no space to store BMT!\n"); + return (u16)-1; + } + + block = bmt_tbl(bbt)[n].block; + BBT_LOG("n = 0x%x, block = 0x%x", n, block); + if (bbt_nand_erase(block)) { + bmt_tbl(bbt)[n].block = 0; + /* erase failed, try the previous block: bmt_tbl[n - 1].block */ + n--; + goto retry; + } + + /* The signature offset is fixed set to 0, + * oob signature offset is fixed set to 1 + */ + memcpy(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3); + bbt->version = BBMT_VERSION; + + if (write_bmt(block, (unsigned char *)bbt)) { + bmt_tbl(bbt)[n].block = 0; + + /* write failed, try the previous block in bmt_tbl[n - 1] */ + n--; + goto retry; + } + + /* Return the current index(n) of BMT pool (bmt_tbl[n]) */ + return n; +} + +static u16 find_valid_block_in_pool(struct bbbt *bbt) +{ + int i; + + if (bmtd.bmt_blk_idx == 0) + goto error; + + for (i = 0; i < bmtd.bmt_blk_idx; i++) { + if (bmt_tbl(bbt)[i].block != 0 && bmt_tbl(bbt)[i].mapped == NO_MAPPED) { + bmt_tbl(bbt)[i].mapped = NORMAL_MAPPED; + return bmt_tbl(bbt)[i].block; + } + } + +error: + pr_info("nand: FATAL ERR: BMT pool is run out!\n"); + return 0; +} + +/* We met a bad block, mark it as bad and map it to a valid block in pool, + * if it's a write failure, we need to write the data to mapped block + */ +static bool remap_block_v2(u16 block, u16 mapped_block, int copy_len) +{ + u16 mapped_blk; + struct bbbt *bbt; + + bbt = bmtd.bbt; + mapped_blk = find_valid_block_in_pool(bbt); + if (mapped_blk == 0) + return false; + + /* Map new bad block to available block in pool */ + bbt->bb_tbl[block] = mapped_blk; + + /* Erase new block */ + bbt_nand_erase(mapped_blk); + if (copy_len > 0) + bbt_nand_copy(mapped_blk, block, copy_len); + + bmtd.bmt_blk_idx = upload_bmt(bbt, bmtd.bmt_blk_idx); + + return true; +} + +static bool +mapping_block_in_range(int block, int *start, int *end) +{ + const __be32 *cur = bmtd.remap_range; + u32 addr = block << bmtd.blk_shift; + int i; + + if (!cur || !bmtd.remap_range_len) { + *start = 0; + *end = bmtd.total_blks; + return true; + } + + for (i = 0; i < bmtd.remap_range_len; i++, cur += 2) { + if (addr < be32_to_cpu(cur[0]) || addr >= be32_to_cpu(cur[1])) + continue; + + *start = be32_to_cpu(cur[0]); + *end = be32_to_cpu(cur[1]); + return true; + } + + return false; +} + +static u16 +get_mapping_block_index_v2(int block) +{ + int start, end; + + if (block >= bmtd.pool_lba) + return block; + + if (!mapping_block_in_range(block, &start, &end)) + return block; + + return bmtd.bbt->bb_tbl[block]; +} + +static int +mtk_bmt_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct mtd_oob_ops cur_ops = *ops; + int retry_count = 0; + loff_t cur_from; + int ret = 0; + int max_bitflips = 0; + int start, end; + + ops->retlen = 0; + ops->oobretlen = 0; + + while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) { + int cur_ret; + + u32 offset = from & (bmtd.blk_size - 1); + u32 block = from >> bmtd.blk_shift; + u32 cur_block; + + cur_block = bmtd.ops->get_mapping_block(block); + cur_from = ((loff_t)cur_block << bmtd.blk_shift) + offset; + + cur_ops.oobretlen = 0; + cur_ops.retlen = 0; + cur_ops.len = min_t(u32, mtd->erasesize - offset, + ops->len - ops->retlen); + cur_ret = bmtd._read_oob(mtd, cur_from, &cur_ops); + if (cur_ret < 0) + ret = cur_ret; + else + max_bitflips = max_t(int, max_bitflips, cur_ret); + if (cur_ret < 0 && !mtd_is_bitflip(cur_ret)) { + bmtd.ops->remap_block(block, cur_block, mtd->erasesize); + if (retry_count++ < 10) + continue; + + goto out; + } + + if (cur_ret >= mtd->bitflip_threshold && + mapping_block_in_range(block, &start, &end)) + bmtd.ops->remap_block(block, cur_block, mtd->erasesize); + + ops->retlen += cur_ops.retlen; + ops->oobretlen += cur_ops.oobretlen; + + cur_ops.ooboffs = 0; + cur_ops.datbuf += cur_ops.retlen; + cur_ops.oobbuf += cur_ops.oobretlen; + cur_ops.ooblen -= cur_ops.oobretlen; + + if (!cur_ops.len) + cur_ops.len = mtd->erasesize - offset; + + from += cur_ops.len; + retry_count = 0; + } + +out: + if (ret < 0) + return ret; + + return max_bitflips; +} + +static int +mtk_bmt_write(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct mtd_oob_ops cur_ops = *ops; + int retry_count = 0; + loff_t cur_to; + int ret; + + ops->retlen = 0; + ops->oobretlen = 0; + + while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) { + u32 offset = to & (bmtd.blk_size - 1); + u32 block = to >> bmtd.blk_shift; + u32 cur_block; + + cur_block = bmtd.ops->get_mapping_block(block); + cur_to = ((loff_t)cur_block << bmtd.blk_shift) + offset; + + cur_ops.oobretlen = 0; + cur_ops.retlen = 0; + cur_ops.len = min_t(u32, bmtd.blk_size - offset, + ops->len - ops->retlen); + ret = bmtd._write_oob(mtd, cur_to, &cur_ops); + if (ret < 0) { + bmtd.ops->remap_block(block, cur_block, offset); + if (retry_count++ < 10) + continue; + + return ret; + } + + ops->retlen += cur_ops.retlen; + ops->oobretlen += cur_ops.oobretlen; + + cur_ops.ooboffs = 0; + cur_ops.datbuf += cur_ops.retlen; + cur_ops.oobbuf += cur_ops.oobretlen; + cur_ops.ooblen -= cur_ops.oobretlen; + + if (!cur_ops.len) + cur_ops.len = mtd->erasesize - offset; + + to += cur_ops.len; + retry_count = 0; + } + + return 0; +} + +static int +mtk_bmt_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct erase_info mapped_instr = { + .len = bmtd.blk_size, + }; + int retry_count = 0; + u64 start_addr, end_addr; + int ret; + u16 orig_block, block; + + start_addr = instr->addr & (~mtd->erasesize_mask); + end_addr = instr->addr + instr->len; + + while (start_addr < end_addr) { + orig_block = start_addr >> bmtd.blk_shift; + block = bmtd.ops->get_mapping_block(orig_block); + mapped_instr.addr = (loff_t)block << bmtd.blk_shift; + ret = bmtd._erase(mtd, &mapped_instr); + if (ret) { + bmtd.ops->remap_block(orig_block, block, 0); + if (retry_count++ < 10) + continue; + instr->fail_addr = start_addr; + break; + } + start_addr += mtd->erasesize; + retry_count = 0; + } + + return ret; +} +static int +mtk_bmt_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + int retry_count = 0; + u16 orig_block = ofs >> bmtd.blk_shift; + u16 block; + int ret; + +retry: + block = bmtd.ops->get_mapping_block(orig_block); + ret = bmtd._block_isbad(mtd, (loff_t)block << bmtd.blk_shift); + if (ret) { + bmtd.ops->remap_block(orig_block, block, bmtd.blk_size); + if (retry_count++ < 10) + goto retry; + } + return ret; +} + +static int +mtk_bmt_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + u16 orig_block = ofs >> bmtd.blk_shift; + u16 block = bmtd.ops->get_mapping_block(orig_block); + + bmtd.ops->remap_block(orig_block, block, bmtd.blk_size); + + return bmtd._block_markbad(mtd, (loff_t)block << bmtd.blk_shift); +} + +static void +mtk_bmt_replace_ops(struct mtd_info *mtd) +{ + bmtd._read_oob = mtd->_read_oob; + bmtd._write_oob = mtd->_write_oob; + bmtd._erase = mtd->_erase; + bmtd._block_isbad = mtd->_block_isbad; + bmtd._block_markbad = mtd->_block_markbad; + + mtd->_read_oob = mtk_bmt_read; + mtd->_write_oob = mtk_bmt_write; + mtd->_erase = mtk_bmt_mtd_erase; + mtd->_block_isbad = mtk_bmt_block_isbad; + mtd->_block_markbad = mtk_bmt_block_markbad; +} + +static void +unmap_block_v2(u16 block) +{ + bmtd.bbt->bb_tbl[block] = block; + bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx); +} + +static int mtk_bmt_debug_mark_good(void *data, u64 val) +{ + bmtd.ops->unmap_block(val >> bmtd.blk_shift); + + return 0; +} + +static int mtk_bmt_debug_mark_bad(void *data, u64 val) +{ + u32 block = val >> bmtd.blk_shift; + u16 cur_block = bmtd.ops->get_mapping_block(block); + + bmtd.ops->remap_block(block, cur_block, bmtd.blk_size); + + return 0; +} + +static unsigned long * +mtk_bmt_get_mapping_mask(void) +{ + struct bbmt *bbmt = bmt_tbl(bmtd.bbt); + int main_blocks = bmtd.mtd->size >> bmtd.blk_shift; + unsigned long *used; + int i, k; + + used = kcalloc(sizeof(unsigned long), BIT_WORD(bmtd.bmt_blk_idx) + 1, GFP_KERNEL); + if (!used) + return NULL; + + for (i = 1; i < main_blocks; i++) { + if (bmtd.bbt->bb_tbl[i] == i) + continue; + + for (k = 0; k < bmtd.bmt_blk_idx; k++) { + if (bmtd.bbt->bb_tbl[i] != bbmt[k].block) + continue; + + set_bit(k, used); + break; + } + } + + return used; +} + +static int mtk_bmt_debug_v2(void *data, u64 val) +{ + struct bbmt *bbmt = bmt_tbl(bmtd.bbt); + struct mtd_info *mtd = bmtd.mtd; + unsigned long *used; + int main_blocks = mtd->size >> bmtd.blk_shift; + int n_remap = 0; + int i; + + used = mtk_bmt_get_mapping_mask(); + if (!used) + return -ENOMEM; + + switch (val) { + case 0: + for (i = 1; i < main_blocks; i++) { + if (bmtd.bbt->bb_tbl[i] == i) + continue; + + printk("remap [%x->%x]\n", i, bmtd.bbt->bb_tbl[i]); + n_remap++; + } + for (i = 0; i <= bmtd.bmt_blk_idx; i++) { + char c; + + switch (bbmt[i].mapped) { + case NO_MAPPED: + continue; + case NORMAL_MAPPED: + c = 'm'; + if (test_bit(i, used)) + c = 'M'; + break; + case BMT_MAPPED: + c = 'B'; + break; + default: + c = 'X'; + break; + } + printk("[%x:%c] = 0x%x\n", i, c, bbmt[i].block); + } + break; + case 100: + for (i = 0; i <= bmtd.bmt_blk_idx; i++) { + if (bbmt[i].mapped != NORMAL_MAPPED) + continue; + + if (test_bit(i, used)) + continue; + + n_remap++; + bbmt[i].mapped = NO_MAPPED; + printk("free block [%d:%x]\n", i, bbmt[i].block); + } + if (n_remap) + bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx); + break; + } + + kfree(used); + + return 0; +} + +static int mtk_bmt_debug(void *data, u64 val) +{ + return bmtd.ops->debug(data, val); +} + + +DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_good, NULL, mtk_bmt_debug_mark_good, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_bad, NULL, mtk_bmt_debug_mark_bad, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(fops_debug, NULL, mtk_bmt_debug, "%llu\n"); + +static void +mtk_bmt_add_debugfs(void) +{ + struct dentry *dir; + + dir = bmtd.debugfs_dir = debugfs_create_dir("mtk-bmt", NULL); + if (!dir) + return; + + debugfs_create_file_unsafe("mark_good", S_IWUSR, dir, NULL, &fops_mark_good); + debugfs_create_file_unsafe("mark_bad", S_IWUSR, dir, NULL, &fops_mark_bad); + debugfs_create_file_unsafe("debug", S_IWUSR, dir, NULL, &fops_debug); +} + +void mtk_bmt_detach(struct mtd_info *mtd) +{ + if (bmtd.mtd != mtd) + return; + + if (bmtd.debugfs_dir) + debugfs_remove_recursive(bmtd.debugfs_dir); + bmtd.debugfs_dir = NULL; + + kfree(nand_bbt_buf); + kfree(nand_data_buf); + + mtd->_read_oob = bmtd._read_oob; + mtd->_write_oob = bmtd._write_oob; + mtd->_erase = bmtd._erase; + mtd->_block_isbad = bmtd._block_isbad; + mtd->_block_markbad = bmtd._block_markbad; + mtd->size = bmtd.total_blks << bmtd.blk_shift; + + memset(&bmtd, 0, sizeof(bmtd)); +} + +static int mtk_bmt_init_v2(struct device_node *np) +{ + u32 bmt_pool_size, bmt_table_size; + u32 bufsz, block; + u16 pmt_block; + + if (of_property_read_u32(np, "mediatek,bmt-pool-size", + &bmt_pool_size) != 0) + bmt_pool_size = 80; + + if (of_property_read_u8(np, "mediatek,bmt-oob-offset", + &bmtd.oob_offset) != 0) + bmtd.oob_offset = 0; + + if (of_property_read_u32(np, "mediatek,bmt-table-size", + &bmt_table_size) != 0) + bmt_table_size = 0x2000U; + + bmtd.table_size = bmt_table_size; + + pmt_block = bmtd.total_blks - bmt_pool_size - 2; + + bmtd.mtd->size = pmt_block << bmtd.blk_shift; + + /* + * --------------------------------------- + * | PMT(2blks) | BMT POOL(totalblks * 2%) | + * --------------------------------------- + * ^ ^ + * | | + * pmt_block pmt_block + 2blocks(pool_lba) + * + * ATTETION!!!!!! + * The blocks ahead of the boundary block are stored in bb_tbl + * and blocks behind are stored in bmt_tbl + */ + + bmtd.pool_lba = (u16)(pmt_block + 2); + bmtd.bb_max = bmtd.total_blks * BBPOOL_RATIO / 100; + + bufsz = round_up(sizeof(struct bbbt) + + bmt_table_size * sizeof(struct bbmt), bmtd.pg_size); + bmtd.bmt_pgs = bufsz >> bmtd.pg_shift; + + nand_bbt_buf = kzalloc(bufsz, GFP_KERNEL); + if (!nand_bbt_buf) + return -ENOMEM; + + memset(nand_bbt_buf, 0xff, bufsz); + + /* Scanning start from the first page of the last block + * of whole flash + */ + bmtd.bbt = scan_bmt(bmtd.total_blks - 1); + if (!bmtd.bbt) { + /* BMT not found */ + if (bmtd.total_blks > BB_TABLE_MAX + BMT_TABLE_MAX) { + pr_info("nand: FATAL: Too many blocks, can not support!\n"); + return -1; + } + + bmtd.bbt = (struct bbbt *)nand_bbt_buf; + memset(bmt_tbl(bmtd.bbt), BMT_TBL_DEF_VAL, + bmtd.table_size * sizeof(struct bbmt)); + + if (scan_bad_blocks(bmtd.bbt)) + return -1; + + /* BMT always in the last valid block in pool */ + bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx); + block = bmt_tbl(bmtd.bbt)[bmtd.bmt_blk_idx].block; + pr_notice("[BBT] BMT.v2 is written into PBA:0x%x\n", block); + + if (bmtd.bmt_blk_idx == 0) + pr_info("nand: Warning: no available block in BMT pool!\n"); + else if (bmtd.bmt_blk_idx == (u16)-1) + return -1; + } + + return 0; +} + +static bool +bbt_block_is_bad(u16 block) +{ + u8 cur = nand_bbt_buf[block / 4]; + + return cur & (3 << ((block % 4) * 2)); +} + +static void +bbt_set_block_state(u16 block, bool bad) +{ + u8 mask = (3 << ((block % 4) * 2)); + + if (bad) + nand_bbt_buf[block / 4] |= mask; + else + nand_bbt_buf[block / 4] &= ~mask; + + bbt_nand_erase(bmtd.bmt_blk_idx); + write_bmt(bmtd.bmt_blk_idx, nand_bbt_buf); +} + +static u16 +get_mapping_block_index_bbt(int block) +{ + int start, end, ofs; + int bad_blocks = 0; + int i; + + if (!mapping_block_in_range(block, &start, &end)) + return block; + + start >>= bmtd.blk_shift; + end >>= bmtd.blk_shift; + /* skip bad blocks within the mapping range */ + ofs = block - start; + for (i = start; i < end; i++) { + if (bbt_block_is_bad(i)) + bad_blocks++; + else if (ofs) + ofs--; + else + break; + } + + if (i < end) + return i; + + /* when overflowing, remap remaining blocks to bad ones */ + for (i = end - 1; bad_blocks > 0; i--) { + if (!bbt_block_is_bad(i)) + continue; + + bad_blocks--; + if (bad_blocks <= ofs) + return i; + } + + return block; +} + +static bool remap_block_bbt(u16 block, u16 mapped_blk, int copy_len) +{ + int start, end; + u16 new_blk; + + if (!mapping_block_in_range(block, &start, &end)) + return false; + + bbt_set_block_state(mapped_blk, true); + + new_blk = get_mapping_block_index_bbt(block); + bbt_nand_erase(new_blk); + if (copy_len > 0) + bbt_nand_copy(new_blk, mapped_blk, copy_len); + + return false; +} + +static void +unmap_block_bbt(u16 block) +{ + bbt_set_block_state(block, false); +} + +static int +mtk_bmt_read_bbt(void) +{ + u8 oob_buf[8]; + int i; + + for (i = bmtd.total_blks - 1; i >= bmtd.total_blks - 5; i--) { + u32 page = i << (bmtd.blk_shift - bmtd.pg_shift); + + if (bbt_nand_read(page, nand_bbt_buf, bmtd.pg_size, + oob_buf, sizeof(oob_buf))) { + pr_info("read_bbt: could not read block %d\n", i); + continue; + } + + if (oob_buf[0] != 0xff) { + pr_info("read_bbt: bad block at %d\n", i); + continue; + } + + if (memcmp(&oob_buf[1], "mtknand", 7) != 0) { + pr_info("read_bbt: signature mismatch in block %d\n", i); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, oob_buf, 8, 1); + continue; + } + + pr_info("read_bbt: found bbt at block %d\n", i); + bmtd.bmt_blk_idx = i; + return 0; + } + + return -EIO; +} + + +static int +mtk_bmt_init_bbt(struct device_node *np) +{ + int buf_size = round_up(bmtd.total_blks >> 2, bmtd.blk_size); + int ret; + + nand_bbt_buf = kmalloc(buf_size, GFP_KERNEL); + if (!nand_bbt_buf) + return -ENOMEM; + + memset(nand_bbt_buf, 0xff, buf_size); + bmtd.mtd->size -= 4 * bmtd.mtd->erasesize; + + ret = mtk_bmt_read_bbt(); + if (ret) + return ret; + + bmtd.bmt_pgs = buf_size / bmtd.pg_size; + + return 0; +} + +static int mtk_bmt_debug_bbt(void *data, u64 val) +{ + char buf[5]; + int i, k; + + switch (val) { + case 0: + for (i = 0; i < bmtd.total_blks; i += 4) { + u8 cur = nand_bbt_buf[i / 4]; + + for (k = 0; k < 4; k++, cur >>= 2) + buf[k] = (cur & 3) ? 'B' : '.'; + + buf[4] = 0; + printk("[%06x] %s\n", i * bmtd.blk_size, buf); + } + break; + case 100: +#if 0 + for (i = bmtd.bmt_blk_idx; i < bmtd.total_blks - 1; i++) + bbt_nand_erase(bmtd.bmt_blk_idx); +#endif + + bmtd.bmt_blk_idx = bmtd.total_blks - 1; + bbt_nand_erase(bmtd.bmt_blk_idx); + write_bmt(bmtd.bmt_blk_idx, nand_bbt_buf); + break; + default: + break; + } + return 0; +} + +int mtk_bmt_attach(struct mtd_info *mtd) +{ + static const struct mtk_bmt_ops v2_ops = { + .sig = "bmt", + .sig_len = 3, + .init = mtk_bmt_init_v2, + .remap_block = remap_block_v2, + .unmap_block = unmap_block_v2, + .get_mapping_block = get_mapping_block_index_v2, + .debug = mtk_bmt_debug_v2, + }; + static const struct mtk_bmt_ops bbt_ops = { + .sig = "mtknand", + .sig_len = 7, + .init = mtk_bmt_init_bbt, + .remap_block = remap_block_bbt, + .unmap_block = unmap_block_bbt, + .get_mapping_block = get_mapping_block_index_bbt, + .debug = mtk_bmt_debug_bbt, + }; + struct device_node *np; + int ret = 0; + + if (bmtd.mtd) + return -ENOSPC; + + np = mtd_get_of_node(mtd); + if (!np) + return 0; + + if (of_property_read_bool(np, "mediatek,bmt-v2")) + bmtd.ops = &v2_ops; + else if (of_property_read_bool(np, "mediatek,bbt")) + bmtd.ops = &bbt_ops; + else + return 0; + + bmtd.remap_range = of_get_property(np, "mediatek,bmt-remap-range", + &bmtd.remap_range_len); + bmtd.remap_range_len /= 8; + + bmtd.mtd = mtd; + mtk_bmt_replace_ops(mtd); + + bmtd.blk_size = mtd->erasesize; + bmtd.blk_shift = ffs(bmtd.blk_size) - 1; + bmtd.pg_size = mtd->writesize; + bmtd.pg_shift = ffs(bmtd.pg_size) - 1; + bmtd.total_blks = mtd->size >> bmtd.blk_shift; + + nand_data_buf = kzalloc(bmtd.pg_size, GFP_KERNEL); + if (!nand_data_buf) { + pr_info("nand: FATAL ERR: allocate buffer failed!\n"); + ret = -1; + goto error; + } + + memset(nand_data_buf, 0xff, bmtd.pg_size); + + ret = bmtd.ops->init(np); + if (ret) + goto error; + + mtk_bmt_add_debugfs(); + return 0; + +error: + mtk_bmt_detach(mtd); + return ret; +} + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xiangsheng Hou , Felix Fietkau "); +MODULE_DESCRIPTION("Bad Block mapping management v2 for MediaTek NAND Flash Driver"); + diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c index e6a6928896..bd0469d5e8 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c @@ -39,7 +39,7 @@ #include "routerboot.h" -#define RB_HARDCONFIG_VER "0.06" +#define RB_HARDCONFIG_VER "0.07" #define RB_HC_PR_PFX "[rb_hardconfig] " /* ID values for hardware settings */ @@ -676,10 +676,9 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, return count; } -int __init rb_hardconfig_init(struct kobject *rb_kobj) +int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd) { struct kobject *hc_wlan_kobj; - struct mtd_info *mtd; size_t bytes_read, buflen, outlen; const u8 *buf; void *outbuf; @@ -690,20 +689,19 @@ int __init rb_hardconfig_init(struct kobject *rb_kobj) hc_kobj = NULL; hc_wlan_kobj = NULL; - // TODO allow override - mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG); - if (IS_ERR(mtd)) + ret = __get_mtd_device(mtd); + if (ret) return -ENODEV; hc_buflen = mtd->size; hc_buf = kmalloc(hc_buflen, GFP_KERNEL); if (!hc_buf) { - put_mtd_device(mtd); + __put_mtd_device(mtd); return -ENOMEM; } ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf); - put_mtd_device(mtd); + __put_mtd_device(mtd); if (ret) goto fail; @@ -818,8 +816,10 @@ fail: return ret; } -void __exit rb_hardconfig_exit(void) +void rb_hardconfig_exit(void) { kobject_put(hc_kobj); + hc_kobj = NULL; kfree(hc_buf); + hc_buf = NULL; } diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c index 070bd32d5a..5acff6aa91 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c @@ -56,23 +56,12 @@ #include "routerboot.h" -#define RB_SOFTCONFIG_VER "0.03" +#define RB_SOFTCONFIG_VER "0.05" #define RB_SC_PR_PFX "[rb_softconfig] " -/* - * mtd operations before 4.17 are asynchronous, not handled by this code - * Also make the driver act read-only if 4K_SECTORS are not enabled, since they - * are require to handle partial erasing of the small soft_config partition. - */ -#if defined(CONFIG_MTD_SPI_NOR_USE_4K_SECTORS) - #define RB_SC_HAS_WRITE_SUPPORT true - #define RB_SC_WMODE S_IWUSR - #define RB_SC_RMODE S_IRUSR -#else - #define RB_SC_HAS_WRITE_SUPPORT false - #define RB_SC_WMODE 0 - #define RB_SC_RMODE S_IRUSR -#endif +#define RB_SC_HAS_WRITE_SUPPORT true +#define RB_SC_WMODE S_IWUSR +#define RB_SC_RMODE S_IRUSR /* ID values for software settings */ #define RB_SCID_UART_SPEED 0x01 // u32*1 @@ -705,9 +694,8 @@ mtdfail: static struct kobj_attribute sc_kattrcommit = __ATTR(commit, RB_SC_RMODE|RB_SC_WMODE, sc_commit_show, sc_commit_store); -int __init rb_softconfig_init(struct kobject *rb_kobj) +int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd) { - struct mtd_info *mtd; size_t bytes_read, buflen; const u8 *buf; int i, ret; @@ -716,20 +704,19 @@ int __init rb_softconfig_init(struct kobject *rb_kobj) sc_buf = NULL; sc_kobj = NULL; - // TODO allow override - mtd = get_mtd_device_nm(RB_MTD_SOFT_CONFIG); - if (IS_ERR(mtd)) + ret = __get_mtd_device(mtd); + if (ret) return -ENODEV; sc_buflen = mtd->size; sc_buf = kmalloc(sc_buflen, GFP_KERNEL); if (!sc_buf) { - put_mtd_device(mtd); + __put_mtd_device(mtd); return -ENOMEM; } ret = mtd_read(mtd, 0, sc_buflen, &bytes_read, sc_buf); - put_mtd_device(mtd); + __put_mtd_device(mtd); if (ret) goto fail; @@ -799,8 +786,10 @@ fail: return ret; } -void __exit rb_softconfig_exit(void) +void rb_softconfig_exit(void) { kobject_put(sc_kobj); + sc_kobj = NULL; kfree(sc_buf); + sc_buf = NULL; } diff --git a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c index 4c8c0bfac5..96f2460916 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "routerboot.h" @@ -160,25 +161,57 @@ fail: return ret; } +static void routerboot_mtd_notifier_add(struct mtd_info *mtd) +{ + /* Currently routerboot is only known to live on NOR flash */ + if (mtd->type != MTD_NORFLASH) + return; + + /* + * We ignore the following return values and always register. + * These init() routines are designed so that their failed state is + * always manageable by the corresponding exit() calls. + * Notifier is called with MTD mutex held: use __get/__put variants. + * TODO: allow partition names override + */ + if (!strcmp(mtd->name, RB_MTD_HARD_CONFIG)) + rb_hardconfig_init(rb_kobj, mtd); + else if (!strcmp(mtd->name, RB_MTD_SOFT_CONFIG)) + rb_softconfig_init(rb_kobj, mtd); +} + +static void routerboot_mtd_notifier_remove(struct mtd_info *mtd) +{ + if (mtd->type != MTD_NORFLASH) + return; + + if (!strcmp(mtd->name, RB_MTD_HARD_CONFIG)) + rb_hardconfig_exit(); + else if (!strcmp(mtd->name, RB_MTD_SOFT_CONFIG)) + rb_softconfig_exit(); +} + +/* Note: using a notifier prevents qualifying init()/exit() functions with __init/__exit */ +static struct mtd_notifier routerboot_mtd_notifier = { + .add = routerboot_mtd_notifier_add, + .remove = routerboot_mtd_notifier_remove, +}; + static int __init routerboot_init(void) { rb_kobj = kobject_create_and_add("mikrotik", firmware_kobj); if (!rb_kobj) return -ENOMEM; - /* - * We ignore the following return values and always register. - * These init() routines are designed so that their failed state is - * always manageable by the corresponding exit() calls. - */ - rb_hardconfig_init(rb_kobj); - rb_softconfig_init(rb_kobj); + register_mtd_user(&routerboot_mtd_notifier); return 0; } static void __exit routerboot_exit(void) { + unregister_mtd_user(&routerboot_mtd_notifier); + /* Exit routines are idempotent */ rb_softconfig_exit(); rb_hardconfig_exit(); kobject_put(rb_kobj); // recursive afaict diff --git a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h index 67d89808d5..e858a524af 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h +++ b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h @@ -25,11 +25,11 @@ int routerboot_tag_find(const u8 *bufhead, const size_t buflen, const u16 tag_id, u16 *pld_ofs, u16 *pld_len); int routerboot_rle_decode(const u8 *in, size_t inlen, u8 *out, size_t *outlen); -int __init rb_hardconfig_init(struct kobject *rb_kobj); -void __exit rb_hardconfig_exit(void); +int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd); +void rb_hardconfig_exit(void); -int __init rb_softconfig_init(struct kobject *rb_kobj); -void __exit rb_softconfig_exit(void); +int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd); +void rb_softconfig_exit(void); ssize_t routerboot_tag_show_string(const u8 *pld, u16 pld_len, char *buf); ssize_t routerboot_tag_show_u32s(const u8 *pld, u16 pld_len, char *buf); diff --git a/target/linux/generic/files/include/linux/mtd/mtk_bmt.h b/target/linux/generic/files/include/linux/mtd/mtk_bmt.h new file mode 100644 index 0000000000..cbb6d04d89 --- /dev/null +++ b/target/linux/generic/files/include/linux/mtd/mtk_bmt.h @@ -0,0 +1,18 @@ +#ifndef __MTK_BMT_H +#define __MTK_BMT_H + +#ifdef CONFIG_MTD_NAND_MTK_BMT +int mtk_bmt_attach(struct mtd_info *mtd); +void mtk_bmt_detach(struct mtd_info *mtd); +#else +static inline int mtk_bmt_attach(struct mtd_info *mtd) +{ + return 0; +} + +static inline void mtk_bmt_detach(struct mtd_info *mtd) +{ +} +#endif + +#endif diff --git a/target/linux/generic/hack-5.10/430-mtk-bmt-support.patch b/target/linux/generic/hack-5.10/430-mtk-bmt-support.patch new file mode 100644 index 0000000000..749dcb8356 --- /dev/null +++ b/target/linux/generic/hack-5.10/430-mtk-bmt-support.patch @@ -0,0 +1,23 @@ +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -15,6 +15,10 @@ config MTD_NAND_ECC + bool + depends on MTD_NAND_CORE + ++config MTD_NAND_MTK_BMT ++ bool "Support MediaTek NAND Bad-block Management Table" ++ default n ++ + endmenu + + endmenu +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -2,6 +2,7 @@ + + nandcore-objs := core.o bbt.o + obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o ++obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o + + obj-y += onenand/ + obj-y += raw/ diff --git a/target/linux/generic/pending-5.10/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/target/linux/generic/pending-5.10/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch new file mode 100644 index 0000000000..fe92530081 --- /dev/null +++ b/target/linux/generic/pending-5.10/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch @@ -0,0 +1,397 @@ +From patchwork Tue Jun 8 04:07:19 2021 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: John Thomson +X-Patchwork-Id: 1489105 +X-Patchwork-Delegate: tudor.ambarus@gmail.com +Return-Path: + +X-Original-To: incoming@patchwork.ozlabs.org +Delivered-To: patchwork-incoming@bilbo.ozlabs.org +Authentication-Results: ozlabs.org; + spf=none (no SPF record) smtp.mailfrom=lists.infradead.org + (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; + envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; + receiver=) +Authentication-Results: ozlabs.org; + dkim=pass (2048-bit key; + secure) header.d=lists.infradead.org header.i=@lists.infradead.org + header.a=rsa-sha256 header.s=bombadil.20210309 header.b=EMabhVoR; + dkim=fail reason="signature verification failed" (2048-bit key; + unprotected) header.d=fastmail.com.au header.i=@fastmail.com.au + header.a=rsa-sha256 header.s=fm3 header.b=dLzuZ6dB; + dkim=fail reason="signature verification failed" (2048-bit key; + unprotected) header.d=messagingengine.com header.i=@messagingengine.com + header.a=rsa-sha256 header.s=fm3 header.b=nSRGsW+C; + dkim-atps=neutral +Received: from bombadil.infradead.org (bombadil.infradead.org + [IPv6:2607:7c80:54:e::133]) + (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) + key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest + SHA256) + (No client certificate requested) + by ozlabs.org (Postfix) with ESMTPS id 4FzcFN1j1nz9sW8 + for ; Tue, 8 Jun 2021 14:09:28 +1000 (AEST) +DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; + d=lists.infradead.org; s=bombadil.20210309; h=Sender: + Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: + List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:Cc + :To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: + Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: + List-Owner; bh=6mUWQd71FwsINycGYY1qOhKz+ecWJVNtwDkTebG3XkA=; b=EMabhVoRE3ad89 + o3L2AgyKrs+blSofUC3hoSsQe7gi3m4si8S9HW8Z+8SsS5TufUsvGwDl80qSYGlQOytQF+1yRUWvE + 6FJ/+bqv+TwjqZFibgJ6+9OVsQN9dZ/no1R0bBXIpmrf8ORUmv58QK4ZQquaFKbyXKpFeWOC2MSv4 + H2MAhyhTU8a3gtooH6G8+KvsJEfVgh6C+aDbwxyh2UY3chHKuw1kvL6AktbfUE2xl4zxi3x3kc70B + Wi3LiJBFokxVdgnROXxTU5tI0XboWYkQV64gLuQNV4XKClcuhVpzloDK8Iok6NTd7b32a7TdEFlCS + lGKsEKmxtUlW2FpfoduA==; +Received: from localhost ([::1] helo=bombadil.infradead.org) + by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) + id 1lqT1r-006OAW-DX; Tue, 08 Jun 2021 04:07:51 +0000 +Received: from new1-smtp.messagingengine.com ([66.111.4.221]) + by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) + id 1lqT1l-006O9b-Fq + for linux-mtd@lists.infradead.org; Tue, 08 Jun 2021 04:07:50 +0000 +Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) + by mailnew.nyi.internal (Postfix) with ESMTP id 4456B580622; + Tue, 8 Jun 2021 00:07:42 -0400 (EDT) +Received: from mailfrontend2 ([10.202.2.163]) + by compute2.internal (MEProxy); Tue, 08 Jun 2021 00:07:42 -0400 +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fastmail.com.au; + h=from:to:cc:subject:date:message-id:mime-version + :content-transfer-encoding; s=fm3; bh=ZXRH+YluM1mHCS1EWUiCY/Sg8O + LccfHe1oW5iAay6y8=; b=dLzuZ6dBYf7ZA8tWLOBFZYLi7ERsGe/4vnMXG+ovvb + dNBO0+SaFGwoqYSFrfq/TeyHfKyvxrA7+LCdopIuT4abpLHxtRwtRiafQcDYCPat + qJIqOZO+wCZC5S9Jc1OP7+t1FviGpgevqIMotci37P+RWc5u3AweMzFljZk90E8C + uorV6rXagD+OssJQzllRnAIK88+rOAC9ZyXv2gWxy4d1HSCwSWgzx2vnV9CNp918 + YC/3tiHas9krbrPIaAsdBROr7Bvoe/ShRRzruKRuvZVgg5NN90vX+/5ZjI8u04GM + p2bWCbC62CP6wlcgDaz+c/Sgr5ITd2GPENJsHfqmLRBA== +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= + messagingengine.com; h=cc:content-transfer-encoding:date:from + :message-id:mime-version:subject:to:x-me-proxy:x-me-proxy + :x-me-sender:x-me-sender:x-sasl-enc; s=fm3; bh=ZXRH+YluM1mHCS1EW + UiCY/Sg8OLccfHe1oW5iAay6y8=; b=nSRGsW+CQ2Zx1RVpIUu8W/VD/k5P+32BW + 5k2ltd+UhI3dfldBPzHrYiOP/IJqGkNW+V+rHASacW/vFygnaZoxNjRYKnOsu+26 + wb2yK3jpl6lsNTg3N1Z4XJrYY2lf9H29DMFbhC67l0PTc050rcZk4XsKTLAlv14Q + VA4WREYSaX/4IN4O+ES4TMq0a/3gKZh6nvbbJXbsXfK0WlSHTGZtZmW3fyrqvbXa + t+R7L8vvqWvwls0pV+Sn8LeQqb7+A69w0UOnuznjkcA3sCc2YehcHbxcUEnMH+9N + bxOjmIDeg9/4X/829tUWUJiLhE5SFmQZ1P6oFtmbWoLrDz0ZJIVBw== +X-ME-Sender: + +X-ME-Received: + +X-ME-Proxy-Cause: + gggruggvucftvghtrhhoucdtuddrgeduledrfedtkedgjeduucetufdoteggodetrfdotf + fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen + uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne + cujfgurhephffvufffkffoggfgsedtkeertdertddtnecuhfhrohhmpeflohhhnhcuvfhh + ohhmshhonhcuoehgihhtsehjohhhnhhthhhomhhsohhnrdhfrghsthhmrghilhdrtghomh + drrghuqeenucggtffrrghtthgvrhhnpefffeeihfdukedtuedufeetieeuudfhhefhkefh + tefgtdeuffekffelleetveduieenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmh + epmhgrihhlfhhrohhmpehgihhtsehjohhhnhhthhhomhhsohhnrdhfrghsthhmrghilhdr + tghomhdrrghu +X-ME-Proxy: + + + +Received: by mail.messagingengine.com (Postfix) with ESMTPA; Tue, + 8 Jun 2021 00:07:35 -0400 (EDT) +From: John Thomson +To: Miquel Raynal , + Richard Weinberger , Vignesh Raghavendra , + Tudor Ambarus , + Michael Walle , Pratyush Yadav , + linux-mtd@lists.infradead.org +Cc: linux-kernel@vger.kernel.org, + John Thomson , + kernel test robot , Dan Carpenter +Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions +Date: Tue, 8 Jun 2021 14:07:19 +1000 +Message-Id: <20210608040719.14431-1-git@johnthomson.fastmail.com.au> +X-Mailer: git-send-email 2.31.1 +MIME-Version: 1.0 +X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 +X-CRM114-CacheID: sfid-20210607_210745_712053_67A7D864 +X-CRM114-Status: GOOD ( 26.99 ) +X-Spam-Score: -0.8 (/) +X-Spam-Report: Spam detection software, + running on the system "bombadil.infradead.org", + has NOT identified this incoming email as spam. The original + message has been attached to this so you can view it or label + similar future email. If you have any questions, see + the administrator of that system for details. + Content preview: Do not prevent writing to mtd partitions where a partition + boundary sits on a minor erasesize boundary. This addresses a FIXME that + has been present since the start of the linux git history: /* Doesn' [...] + Content analysis details: (-0.8 points, 5.0 required) + pts rule name description + ---- ---------------------- + -------------------------------------------------- + -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at https://www.dnswl.org/, + low trust [66.111.4.221 listed in list.dnswl.org] + -0.0 SPF_PASS SPF: sender matches SPF record + -0.0 SPF_HELO_PASS SPF: HELO matches SPF record + 0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3) + [66.111.4.221 listed in wl.mailspike.net] + -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature + 0.1 DKIM_SIGNED Message has a DKIM or DK signature, + not necessarily + valid + -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from + envelope-from domain + 0.0 RCVD_IN_MSPIKE_WL Mailspike good senders +X-BeenThere: linux-mtd@lists.infradead.org +X-Mailman-Version: 2.1.34 +Precedence: list +List-Id: Linux MTD discussion mailing list +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +Sender: "linux-mtd" +Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org + +Do not prevent writing to mtd partitions where a partition boundary sits +on a minor erasesize boundary. +This addresses a FIXME that has been present since the start of the +linux git history: +/* Doesn't start on a boundary of major erase size */ +/* FIXME: Let it be writable if it is on a boundary of + * _minor_ erase size though */ + +Allow a uniform erase region spi-nor device to be configured +to use the non-uniform erase regions code path for an erase with: +CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y + +On supporting hardware (SECT_4K: majority of current SPI-NOR device) +provide the facility for an erase to use the least number +of SPI-NOR operations, as well as access to 4K erase without +requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + +Introduce erasesize_minor to the mtd struct, +the smallest erasesize supported by the device + +On existing devices, this is useful where write support is wanted +for data on a 4K partition, such as some u-boot-env partitions, +or RouterBoot soft_config, while still netting the performance +benefits of using 64K sectors + +Performance: +time mtd erase firmware +OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length + +Without this patch +MTD_SPI_NOR_USE_4K_SECTORS=y |n +real 2m 11.66s |0m 50.86s +user 0m 0.00s |0m 0.00s +sys 1m 56.20s |0m 50.80s + +With this patch +MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y +real 0m 51.68s |0m 50.85s |2m 12.89s +user 0m 0.00s |0m 0.00s |0m 0.01s +sys 0m 46.94s |0m 50.38s |2m 12.46s + +Signed-off-by: John Thomson +--- +Have not tested on variable erase regions device. + +checkpatch does not like the printk(KERN_WARNING +these should be changed separately beforehand? + +Changes RFC -> v1: +Fix uninitialized variable smatch warning +Reported-by: kernel test robot +Reported-by: Dan Carpenter +--- + drivers/mtd/mtdpart.c | 52 ++++++++++++++++++++++++++++--------- + drivers/mtd/spi-nor/Kconfig | 10 +++++++ + drivers/mtd/spi-nor/core.c | 10 +++++-- + include/linux/mtd/mtd.h | 2 ++ + 4 files changed, 60 insertions(+), 14 deletions(-) + +diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c +index 665fd9020b76..fe7626b5020e 100644 +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -38,10 +38,11 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent, + struct mtd_info *master = mtd_get_master(parent); + int wr_alignment = (parent->flags & MTD_NO_ERASE) ? + master->writesize : master->erasesize; ++ int wr_alignment_minor = 0; + u64 parent_size = mtd_is_partition(parent) ? + parent->part.size : parent->size; + struct mtd_info *child; +- u32 remainder; ++ u32 remainder, remainder_minor; + char *name; + u64 tmp; + +@@ -143,6 +144,7 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent, + int i, max = parent->numeraseregions; + u64 end = child->part.offset + child->part.size; + struct mtd_erase_region_info *regions = parent->eraseregions; ++ uint32_t erasesize_minor = child->erasesize; + + /* Find the first erase regions which is part of this + * partition. */ +@@ -153,15 +155,24 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent, + if (i > 0) + i--; + +- /* Pick biggest erasesize */ + for (; i < max && regions[i].offset < end; i++) { ++ /* Pick biggest erasesize */ + if (child->erasesize < regions[i].erasesize) + child->erasesize = regions[i].erasesize; ++ /* Pick smallest non-zero erasesize */ ++ if ((erasesize_minor > regions[i].erasesize) && (regions[i].erasesize > 0)) ++ erasesize_minor = regions[i].erasesize; + } ++ ++ if (erasesize_minor < child->erasesize) ++ child->erasesize_minor = erasesize_minor; ++ + BUG_ON(child->erasesize == 0); + } else { + /* Single erase size */ + child->erasesize = master->erasesize; ++ if (master->erasesize_minor) ++ child->erasesize_minor = master->erasesize_minor; + } + + /* +@@ -169,26 +180,43 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent, + * exposes several regions with different erasesize. Adjust + * wr_alignment accordingly. + */ +- if (!(child->flags & MTD_NO_ERASE)) ++ if (!(child->flags & MTD_NO_ERASE)) { + wr_alignment = child->erasesize; ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) && child->erasesize_minor) ++ wr_alignment_minor = child->erasesize_minor; ++ } + + tmp = mtd_get_master_ofs(child, 0); + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- /* Doesn't start on a boundary of major erase size */ +- /* FIXME: Let it be writable if it is on a boundary of +- * _minor_ erase size though */ +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ tmp = mtd_get_master_ofs(child, 0); ++ remainder_minor = do_div(tmp, wr_alignment_minor); ++ if (remainder_minor == 0) ++ child->erasesize = child->erasesize_minor; ++ } ++ ++ if ((!wr_alignment_minor) || (wr_alignment_minor && remainder_minor != 0)) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", ++ part->name); ++ } + } + + tmp = mtd_get_master_ofs(child, 0) + child->part.size; + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ tmp = mtd_get_master_ofs(child, 0) + child->part.size; ++ remainder_minor = do_div(tmp, wr_alignment_minor); ++ if (remainder_minor == 0) ++ child->erasesize = child->erasesize_minor; ++ } ++ if ((!wr_alignment_minor) || (wr_alignment_minor && remainder_minor != 0)) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", ++ part->name); ++ } + } + + child->size = child->part.size; +diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig +index 24cd25de2b8b..09df9f1a8127 100644 +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR + + if MTD_SPI_NOR + ++config MTD_SPI_NOR_USE_VARIABLE_ERASE ++ bool "Disable uniform_erase to allow use of all hardware supported erasesizes" ++ depends on !MTD_SPI_NOR_USE_4K_SECTORS ++ default n ++ help ++ Allow mixed use of all hardware supported erasesizes, ++ by forcing spi_nor to use the multiple eraseregions code path. ++ For example: A 68K erase will use one 64K erase, and one 4K erase ++ on supporting hardware. ++ + config MTD_SPI_NOR_USE_4K_SECTORS + bool "Use small 4096 B erase sectors" + default y +diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c +index bd2c7717eb10..43d9b54e7edd 100644 +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1262,6 +1262,8 @@ static u8 spi_nor_convert_3to4_erase(u8 opcode) + + static bool spi_nor_has_uniform_erase(const struct spi_nor *nor) + { ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE)) ++ return false; + return !!nor->params->erase_map.uniform_erase_type; + } + +@@ -2381,6 +2383,7 @@ static int spi_nor_select_erase(struct spi_nor *nor) + { + struct spi_nor_erase_map *map = &nor->params->erase_map; + const struct spi_nor_erase_type *erase = NULL; ++ const struct spi_nor_erase_type *erase_minor = NULL; + struct mtd_info *mtd = &nor->mtd; + u32 wanted_size = nor->info->sector_size; + int i; +@@ -2413,8 +2416,9 @@ static int spi_nor_select_erase(struct spi_nor *nor) + */ + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) { + if (map->erase_type[i].size) { +- erase = &map->erase_type[i]; +- break; ++ if (!erase) ++ erase = &map->erase_type[i]; ++ erase_minor = &map->erase_type[i]; + } + } + +@@ -2422,6 +2426,8 @@ static int spi_nor_select_erase(struct spi_nor *nor) + return -EINVAL; + + mtd->erasesize = erase->size; ++ if (erase_minor && erase_minor->size < erase->size) ++ mtd->erasesize_minor = erase_minor->size; + return 0; + } + +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index a89955f3cbc8..33eafa27da50 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -243,6 +243,8 @@ struct mtd_info { + * information below if they desire + */ + uint32_t erasesize; ++ /* "Minor" (smallest) erase size supported by the whole device */ ++ uint32_t erasesize_minor; + /* Minimal writable flash unit size. In case of NOR flash it is 1 (even + * though individual bits can be cleared), in case of NAND flash it is + * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR diff --git a/target/linux/imx/files/firmware/imx/sdma/sdma-imx6q.bin b/target/linux/imx/files/firmware/imx/sdma/sdma-imx6q.bin index 4d0593cec7..420256546e 100644 Binary files a/target/linux/imx/files/firmware/imx/sdma/sdma-imx6q.bin and b/target/linux/imx/files/firmware/imx/sdma/sdma-imx6q.bin differ diff --git a/target/linux/ipq40xx/mikrotik/config-default b/target/linux/ipq40xx/mikrotik/config-default index 147217380c..dd0c69b748 100644 --- a/target/linux/ipq40xx/mikrotik/config-default +++ b/target/linux/ipq40xx/mikrotik/config-default @@ -2,3 +2,4 @@ CONFIG_MIKROTIK=y CONFIG_MIKROTIK_RB_SYSFS=y CONFIG_MTD_ROUTERBOOT_PARTS=y CONFIG_MTD_SPLIT_MINOR_FW=y +CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y diff --git a/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz736x.dtsi b/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz736x.dtsi index 83ef415423..8406539f69 100644 --- a/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz736x.dtsi +++ b/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz736x.dtsi @@ -33,13 +33,13 @@ dect { label = "dect"; - gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; + gpios = <&gpio 1 GPIO_ACTIVE_LOW>; linux,code = ; }; wifi { label = "wifi"; - gpios = <&gpio 29 GPIO_ACTIVE_HIGH>; + gpios = <&gpio 29 GPIO_ACTIVE_LOW>; linux,code = ; }; }; diff --git a/target/linux/lantiq/xrx200/base-files/etc/hotplug.d/firmware/12-ath9k-eeprom b/target/linux/lantiq/xrx200/base-files/etc/hotplug.d/firmware/12-ath9k-eeprom index 07463a8061..e4683af960 100644 --- a/target/linux/lantiq/xrx200/base-files/etc/hotplug.d/firmware/12-ath9k-eeprom +++ b/target/linux/lantiq/xrx200/base-files/etc/hotplug.d/firmware/12-ath9k-eeprom @@ -23,10 +23,9 @@ case "$FIRMWARE" in avm,fritz7360-v2) caldata_extract "urlader" 0x985 0x1000 ;; - avm,fritz7412) - /usr/bin/fritz_cal_extract -i 1 -s 0x1e000 -e 0x207 -l 4096 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader") - ;; + avm,fritz7412|\ avm,fritz7430) + /usr/bin/fritz_cal_extract -i 1 -s 0x1e000 -e 0x207 -l 4096 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader") || \ /usr/bin/fritz_cal_extract -i 1 -s 0x1e800 -e 0x207 -l 4096 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader") ;; bt,homehub-v5a) diff --git a/target/linux/mediatek/dts/mt7622-rfb1-ubi.dts b/target/linux/mediatek/dts/mt7622-rfb1-ubi.dts index 3816dcb9cc..1a94446c7a 100644 --- a/target/linux/mediatek/dts/mt7622-rfb1-ubi.dts +++ b/target/linux/mediatek/dts/mt7622-rfb1-ubi.dts @@ -3,10 +3,13 @@ #include "mt7622-rfb1.dts" / { model = "MT7622_MT7531 RFB (UBI)"; - compatible = "mediatek,mt7622,ubi"; + compatible = "mediatek,mt7622-rfb1-ubi"; }; &snand { + mediatek,bmt-v2; + mediatek,bmt-remap-range = <0x0 0x6c0000>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; @@ -35,22 +38,19 @@ factory: partition@1c0000 { label = "Factory"; - reg = <0x1c0000 0x0040000>; + reg = <0x1c0000 0x0100000>; }; partition@200000 { label = "kernel"; - reg = <0x200000 0x400000>; + reg = <0x2c0000 0x400000>; }; - partition@600000 { + partition@6c0000 { label = "ubi"; - reg = <0x600000 0x1C00000>; + reg = <0x6c0000 0x6f00000>; }; - partition@2200000 { - label = "User_data"; - reg = <0x2200000 0x4000000>; - }; + /delete-node/ partition@2200000; }; }; diff --git a/target/linux/mediatek/dts/mt7623a-unielec-u7623-02-emmc.dtsi b/target/linux/mediatek/dts/mt7623a-unielec-u7623-02-emmc.dtsi index 0a2972e23b..1c5a18de02 100644 --- a/target/linux/mediatek/dts/mt7623a-unielec-u7623-02-emmc.dtsi +++ b/target/linux/mediatek/dts/mt7623a-unielec-u7623-02-emmc.dtsi @@ -328,6 +328,10 @@ pinctrl-0 = <&pcie_default>; status = "okay"; + pcie@0,0 { + status = "okay"; + }; + pcie@1,0 { status = "okay"; }; @@ -337,6 +341,10 @@ }; }; +&pcie0_phy { + status = "okay"; +}; + &pcie1_phy { status = "okay"; }; diff --git a/target/linux/mediatek/image/mt7622.mk b/target/linux/mediatek/image/mt7622.mk index 795bb3fc76..3b0afa98df 100644 --- a/target/linux/mediatek/image/mt7622.mk +++ b/target/linux/mediatek/image/mt7622.mk @@ -190,6 +190,7 @@ define Device/mediatek_mt7622-rfb1-ubi DEVICE_DTS := mt7622-rfb1-ubi DEVICE_DTS_DIR := ../dts DEVICE_PACKAGES := kmod-ata-ahci-mtk kmod-btmtkuart kmod-usb3 + BOARD_NAME := mediatek,mt7622-rfb1-ubi UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k PAGESIZE := 2048 diff --git a/target/linux/mediatek/mt7622/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7622/base-files/etc/board.d/02_network index 3724fd6d5c..7b454314cf 100644 --- a/target/linux/mediatek/mt7622/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/mt7622/base-files/etc/board.d/02_network @@ -11,7 +11,8 @@ mediatek_setup_interfaces() bananapi,bpi-r64|\ linksys,e8450|\ linksys,e8450-ubi|\ - mediatek,mt7622-rfb1) + mediatek,mt7622-rfb1|\ + mediatek,mt7622-rfb1-ubi) ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" wan ;; buffalo,wsr-2533dhp2) diff --git a/target/linux/mediatek/mt7622/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/mt7622/base-files/lib/upgrade/platform.sh index e6afb9547c..bd86a92c7b 100755 --- a/target/linux/mediatek/mt7622/base-files/lib/upgrade/platform.sh +++ b/target/linux/mediatek/mt7622/base-files/lib/upgrade/platform.sh @@ -34,8 +34,7 @@ platform_do_upgrade() { nand_do_upgrade "$1" fi ;; - linksys,e8450-ubi|\ - mediatek,mt7622,ubi) + linksys,e8450-ubi) CI_KERNPART="fit" nand_do_upgrade "$1" ;; @@ -47,6 +46,7 @@ platform_do_upgrade() { fi default_do_upgrade "$1" ;; + mediatek,mt7622-rfb1-ubi|\ totolink,a8000ru) nand_do_upgrade "$1" ;; @@ -68,6 +68,7 @@ platform_check_image() { buffalo,wsr-2533dhp2) buffalo_check_image "$board" "$magic" "$1" || return 1 ;; + mediatek,mt7622-rfb1-ubi|\ totolink,a8000ru) nand_do_platform_check "$board" "$1" ;; diff --git a/target/linux/mediatek/patches-5.10/330-mtk-bmt-support.patch b/target/linux/mediatek/patches-5.10/330-mtk-bmt-support.patch deleted file mode 100644 index bf70fb75b9..0000000000 --- a/target/linux/mediatek/patches-5.10/330-mtk-bmt-support.patch +++ /dev/null @@ -1,871 +0,0 @@ ---- a/drivers/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -15,6 +15,10 @@ config MTD_NAND_ECC - bool - depends on MTD_NAND_CORE - -+config MTD_NAND_MTK_BMT -+ bool "Support MediaTek NAND Bad-block Management Table" -+ default n -+ - endmenu - - endmenu ---- a/drivers/mtd/nand/Makefile -+++ b/drivers/mtd/nand/Makefile -@@ -2,6 +2,7 @@ - - nandcore-objs := core.o bbt.o - obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o -+obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o - - obj-y += onenand/ - obj-y += raw/ ---- /dev/null -+++ b/drivers/mtd/nand/mtk_bmt.c -@@ -0,0 +1,788 @@ -+/* -+ * Copyright (c) 2017 MediaTek Inc. -+ * Author: Xiangsheng Hou -+ * Copyright (c) 2020 Felix Fietkau -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MAIN_SIGNATURE_OFFSET 0 -+#define OOB_SIGNATURE_OFFSET 1 -+#define BBPOOL_RATIO 2 -+ -+#define BBT_LOG(fmt, ...) pr_debug("[BBT][%s|%d] "fmt"\n", __func__, __LINE__, ##__VA_ARGS__) -+ -+/* Maximum 8k blocks */ -+#define BB_TABLE_MAX bmtd.table_size -+#define BMT_TABLE_MAX (BB_TABLE_MAX * BBPOOL_RATIO / 100) -+#define BMT_TBL_DEF_VAL 0x0 -+ -+/* -+ * Burner Bad Block Table -+ * --------- Only support SLC Nand Chips!!!!!!!!!!! ---------- -+ */ -+ -+struct bbbt { -+ char signature[3]; -+ /* This version is used to distinguish the legacy and new algorithm */ -+#define BBMT_VERSION 2 -+ unsigned char version; -+ /* Below 2 tables will be written in SLC */ -+ u16 bb_tbl[]; -+}; -+ -+struct bbmt { -+ u16 block; -+#define NO_MAPPED 0 -+#define NORMAL_MAPPED 1 -+#define BMT_MAPPED 2 -+ u16 mapped; -+}; -+ -+static struct bmt_desc { -+ struct mtd_info *mtd; -+ -+ int (*_read_oob) (struct mtd_info *mtd, loff_t from, -+ struct mtd_oob_ops *ops); -+ int (*_write_oob) (struct mtd_info *mtd, loff_t to, -+ struct mtd_oob_ops *ops); -+ int (*_erase) (struct mtd_info *mtd, struct erase_info *instr); -+ int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs); -+ int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); -+ -+ struct bbbt *bbt; -+ -+ struct dentry *debugfs_dir; -+ -+ u32 table_size; -+ u32 pg_size; -+ u32 blk_size; -+ u16 pg_shift; -+ u16 blk_shift; -+ /* bbt logical address */ -+ u16 pool_lba; -+ /* bbt physical address */ -+ u16 pool_pba; -+ /* Maximum count of bad blocks that the vendor guaranteed */ -+ u16 bb_max; -+ /* Total blocks of the Nand Chip */ -+ u16 total_blks; -+ /* The block(n) BMT is located at (bmt_tbl[n]) */ -+ u16 bmt_blk_idx; -+ /* How many pages needs to store 'struct bbbt' */ -+ u32 bmt_pgs; -+ -+ /* to compensate for driver level remapping */ -+ u8 oob_offset; -+} bmtd = {0}; -+ -+static unsigned char *nand_bbt_buf; -+static unsigned char *nand_data_buf; -+ -+/* -------- Unit conversions -------- */ -+static inline u32 blk_pg(u16 block) -+{ -+ return (u32)(block << (bmtd.blk_shift - bmtd.pg_shift)); -+} -+ -+/* -------- Nand operations wrapper -------- */ -+static inline int -+bbt_nand_read(u32 page, unsigned char *dat, int dat_len, -+ unsigned char *fdm, int fdm_len) -+{ -+ struct mtd_oob_ops ops = { -+ .mode = MTD_OPS_PLACE_OOB, -+ .ooboffs = bmtd.oob_offset, -+ .oobbuf = fdm, -+ .ooblen = fdm_len, -+ .datbuf = dat, -+ .len = dat_len, -+ }; -+ -+ return bmtd._read_oob(bmtd.mtd, page << bmtd.pg_shift, &ops); -+} -+ -+static inline int bbt_nand_erase(u16 block) -+{ -+ struct mtd_info *mtd = bmtd.mtd; -+ struct erase_info instr = { -+ .addr = (loff_t)block << bmtd.blk_shift, -+ .len = bmtd.blk_size, -+ }; -+ -+ return bmtd._erase(mtd, &instr); -+} -+ -+/* -------- Bad Blocks Management -------- */ -+static inline struct bbmt *bmt_tbl(struct bbbt *bbbt) -+{ -+ return (struct bbmt *)&bbbt->bb_tbl[bmtd.table_size]; -+} -+ -+static int -+read_bmt(u16 block, unsigned char *dat, unsigned char *fdm, int fdm_len) -+{ -+ u32 len = bmtd.bmt_pgs << bmtd.pg_shift; -+ -+ return bbt_nand_read(blk_pg(block), dat, len, fdm, fdm_len); -+} -+ -+static int write_bmt(u16 block, unsigned char *dat) -+{ -+ struct mtd_oob_ops ops = { -+ .mode = MTD_OPS_PLACE_OOB, -+ .ooboffs = OOB_SIGNATURE_OFFSET + bmtd.oob_offset, -+ .oobbuf = "bmt", -+ .ooblen = 3, -+ .datbuf = dat, -+ .len = bmtd.bmt_pgs << bmtd.pg_shift, -+ }; -+ loff_t addr = (loff_t)block << bmtd.blk_shift; -+ -+ return bmtd._write_oob(bmtd.mtd, addr, &ops); -+} -+ -+static u16 find_valid_block(u16 block) -+{ -+ u8 fdm[4]; -+ int ret; -+ int loop = 0; -+ -+retry: -+ if (block >= bmtd.total_blks) -+ return 0; -+ -+ ret = bbt_nand_read(blk_pg(block), nand_data_buf, bmtd.pg_size, -+ fdm, sizeof(fdm)); -+ /* Read the 1st byte of FDM to judge whether it's a bad -+ * or not -+ */ -+ if (ret || fdm[0] != 0xff) { -+ pr_info("nand: found bad block 0x%x\n", block); -+ if (loop >= bmtd.bb_max) { -+ pr_info("nand: FATAL ERR: too many bad blocks!!\n"); -+ return 0; -+ } -+ -+ loop++; -+ block++; -+ goto retry; -+ } -+ -+ return block; -+} -+ -+/* Find out all bad blocks, and fill in the mapping table */ -+static int scan_bad_blocks(struct bbbt *bbt) -+{ -+ int i; -+ u16 block = 0; -+ -+ /* First time download, the block0 MUST NOT be a bad block, -+ * this is guaranteed by vendor -+ */ -+ bbt->bb_tbl[0] = 0; -+ -+ /* -+ * Construct the mapping table of Normal data area(non-PMT/BMTPOOL) -+ * G - Good block; B - Bad block -+ * --------------------------- -+ * physical |G|G|B|G|B|B|G|G|G|G|B|G|B| -+ * --------------------------- -+ * What bb_tbl[i] looks like: -+ * physical block(i): -+ * 0 1 2 3 4 5 6 7 8 9 a b c -+ * mapped block(bb_tbl[i]): -+ * 0 1 3 6 7 8 9 b ...... -+ * ATTENTION: -+ * If new bad block ocurred(n), search bmt_tbl to find -+ * a available block(x), and fill in the bb_tbl[n] = x; -+ */ -+ for (i = 1; i < bmtd.pool_lba; i++) { -+ bbt->bb_tbl[i] = find_valid_block(bbt->bb_tbl[i - 1] + 1); -+ BBT_LOG("bb_tbl[0x%x] = 0x%x", i, bbt->bb_tbl[i]); -+ if (bbt->bb_tbl[i] == 0) -+ return -1; -+ } -+ -+ /* Physical Block start Address of BMT pool */ -+ bmtd.pool_pba = bbt->bb_tbl[i - 1] + 1; -+ if (bmtd.pool_pba >= bmtd.total_blks - 2) { -+ pr_info("nand: FATAL ERR: Too many bad blocks!!\n"); -+ return -1; -+ } -+ -+ BBT_LOG("pool_pba=0x%x", bmtd.pool_pba); -+ i = 0; -+ block = bmtd.pool_pba; -+ /* -+ * The bmt table is used for runtime bad block mapping -+ * G - Good block; B - Bad block -+ * --------------------------- -+ * physical |G|G|B|G|B|B|G|G|G|G|B|G|B| -+ * --------------------------- -+ * block: 0 1 2 3 4 5 6 7 8 9 a b c -+ * What bmt_tbl[i] looks like in initial state: -+ * i: -+ * 0 1 2 3 4 5 6 7 -+ * bmt_tbl[i].block: -+ * 0 1 3 6 7 8 9 b -+ * bmt_tbl[i].mapped: -+ * N N N N N N N B -+ * N - Not mapped(Available) -+ * M - Mapped -+ * B - BMT -+ * ATTENTION: -+ * BMT always in the last valid block in pool -+ */ -+ while ((block = find_valid_block(block)) != 0) { -+ bmt_tbl(bbt)[i].block = block; -+ bmt_tbl(bbt)[i].mapped = NO_MAPPED; -+ BBT_LOG("bmt_tbl[%d].block = 0x%x", i, block); -+ block++; -+ i++; -+ } -+ -+ /* i - How many available blocks in pool, which is the length of bmt_tbl[] -+ * bmtd.bmt_blk_idx - bmt_tbl[bmtd.bmt_blk_idx].block => the BMT block -+ */ -+ bmtd.bmt_blk_idx = i - 1; -+ bmt_tbl(bbt)[bmtd.bmt_blk_idx].mapped = BMT_MAPPED; -+ -+ if (i < 1) { -+ pr_info("nand: FATAL ERR: no space to store BMT!!\n"); -+ return -1; -+ } -+ -+ pr_info("[BBT] %d available blocks in BMT pool\n", i); -+ -+ return 0; -+} -+ -+static bool is_valid_bmt(unsigned char *buf, unsigned char *fdm) -+{ -+ struct bbbt *bbt = (struct bbbt *)buf; -+ u8 *sig = (u8*)bbt->signature + MAIN_SIGNATURE_OFFSET; -+ -+ -+ if (memcmp(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3) == 0 && -+ memcmp(fdm + OOB_SIGNATURE_OFFSET, "bmt", 3) == 0) { -+ if (bbt->version == BBMT_VERSION) -+ return true; -+ } -+ BBT_LOG("[BBT] BMT Version not match,upgrage preloader and uboot please! sig=%02x%02x%02x, fdm=%02x%02x%02x", -+ sig[0], sig[1], sig[2], -+ fdm[1], fdm[2], fdm[3]); -+ return false; -+} -+ -+static u16 get_bmt_index(struct bbmt *bmt) -+{ -+ int i = 0; -+ -+ while (bmt[i].block != BMT_TBL_DEF_VAL) { -+ if (bmt[i].mapped == BMT_MAPPED) -+ return i; -+ i++; -+ } -+ return 0; -+} -+ -+static struct bbbt *scan_bmt(u16 block) -+{ -+ u8 fdm[4]; -+ -+ if (block < bmtd.pool_lba) -+ return NULL; -+ -+ if (read_bmt(block, nand_bbt_buf, fdm, sizeof(fdm))) -+ return scan_bmt(block - 1); -+ -+ if (is_valid_bmt(nand_bbt_buf, fdm)) { -+ bmtd.bmt_blk_idx = get_bmt_index(bmt_tbl((struct bbbt *)nand_bbt_buf)); -+ if (bmtd.bmt_blk_idx == 0) { -+ pr_info("[BBT] FATAL ERR: bmt block index is wrong!\n"); -+ return NULL; -+ } -+ pr_info("[BBT] BMT.v2 is found at 0x%x\n", block); -+ return (struct bbbt *)nand_bbt_buf; -+ } else -+ return scan_bmt(block - 1); -+} -+ -+/* Write the Burner Bad Block Table to Nand Flash -+ * n - write BMT to bmt_tbl[n] -+ */ -+static u16 upload_bmt(struct bbbt *bbt, int n) -+{ -+ u16 block; -+ -+retry: -+ if (n < 0 || bmt_tbl(bbt)[n].mapped == NORMAL_MAPPED) { -+ pr_info("nand: FATAL ERR: no space to store BMT!\n"); -+ return (u16)-1; -+ } -+ -+ block = bmt_tbl(bbt)[n].block; -+ BBT_LOG("n = 0x%x, block = 0x%x", n, block); -+ if (bbt_nand_erase(block)) { -+ bmt_tbl(bbt)[n].block = 0; -+ /* erase failed, try the previous block: bmt_tbl[n - 1].block */ -+ n--; -+ goto retry; -+ } -+ -+ /* The signature offset is fixed set to 0, -+ * oob signature offset is fixed set to 1 -+ */ -+ memcpy(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3); -+ bbt->version = BBMT_VERSION; -+ -+ if (write_bmt(block, (unsigned char *)bbt)) { -+ bmt_tbl(bbt)[n].block = 0; -+ -+ /* write failed, try the previous block in bmt_tbl[n - 1] */ -+ n--; -+ goto retry; -+ } -+ -+ /* Return the current index(n) of BMT pool (bmt_tbl[n]) */ -+ return n; -+} -+ -+static u16 find_valid_block_in_pool(struct bbbt *bbt) -+{ -+ int i; -+ -+ if (bmtd.bmt_blk_idx == 0) -+ goto error; -+ -+ for (i = 0; i < bmtd.bmt_blk_idx; i++) { -+ if (bmt_tbl(bbt)[i].block != 0 && bmt_tbl(bbt)[i].mapped == NO_MAPPED) { -+ bmt_tbl(bbt)[i].mapped = NORMAL_MAPPED; -+ return bmt_tbl(bbt)[i].block; -+ } -+ } -+ -+error: -+ pr_info("nand: FATAL ERR: BMT pool is run out!\n"); -+ return 0; -+} -+ -+/* We met a bad block, mark it as bad and map it to a valid block in pool, -+ * if it's a write failure, we need to write the data to mapped block -+ */ -+static bool update_bmt(u16 block) -+{ -+ u16 mapped_blk; -+ struct bbbt *bbt; -+ -+ bbt = bmtd.bbt; -+ mapped_blk = find_valid_block_in_pool(bbt); -+ if (mapped_blk == 0) -+ return false; -+ -+ /* Map new bad block to available block in pool */ -+ bbt->bb_tbl[block] = mapped_blk; -+ bmtd.bmt_blk_idx = upload_bmt(bbt, bmtd.bmt_blk_idx); -+ -+ return true; -+} -+ -+u16 get_mapping_block_index(int block) -+{ -+ int mapping_block; -+ -+ if (block < bmtd.pool_lba) -+ mapping_block = bmtd.bbt->bb_tbl[block]; -+ else -+ mapping_block = block; -+ BBT_LOG("0x%x mapped to 0x%x", block, mapping_block); -+ -+ return mapping_block; -+} -+ -+static int -+mtk_bmt_read(struct mtd_info *mtd, loff_t from, -+ struct mtd_oob_ops *ops) -+{ -+ struct mtd_oob_ops cur_ops = *ops; -+ int retry_count = 0; -+ loff_t cur_from; -+ int ret; -+ -+ ops->retlen = 0; -+ ops->oobretlen = 0; -+ -+ while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) { -+ u32 offset = from & (bmtd.blk_size - 1); -+ u32 block = from >> bmtd.blk_shift; -+ u32 cur_block; -+ -+ cur_block = get_mapping_block_index(block); -+ cur_from = ((loff_t)cur_block << bmtd.blk_shift) + offset; -+ -+ cur_ops.oobretlen = 0; -+ cur_ops.retlen = 0; -+ cur_ops.len = min_t(u32, mtd->erasesize - offset, -+ ops->len - ops->retlen); -+ ret = bmtd._read_oob(mtd, cur_from, &cur_ops); -+ if (ret < 0) { -+ update_bmt(block); -+ if (retry_count++ < 10) -+ continue; -+ -+ return ret; -+ } -+ -+ ops->retlen += cur_ops.retlen; -+ ops->oobretlen += cur_ops.oobretlen; -+ -+ cur_ops.ooboffs = 0; -+ cur_ops.datbuf += cur_ops.retlen; -+ cur_ops.oobbuf += cur_ops.oobretlen; -+ cur_ops.ooblen -= cur_ops.oobretlen; -+ -+ if (!cur_ops.len) -+ cur_ops.len = mtd->erasesize - offset; -+ -+ from += cur_ops.len; -+ retry_count = 0; -+ } -+ -+ return 0; -+} -+ -+static int -+mtk_bmt_write(struct mtd_info *mtd, loff_t to, -+ struct mtd_oob_ops *ops) -+{ -+ struct mtd_oob_ops cur_ops = *ops; -+ int retry_count = 0; -+ loff_t cur_to; -+ int ret; -+ -+ ops->retlen = 0; -+ ops->oobretlen = 0; -+ -+ while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) { -+ u32 offset = to & (bmtd.blk_size - 1); -+ u32 block = to >> bmtd.blk_shift; -+ u32 cur_block; -+ -+ cur_block = get_mapping_block_index(block); -+ cur_to = ((loff_t)cur_block << bmtd.blk_shift) + offset; -+ -+ cur_ops.oobretlen = 0; -+ cur_ops.retlen = 0; -+ cur_ops.len = min_t(u32, bmtd.blk_size - offset, -+ ops->len - ops->retlen); -+ ret = bmtd._write_oob(mtd, cur_to, &cur_ops); -+ if (ret < 0) { -+ update_bmt(block); -+ if (retry_count++ < 10) -+ continue; -+ -+ return ret; -+ } -+ -+ ops->retlen += cur_ops.retlen; -+ ops->oobretlen += cur_ops.oobretlen; -+ -+ cur_ops.ooboffs = 0; -+ cur_ops.datbuf += cur_ops.retlen; -+ cur_ops.oobbuf += cur_ops.oobretlen; -+ cur_ops.ooblen -= cur_ops.oobretlen; -+ -+ if (!cur_ops.len) -+ cur_ops.len = mtd->erasesize - offset; -+ -+ to += cur_ops.len; -+ retry_count = 0; -+ } -+ -+ return 0; -+} -+ -+static int -+mtk_bmt_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) -+{ -+ struct erase_info mapped_instr = { -+ .len = bmtd.blk_size, -+ }; -+ int retry_count = 0; -+ u64 start_addr, end_addr; -+ int ret; -+ u16 orig_block, block; -+ -+ start_addr = instr->addr & (~mtd->erasesize_mask); -+ end_addr = instr->addr + instr->len; -+ -+ while (start_addr < end_addr) { -+ orig_block = start_addr >> bmtd.blk_shift; -+ block = get_mapping_block_index(orig_block); -+ mapped_instr.addr = (loff_t)block << bmtd.blk_shift; -+ ret = bmtd._erase(mtd, &mapped_instr); -+ if (ret) { -+ update_bmt(orig_block); -+ if (retry_count++ < 10) -+ continue; -+ instr->fail_addr = start_addr; -+ break; -+ } -+ start_addr += mtd->erasesize; -+ retry_count = 0; -+ } -+ -+ return ret; -+} -+static int -+mtk_bmt_block_isbad(struct mtd_info *mtd, loff_t ofs) -+{ -+ int retry_count = 0; -+ u16 orig_block = ofs >> bmtd.blk_shift; -+ u16 block; -+ int ret; -+ -+retry: -+ block = get_mapping_block_index(orig_block); -+ ret = bmtd._block_isbad(mtd, (loff_t)block << bmtd.blk_shift); -+ if (ret) { -+ update_bmt(orig_block); -+ if (retry_count++ < 10) -+ goto retry; -+ } -+ return ret; -+} -+ -+static int -+mtk_bmt_block_markbad(struct mtd_info *mtd, loff_t ofs) -+{ -+ u16 orig_block = ofs >> bmtd.blk_shift; -+ u16 block = get_mapping_block_index(orig_block); -+ update_bmt(orig_block); -+ return bmtd._block_markbad(mtd, (loff_t)block << bmtd.blk_shift); -+} -+ -+static void -+mtk_bmt_replace_ops(struct mtd_info *mtd) -+{ -+ bmtd._read_oob = mtd->_read_oob; -+ bmtd._write_oob = mtd->_write_oob; -+ bmtd._erase = mtd->_erase; -+ bmtd._block_isbad = mtd->_block_isbad; -+ bmtd._block_markbad = mtd->_block_markbad; -+ -+ mtd->_read_oob = mtk_bmt_read; -+ mtd->_write_oob = mtk_bmt_write; -+ mtd->_erase = mtk_bmt_mtd_erase; -+ mtd->_block_isbad = mtk_bmt_block_isbad; -+ mtd->_block_markbad = mtk_bmt_block_markbad; -+} -+ -+static int mtk_bmt_debug_mark_good(void *data, u64 val) -+{ -+ u32 block = val >> bmtd.blk_shift; -+ -+ bmtd.bbt->bb_tbl[block] = block; -+ bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx); -+ -+ return 0; -+} -+ -+static int mtk_bmt_debug_mark_bad(void *data, u64 val) -+{ -+ u32 block = val >> bmtd.blk_shift; -+ -+ update_bmt(block); -+ -+ return 0; -+} -+ -+DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_good, NULL, mtk_bmt_debug_mark_good, "%llu\n"); -+DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_bad, NULL, mtk_bmt_debug_mark_bad, "%llu\n"); -+ -+static void -+mtk_bmt_add_debugfs(void) -+{ -+ struct dentry *dir; -+ -+ dir = bmtd.debugfs_dir = debugfs_create_dir("mtk-bmt", NULL); -+ if (!dir) -+ return; -+ -+ debugfs_create_file_unsafe("mark_good", S_IWUSR, dir, NULL, &fops_mark_good); -+ debugfs_create_file_unsafe("mark_bad", S_IWUSR, dir, NULL, &fops_mark_bad); -+} -+ -+void mtk_bmt_detach(struct mtd_info *mtd) -+{ -+ if (bmtd.mtd != mtd) -+ return; -+ -+ if (bmtd.debugfs_dir) -+ debugfs_remove_recursive(bmtd.debugfs_dir); -+ bmtd.debugfs_dir = NULL; -+ -+ kfree(nand_bbt_buf); -+ kfree(nand_data_buf); -+ -+ mtd->_read_oob = bmtd._read_oob; -+ mtd->_write_oob = bmtd._write_oob; -+ mtd->_erase = bmtd._erase; -+ mtd->_block_isbad = bmtd._block_isbad; -+ mtd->_block_markbad = bmtd._block_markbad; -+ mtd->size = bmtd.total_blks << bmtd.blk_shift; -+ -+ memset(&bmtd, 0, sizeof(bmtd)); -+} -+ -+/* total_blocks - The total count of blocks that the Nand Chip has */ -+int mtk_bmt_attach(struct mtd_info *mtd) -+{ -+ struct device_node *np; -+ struct bbbt *bbt; -+ u32 bufsz; -+ u32 block; -+ u16 total_blocks, pmt_block; -+ int ret = 0; -+ u32 bmt_pool_size, bmt_table_size; -+ -+ if (bmtd.mtd) -+ return -ENOSPC; -+ -+ np = mtd_get_of_node(mtd); -+ if (!np) -+ return 0; -+ -+ if (!of_property_read_bool(np, "mediatek,bmt-v2")) -+ return 0; -+ -+ if (of_property_read_u32(np, "mediatek,bmt-pool-size", -+ &bmt_pool_size) != 0) -+ bmt_pool_size = 80; -+ -+ if (of_property_read_u8(np, "mediatek,bmt-oob-offset", -+ &bmtd.oob_offset) != 0) -+ bmtd.oob_offset = 0; -+ -+ if (of_property_read_u32(np, "mediatek,bmt-table-size", -+ &bmt_table_size) != 0) -+ bmt_table_size = 0x2000U; -+ -+ bmtd.mtd = mtd; -+ mtk_bmt_replace_ops(mtd); -+ -+ bmtd.table_size = bmt_table_size; -+ bmtd.blk_size = mtd->erasesize; -+ bmtd.blk_shift = ffs(bmtd.blk_size) - 1; -+ bmtd.pg_size = mtd->writesize; -+ bmtd.pg_shift = ffs(bmtd.pg_size) - 1; -+ total_blocks = mtd->size >> bmtd.blk_shift; -+ pmt_block = total_blocks - bmt_pool_size - 2; -+ -+ mtd->size = pmt_block << bmtd.blk_shift; -+ -+ /* -+ * --------------------------------------- -+ * | PMT(2blks) | BMT POOL(totalblks * 2%) | -+ * --------------------------------------- -+ * ^ ^ -+ * | | -+ * pmt_block pmt_block + 2blocks(pool_lba) -+ * -+ * ATTETION!!!!!! -+ * The blocks ahead of the boundary block are stored in bb_tbl -+ * and blocks behind are stored in bmt_tbl -+ */ -+ -+ bmtd.pool_lba = (u16)(pmt_block + 2); -+ bmtd.total_blks = total_blocks; -+ bmtd.bb_max = bmtd.total_blks * BBPOOL_RATIO / 100; -+ -+ /* 3 buffers we need */ -+ bufsz = round_up(sizeof(struct bbbt) + -+ bmt_table_size * sizeof(struct bbmt), bmtd.pg_size); -+ bmtd.bmt_pgs = bufsz >> bmtd.pg_shift; -+ -+ nand_bbt_buf = kzalloc(bufsz, GFP_KERNEL); -+ nand_data_buf = kzalloc(bmtd.pg_size, GFP_KERNEL); -+ -+ if (!nand_bbt_buf || !nand_data_buf) { -+ pr_info("nand: FATAL ERR: allocate buffer failed!\n"); -+ ret = -1; -+ goto error; -+ } -+ -+ memset(nand_bbt_buf, 0xff, bufsz); -+ memset(nand_data_buf, 0xff, bmtd.pg_size); -+ -+ BBT_LOG("bbtbuf=0x%p(0x%x) dat=0x%p(0x%x)", -+ nand_bbt_buf, bufsz, nand_data_buf, bmtd.pg_size); -+ BBT_LOG("pool_lba=0x%x total_blks=0x%x bb_max=0x%x", -+ bmtd.pool_lba, bmtd.total_blks, bmtd.bb_max); -+ -+ /* Scanning start from the first page of the last block -+ * of whole flash -+ */ -+ bbt = scan_bmt(bmtd.total_blks - 1); -+ if (!bbt) { -+ /* BMT not found */ -+ if (bmtd.total_blks > BB_TABLE_MAX + BMT_TABLE_MAX) { -+ pr_info("nand: FATAL: Too many blocks, can not support!\n"); -+ ret = -1; -+ goto error; -+ } -+ -+ bbt = (struct bbbt *)nand_bbt_buf; -+ memset(bmt_tbl(bbt), BMT_TBL_DEF_VAL, bmtd.table_size * sizeof(struct bbmt)); -+ -+ if (scan_bad_blocks(bbt)) { -+ ret = -1; -+ goto error; -+ } -+ -+ /* BMT always in the last valid block in pool */ -+ bmtd.bmt_blk_idx = upload_bmt(bbt, bmtd.bmt_blk_idx); -+ block = bmt_tbl(bbt)[bmtd.bmt_blk_idx].block; -+ pr_notice("[BBT] BMT.v2 is written into PBA:0x%x\n", block); -+ -+ if (bmtd.bmt_blk_idx == 0) -+ pr_info("nand: Warning: no available block in BMT pool!\n"); -+ else if (bmtd.bmt_blk_idx == (u16)-1) { -+ ret = -1; -+ goto error; -+ } -+ } -+ mtk_bmt_add_debugfs(); -+ -+ bmtd.bbt = bbt; -+ return 0; -+ -+error: -+ mtk_bmt_detach(mtd); -+ return ret; -+} -+ -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Xiangsheng Hou , Felix Fietkau "); -+MODULE_DESCRIPTION("Bad Block mapping management v2 for MediaTek NAND Flash Driver"); -+ ---- /dev/null -+++ b/include/linux/mtd/mtk_bmt.h -@@ -0,0 +1,18 @@ -+#ifndef __MTK_BMT_H -+#define __MTK_BMT_H -+ -+#ifdef CONFIG_MTD_NAND_MTK_BMT -+int mtk_bmt_attach(struct mtd_info *mtd); -+void mtk_bmt_detach(struct mtd_info *mtd); -+#else -+static inline int mtk_bmt_attach(struct mtd_info *mtd) -+{ -+ return 0; -+} -+ -+static inline void mtk_bmt_detach(struct mtd_info *mtd) -+{ -+} -+#endif -+ -+#endif ---- a/drivers/mtd/mtk-snand/mtk-snand-mtd.c -+++ b/drivers/mtd/mtk-snand/mtk-snand-mtd.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -612,6 +613,8 @@ static int mtk_snand_probe(struct platfo - mtd->_block_isbad = mtk_snand_mtd_block_isbad; - mtd->_block_markbad = mtk_snand_mtd_block_markbad; - -+ mtk_bmt_attach(mtd); -+ - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - dev_err(msm->pdev.dev, "failed to register mtd partition\n"); -@@ -623,6 +626,7 @@ static int mtk_snand_probe(struct platfo - return 0; - - errout4: -+ mtk_bmt_detach(mtd); - devm_kfree(msm->pdev.dev, msm->page_cache); - - errout3: -@@ -650,6 +654,8 @@ static int mtk_snand_remove(struct platf - if (ret) - return ret; - -+ mtk_bmt_detach(mtd); -+ - mtk_snand_cleanup(msm->snf); - - if (msm->irq >= 0) diff --git a/target/linux/mediatek/patches-5.10/330-mtk-snand-bmt-support.patch b/target/linux/mediatek/patches-5.10/330-mtk-snand-bmt-support.patch new file mode 100644 index 0000000000..318c8b2873 --- /dev/null +++ b/target/linux/mediatek/patches-5.10/330-mtk-snand-bmt-support.patch @@ -0,0 +1,36 @@ +--- a/drivers/mtd/mtk-snand/mtk-snand-mtd.c ++++ b/drivers/mtd/mtk-snand/mtk-snand-mtd.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -612,6 +613,8 @@ static int mtk_snand_probe(struct platfo + mtd->_block_isbad = mtk_snand_mtd_block_isbad; + mtd->_block_markbad = mtk_snand_mtd_block_markbad; + ++ mtk_bmt_attach(mtd); ++ + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(msm->pdev.dev, "failed to register mtd partition\n"); +@@ -623,6 +626,7 @@ static int mtk_snand_probe(struct platfo + return 0; + + errout4: ++ mtk_bmt_detach(mtd); + devm_kfree(msm->pdev.dev, msm->page_cache); + + errout3: +@@ -650,6 +654,8 @@ static int mtk_snand_remove(struct platf + if (ret) + return ret; + ++ mtk_bmt_detach(mtd); ++ + mtk_snand_cleanup(msm->snf); + + if (msm->irq >= 0) diff --git a/target/linux/ramips/dts/mt7621_linksys_ea7xxx.dtsi b/target/linux/ramips/dts/mt7621_linksys_ea7xxx.dtsi index ecff6406d5..ec232968a6 100644 --- a/target/linux/ramips/dts/mt7621_linksys_ea7xxx.dtsi +++ b/target/linux/ramips/dts/mt7621_linksys_ea7xxx.dtsi @@ -76,6 +76,11 @@ &nand { status = "okay"; + mediatek,bbt; + mediatek,bmt-remap-range = + <0x180000 0x580000>, + <0x2980000 0x2d80000>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; diff --git a/target/linux/ramips/dts/mt7621_mikrotik.dtsi b/target/linux/ramips/dts/mt7621_mikrotik.dtsi index 1fc523ea14..ace194c6d6 100644 --- a/target/linux/ramips/dts/mt7621_mikrotik.dtsi +++ b/target/linux/ramips/dts/mt7621_mikrotik.dtsi @@ -37,7 +37,6 @@ partition@0 { label = "RouterBoot"; reg = <0x0 0x40000>; - read-only; compatible = "mikrotik,routerboot-partitions"; #address-cells = <1>; #size-cells = <1>; diff --git a/target/linux/ramips/mt7621/config-5.10 b/target/linux/ramips/mt7621/config-5.10 index 5b02cdfe79..f3bf6fbb2b 100644 --- a/target/linux/ramips/mt7621/config-5.10 +++ b/target/linux/ramips/mt7621/config-5.10 @@ -154,10 +154,12 @@ CONFIG_MTD_NAND_CORE=y CONFIG_MTD_NAND_ECC=y CONFIG_MTD_NAND_ECC_SW_HAMMING=y CONFIG_MTD_NAND_MT7621=y +CONFIG_MTD_NAND_MTK_BMT=y CONFIG_MTD_PHYSMAP=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_ROUTERBOOT_PARTS=y CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y CONFIG_MTD_SPLIT_FIT_FW=y CONFIG_MTD_SPLIT_MINOR_FW=y CONFIG_MTD_SPLIT_SEAMA_FW=y diff --git a/target/linux/ramips/patches-5.10/410-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch b/target/linux/ramips/patches-5.10/410-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch index a37426edec..8049dbde14 100644 --- a/target/linux/ramips/patches-5.10/410-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch +++ b/target/linux/ramips/patches-5.10/410-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch @@ -1022,7 +1022,7 @@ Signed-off-by: Weijie Gao + vall |= (u32)oobptr[j] << (j * 8); + + for (j = 0; j < 4; j++) -+ valm |= (u32)oobptr[j + 4] << ((j - 4) * 8); ++ valm |= (u32)oobptr[j + 4] << (j * 8); + + nfi_write32(nfc, NFI_FDML(i), vall); + nfi_write32(nfc, NFI_FDMM(i), valm); diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index f5e978a7f0..0586cce732 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -11,9 +11,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware-utils.git -PKG_SOURCE_DATE:=2021-12-02 -PKG_SOURCE_VERSION:=56e8e19151743c923f48604c457850cf8eb52076 -PKG_MIRROR_HASH:=91f1bac08af2d856e93e651dc7c1ce6745f0b32b6fe4fb1917660494d7518057 +PKG_SOURCE_DATE:=2022-01-14 +PKG_SOURCE_VERSION:=84dbf8ee49f522d3a4528763c9473cf3dd7c8c52 +PKG_MIRROR_HASH:=aabf70dbc155d10175ee3b25f375953bef31bf7e25a2391fda8687aa93ea79e6 include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/cmake.mk