diff --git a/include/kernel-version.mk b/include/kernel-version.mk index 33fe6b2d0e..72c2fdc9a3 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -6,11 +6,11 @@ ifdef CONFIG_TESTING_KERNEL KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER) endif -LINUX_VERSION-5.4 = .102 -LINUX_VERSION-5.10 = .20 +LINUX_VERSION-5.4 = .105 +LINUX_VERSION-5.10 = .23 -LINUX_KERNEL_HASH-5.4.102 = fd697ce1c3f6024d4ae77d4eb5a1552199407b60cb8e90bc621e23cbce639aed -LINUX_KERNEL_HASH-5.10.20 = 9be37146feba42be05137cf900a7d9012990b5a1d5e59bc0c8da1f86952930a3 +LINUX_KERNEL_HASH-5.4.105 = 244e4cd16184285df55ec5a9501daba011aa8b85c5527ee05eab4592e70fb8b6 +LINUX_KERNEL_HASH-5.10.23 = f6e21c03ec6ff85b26d77c59fdab81e64707792a57593643307df192749edb6a remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1)))) sanitize_uri=$(call qstrip,$(subst @,_,$(subst :,_,$(subst .,_,$(subst -,_,$(subst /,_,$(1))))))) diff --git a/package/boot/arm-trusted-firmware-mediatek/Makefile b/package/boot/arm-trusted-firmware-mediatek/Makefile index 9ec69db09d..b2afdbed06 100644 --- a/package/boot/arm-trusted-firmware-mediatek/Makefile +++ b/package/boot/arm-trusted-firmware-mediatek/Makefile @@ -13,9 +13,9 @@ PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=https://github.com/mtk-openwrt/arm-trusted-firmware.git -PKG_SOURCE_DATE:=2021-02-25 -PKG_SOURCE_VERSION:=1220acb044a9db9a201aba3be0bb4ce0c9ed3702 -PKG_MIRROR_HASH:=f2ca44b9b8acfbd3a6be30aba316c765f73bad6231a821f524c9f21a845e50a3 +PKG_SOURCE_DATE:=2021-03-10 +PKG_SOURCE_VERSION:=8b635f61091f2e5bce88269468c53fc30bb4cd64 +PKG_MIRROR_HASH:=ffe233a19b51504f605a90a88c7a1bb74303e4a7173a59b24a4dfa572ce99139 PKG_MAINTAINER:=Daniel Golle @@ -85,7 +85,11 @@ TFA_TARGETS:= \ mt7622-sdmmc-1ddr \ mt7622-sdmmc-2ddr -TFA_MAKE_FLAGS += BOOT_DEVICE=$(BOOT_DEVICE) $(if $(DDR3_FLYBY),DDR3_FLYBY=1) all +TFA_MAKE_FLAGS += \ + BOOT_DEVICE=$(BOOT_DEVICE) \ + USE_MKIMAGE=1 MKIMAGE=$(STAGING_DIR_HOST)/bin/mkimage \ + $(if $(DDR3_FLYBY),DDR3_FLYBY=1) \ + all define Build/Configure $(call Build/Configure/Default) diff --git a/package/boot/arm-trusted-firmware-mediatek/patches/001-make-brom-sign-key-optional-again.patch b/package/boot/arm-trusted-firmware-mediatek/patches/001-make-brom-sign-key-optional-again.patch deleted file mode 100644 index 60b221ad5f..0000000000 --- a/package/boot/arm-trusted-firmware-mediatek/patches/001-make-brom-sign-key-optional-again.patch +++ /dev/null @@ -1,17 +0,0 @@ -Index: arm-trusted-firmware-mediatek-2021-02-25-1220acb0/plat/mediatek/mt7622/platform.mk -=================================================================== ---- arm-trusted-firmware-mediatek-2021-02-25-1220acb0.orig/plat/mediatek/mt7622/platform.mk -+++ arm-trusted-firmware-mediatek-2021-02-25-1220acb0/plat/mediatek/mt7622/platform.mk -@@ -99,9 +99,9 @@ BL2_SOURCES += drivers/mmc/mmc.c \ - BROM_HEADER_TYPE := sdmmc - CPPFLAGS += -DMSDC_INDEX=1 - DEVICE_HEADER_OFFSET ?= 0x80000 --ifeq ($(BROM_SIGN_KEY),) --$(error BootROM signing key is required for SD booting. Please specify BROM_SIGN_KEY) --endif -+#ifeq ($(BROM_SIGN_KEY),) -+#$(error BootROM signing key is required for SD booting. Please specify BROM_SIGN_KEY) -+#endif - endif - ifeq ($(BOOT_DEVICE),snand) - include ${MTK_PLAT}/common/drivers/snfi/mtk-snand.mk diff --git a/package/boot/uboot-envtools/files/realtek b/package/boot/uboot-envtools/files/realtek index 9573e8944f..a91ca82604 100644 --- a/package/boot/uboot-envtools/files/realtek +++ b/package/boot/uboot-envtools/files/realtek @@ -11,6 +11,7 @@ case "$board" in d-link,dgs-1210-16|\ d-link,dgs-1210-28|\ d-link,dgs-1210-10p|\ +zyxel,gs1900-8|\ zyxel,gs1900-8hp-v1|\ zyxel,gs1900-8hp-v2|\ zyxel,gs1900-10hp) diff --git a/package/boot/uboot-mediatek/Makefile b/package/boot/uboot-mediatek/Makefile index 6904b39938..efdc25a195 100644 --- a/package/boot/uboot-mediatek/Makefile +++ b/package/boot/uboot-mediatek/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk -PKG_VERSION:=2020.10 -PKG_HASH:=0d481bbdc05c0ee74908ec2f56a6daa53166cc6a78a0e4fac2ac5d025770a622 +PKG_VERSION:=2021.04-rc3 +PKG_HASH:=7c418e07f6065c8761eb2df890bb524d7109864325d8850ddb0c93eb345734f9 PKG_BUILD_DEPENDS:=arm-trusted-firmware-tools/host include $(INCLUDE_DIR)/u-boot.mk diff --git a/package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch b/package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch new file mode 100644 index 0000000000..c398ae4ab7 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch @@ -0,0 +1,69 @@ +From 5efb7855a9d33ac897d6e2a7117e4e3d35d433a5 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 11 Mar 2021 10:28:53 +0000 +Subject: [PATCH 01/21] Revert "clk: Add debugging for return values" + +This reverts commit 5c5992cb90cf9ca4d51e38d9a95a13c293904df5. +--- + drivers/clk/clk-uclass.c | 16 +++++----------- + 1 file changed, 5 insertions(+), 11 deletions(-) + +--- a/drivers/clk/clk-uclass.c ++++ b/drivers/clk/clk-uclass.c +@@ -84,7 +84,7 @@ static int clk_get_by_index_tail(int ret + if (ret) { + debug("%s: uclass_get_device_by_of_offset failed: err=%d\n", + __func__, ret); +- return log_msg_ret("get", ret); ++ return ret; + } + + clk->dev = dev_clk; +@@ -97,15 +97,14 @@ static int clk_get_by_index_tail(int ret + ret = clk_of_xlate_default(clk, args); + if (ret) { + debug("of_xlate() failed: %d\n", ret); +- return log_msg_ret("xlate", ret); ++ return ret; + } + + return clk_request(dev_clk, clk); + err: + debug("%s: Node '%s', property '%s', failed to request CLK index %d: %d\n", + __func__, ofnode_get_name(node), list_name, index, ret); +- +- return log_msg_ret("prop", ret); ++ return ret; + } + + static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name, +@@ -124,7 +123,7 @@ static int clk_get_by_indexed_prop(struc + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", + __func__, ret); +- return log_ret(ret); ++ return ret; + } + + +@@ -472,7 +471,6 @@ int clk_free(struct clk *clk) + ulong clk_get_rate(struct clk *clk) + { + const struct clk_ops *ops; +- int ret; + + debug("%s(clk=%p)\n", __func__, clk); + if (!clk_valid(clk)) +@@ -482,11 +480,7 @@ ulong clk_get_rate(struct clk *clk) + if (!ops->get_rate) + return -ENOSYS; + +- ret = ops->get_rate(clk); +- if (ret) +- return log_ret(ret); +- +- return 0; ++ return ops->get_rate(clk); + } + + struct clk *clk_get_parent(struct clk *clk) diff --git a/package/boot/uboot-mediatek/patches/000-mtk-02-configs-RPi2-Disable-EFI-Grub-workaround.patch b/package/boot/uboot-mediatek/patches/000-mtk-02-configs-RPi2-Disable-EFI-Grub-workaround.patch new file mode 100644 index 0000000000..7b54489832 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-02-configs-RPi2-Disable-EFI-Grub-workaround.patch @@ -0,0 +1,21 @@ +From 04815ef5a49a9996acacfcb5e18924569f5e1bf5 Mon Sep 17 00:00:00 2001 +From: Matthias Brugger +Date: Tue, 16 Feb 2021 20:54:08 +0100 +Subject: [PATCH 02/21] configs: RPi2: Disable EFI Grub workaround + +The EFI Grub workaround isn't needed with Grub version 2.04 or higher. +This version was published over a year ago, so disable the workaround +to reduce boot time. + +Signed-off-by: Matthias Brugger +--- + configs/rpi_2_defconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/configs/rpi_2_defconfig ++++ b/configs/rpi_2_defconfig +@@ -42,3 +42,4 @@ CONFIG_SYS_WHITE_ON_BLACK=y + CONFIG_CONSOLE_SCROLL_LINES=10 + CONFIG_PHYS_TO_BUS=y + CONFIG_OF_LIBFDT_OVERLAY=y ++# CONFIG_EFI_GRUB_ARM32_WORKAROUND is not set diff --git a/package/boot/uboot-mediatek/patches/000-mtk-03-pinctrl-mediatek-fix-wrong-assignment-in-mtk_get_pin.patch b/package/boot/uboot-mediatek/patches/000-mtk-03-pinctrl-mediatek-fix-wrong-assignment-in-mtk_get_pin.patch new file mode 100644 index 0000000000..3aa6b6105f --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-03-pinctrl-mediatek-fix-wrong-assignment-in-mtk_get_pin.patch @@ -0,0 +1,25 @@ +From 6f18e581a7e98db3675b4c111701263647b20781 Mon Sep 17 00:00:00 2001 +From: Sam Shih +Date: Thu, 17 Dec 2020 19:29:56 +0800 +Subject: [PATCH 03/21] pinctrl: mediatek: fix wrong assignment in + mtk_get_pin_name + +This is a bug fix for mtk pinctrl common part. Appearently pins should be +used instead of grps in mtk_get_pin_name(). + +Signed-off-by: Sam Shih +--- + drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c ++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +@@ -219,7 +219,7 @@ static const char *mtk_get_pin_name(stru + { + struct mtk_pinctrl_priv *priv = dev_get_priv(dev); + +- if (!priv->soc->grps[selector].name) ++ if (!priv->soc->pins[selector].name) + return mtk_pinctrl_dummy_name; + + return priv->soc->pins[selector].name; diff --git a/package/boot/uboot-mediatek/patches/000-mtk-04-pinctrl-mediatek-add-get_pin_muxing-ops-for-mediatek.patch b/package/boot/uboot-mediatek/patches/000-mtk-04-pinctrl-mediatek-add-get_pin_muxing-ops-for-mediatek.patch new file mode 100644 index 0000000000..89b51f7e81 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-04-pinctrl-mediatek-add-get_pin_muxing-ops-for-mediatek.patch @@ -0,0 +1,43 @@ +From ca73da39ff0c9f599f75d7ccac0196030aa946b9 Mon Sep 17 00:00:00 2001 +From: Sam Shih +Date: Thu, 17 Dec 2020 19:32:48 +0800 +Subject: [PATCH 04/21] pinctrl: mediatek: add get_pin_muxing ops for mediatek + pinctrl + +This patch add get_pin_muxing support for mediatek pinctrl drivers + +Signed-off-by: Sam Shih +--- + drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c ++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +@@ -232,6 +232,19 @@ static int mtk_get_pins_count(struct ude + return priv->soc->npins; + } + ++static int mtk_get_pin_muxing(struct udevice *dev, ++ unsigned int selector, ++ char *buf, int size) ++{ ++ int val, err; ++ err = mtk_hw_get_value(dev, selector, PINCTRL_PIN_REG_MODE, &val); ++ if (err) ++ return err; ++ ++ snprintf(buf, size, "Aux Func.%d", val); ++ return 0; ++} ++ + static const char *mtk_get_group_name(struct udevice *dev, + unsigned int selector) + { +@@ -512,6 +525,7 @@ static int mtk_pinconf_group_set(struct + const struct pinctrl_ops mtk_pinctrl_ops = { + .get_pins_count = mtk_get_pins_count, + .get_pin_name = mtk_get_pin_name, ++ .get_pin_muxing = mtk_get_pin_muxing, + .get_groups_count = mtk_get_groups_count, + .get_group_name = mtk_get_group_name, + .get_functions_count = mtk_get_functions_count, diff --git a/package/boot/uboot-mediatek/patches/000-mtk-05-pinctrl-mediatek-do-not-probe-gpio-driver-if-not-ena.patch b/package/boot/uboot-mediatek/patches/000-mtk-05-pinctrl-mediatek-do-not-probe-gpio-driver-if-not-ena.patch new file mode 100644 index 0000000000..7a98e88843 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-05-pinctrl-mediatek-do-not-probe-gpio-driver-if-not-ena.patch @@ -0,0 +1,58 @@ +From d3fbbef13853a695cdea75a980a3d6bd150a68c1 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 11 Jan 2021 10:17:15 +0800 +Subject: [PATCH 05/21] pinctrl: mediatek: do not probe gpio driver if not + enabled + +The mtk pinctrl driver is a combination driver with support for both +pinctrl and gpio. When this driver is used in SPL, gpio support may not be +enabled, and this will result in a compilation error. + +To fix this, macros are added to make sure gpio related code will only be +compiled when gpio support is enabled. + +Signed-off-by: Weijie Gao +--- + drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c ++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +@@ -540,6 +540,8 @@ const struct pinctrl_ops mtk_pinctrl_ops + .set_state = pinctrl_generic_set_state, + }; + ++#if CONFIG_IS_ENABLED(DM_GPIO) || \ ++ (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO_SUPPORT)) + static int mtk_gpio_get(struct udevice *dev, unsigned int off) + { + int val, err; +@@ -647,12 +649,13 @@ static int mtk_gpiochip_register(struct + + return 0; + } ++#endif + + int mtk_pinctrl_common_probe(struct udevice *dev, + struct mtk_pinctrl_soc *soc) + { + struct mtk_pinctrl_priv *priv = dev_get_priv(dev); +- int ret; ++ int ret = 0; + + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) +@@ -660,9 +663,10 @@ int mtk_pinctrl_common_probe(struct udev + + priv->soc = soc; + ++#if CONFIG_IS_ENABLED(DM_GPIO) || \ ++ (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO_SUPPORT)) + ret = mtk_gpiochip_register(dev); +- if (ret) +- return ret; ++#endif + +- return 0; ++ return ret; + } diff --git a/package/boot/uboot-mediatek/patches/000-mtk-06-pinctrl-mt7629-add-jtag-function-and-pin-group.patch b/package/boot/uboot-mediatek/patches/000-mtk-06-pinctrl-mt7629-add-jtag-function-and-pin-group.patch new file mode 100644 index 0000000000..f4cc1eb69d --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-06-pinctrl-mt7629-add-jtag-function-and-pin-group.patch @@ -0,0 +1,50 @@ +From 1c6d07abf7fc79bf3950dc9ac56e3b566c334d3d Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 13 Jan 2021 16:29:23 +0800 +Subject: [PATCH 06/21] pinctrl: mt7629: add jtag function and pin group + +The EPHY LEDs of mt7629 can be used as JTAG. This patch adds the jtag pin +group to the pinctrl driver. + +Signed-off-by: Weijie Gao +--- + drivers/pinctrl/mediatek/pinctrl-mt7629.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/pinctrl/mediatek/pinctrl-mt7629.c ++++ b/drivers/pinctrl/mediatek/pinctrl-mt7629.c +@@ -201,6 +201,10 @@ static int mt7629_wf2g_led_funcs[] = { 1 + static int mt7629_wf5g_led_pins[] = { 18, }; + static int mt7629_wf5g_led_funcs[] = { 1, }; + ++/* LED for EPHY used as JTAG */ ++static int mt7629_ephy_leds_jtag_pins[] = { 12, 13, 14, 15, 16, }; ++static int mt7629_ephy_leds_jtag_funcs[] = { 7, 7, 7, 7, 7, }; ++ + /* Watchdog */ + static int mt7629_watchdog_pins[] = { 11, }; + static int mt7629_watchdog_funcs[] = { 1, }; +@@ -297,6 +301,7 @@ static const struct mtk_group_desc mt762 + PINCTRL_PIN_GROUP("ephy_led2", mt7629_ephy_led2), + PINCTRL_PIN_GROUP("ephy_led3", mt7629_ephy_led3), + PINCTRL_PIN_GROUP("ephy_led4", mt7629_ephy_led4), ++ PINCTRL_PIN_GROUP("ephy_leds_jtag", mt7629_ephy_leds_jtag), + PINCTRL_PIN_GROUP("wf2g_led", mt7629_wf2g_led), + PINCTRL_PIN_GROUP("wf5g_led", mt7629_wf5g_led), + PINCTRL_PIN_GROUP("watchdog", mt7629_watchdog), +@@ -364,6 +369,7 @@ static const char *const mt7629_uart_gro + static const char *const mt7629_wdt_groups[] = { "watchdog", }; + static const char *const mt7629_wifi_groups[] = { "wf0_5g", "wf0_2g", }; + static const char *const mt7629_flash_groups[] = { "snfi", "spi_nor" }; ++static const char *const mt7629_jtag_groups[] = { "ephy_leds_jtag" }; + + static const struct mtk_function_desc mt7629_functions[] = { + {"eth", mt7629_ethernet_groups, ARRAY_SIZE(mt7629_ethernet_groups)}, +@@ -376,6 +382,7 @@ static const struct mtk_function_desc mt + {"watchdog", mt7629_wdt_groups, ARRAY_SIZE(mt7629_wdt_groups)}, + {"wifi", mt7629_wifi_groups, ARRAY_SIZE(mt7629_wifi_groups)}, + {"flash", mt7629_flash_groups, ARRAY_SIZE(mt7629_flash_groups)}, ++ {"jtag", mt7629_jtag_groups, ARRAY_SIZE(mt7629_jtag_groups)}, + }; + + static struct mtk_pinctrl_soc mt7629_data = { diff --git a/package/boot/uboot-mediatek/patches/000-mtk-07-configs-mt7622-use-ARMv8-Generic-Timer-instead-of-mt.patch b/package/boot/uboot-mediatek/patches/000-mtk-07-configs-mt7622-use-ARMv8-Generic-Timer-instead-of-mt.patch new file mode 100644 index 0000000000..060c9aadbf --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-07-configs-mt7622-use-ARMv8-Generic-Timer-instead-of-mt.patch @@ -0,0 +1,25 @@ +From c47a5b927787a463eff8f67339d91e60fe0381c4 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 2 Mar 2021 15:02:50 +0800 +Subject: [PATCH 07/21] configs: mt7622: use ARMv8 Generic Timer instead of + mtk_timer + +It's better to use the generic timer which is correctly initialized by +the ATF. The generic timer has higher resolution than the mtk_timer. + +Signed-off-by: Weijie Gao +--- + configs/mt7622_rfb_defconfig | 2 -- + 1 file changed, 2 deletions(-) + +--- a/configs/mt7622_rfb_defconfig ++++ b/configs/mt7622_rfb_defconfig +@@ -51,8 +51,6 @@ CONFIG_SPI=y + CONFIG_DM_SPI=y + CONFIG_MTK_SNOR=y + CONFIG_SYSRESET_WATCHDOG=y +-CONFIG_TIMER=y +-CONFIG_MTK_TIMER=y + CONFIG_WDT_MTK=y + CONFIG_LZO=y + CONFIG_HEXDUMP=y diff --git a/package/boot/uboot-mediatek/patches/000-mtk-08-dts-mt7629-enable-JTAG-pins-by-default.patch b/package/boot/uboot-mediatek/patches/000-mtk-08-dts-mt7629-enable-JTAG-pins-by-default.patch new file mode 100644 index 0000000000..f9f783ef37 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-08-dts-mt7629-enable-JTAG-pins-by-default.patch @@ -0,0 +1,50 @@ +From 4bee3f9e285007ccf77ca9916fff3d93fc4d8a80 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 2 Mar 2021 15:43:27 +0800 +Subject: [PATCH 08/21] dts: mt7629: enable JTAG pins by default + +The EPHY LEDs belongs to the built-in FE switch of MT7629, which is barely +used. These LED pins on reference boards are used as JTAG socket. So it's +a good idea to change the default state to JTAG, and this will make it +convenience for debugging. + +Signed-off-by: Weijie Gao +--- + arch/arm/dts/mt7629-rfb.dts | 10 ++++++++++ + arch/arm/dts/mt7629.dtsi | 6 ++++++ + 2 files changed, 16 insertions(+) + +--- a/arch/arm/dts/mt7629-rfb.dts ++++ b/arch/arm/dts/mt7629-rfb.dts +@@ -36,6 +36,16 @@ + }; + + &pinctrl { ++ state_default: pinmux_conf { ++ u-boot,dm-pre-reloc; ++ ++ mux { ++ function = "jtag"; ++ groups = "ephy_leds_jtag"; ++ u-boot,dm-pre-reloc; ++ }; ++ }; ++ + snfi_pins: snfi-pins { + mux { + function = "flash"; +--- a/arch/arm/dts/mt7629.dtsi ++++ b/arch/arm/dts/mt7629.dtsi +@@ -152,6 +152,12 @@ + compatible = "mediatek,mt7629-pinctrl"; + reg = <0x10217000 0x8000>; + ++ pinctrl-names = "default"; ++ pinctrl-0 = <&state_default>; ++ ++ state_default: pinmux_conf { ++ }; ++ + gpio: gpio-controller { + gpio-controller; + #gpio-cells = <2>; diff --git a/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch b/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch new file mode 100644 index 0000000000..56a40ca0e1 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch @@ -0,0 +1,44 @@ +From f3f320af7078a8c5647d870a31c1d3695dacd7cf Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 2 Mar 2021 15:47:45 +0800 +Subject: [PATCH 09/21] board: mediatek: add more network configurations + +Make the network configurations uniform for mediatek boards + +Signed-off-by: Weijie Gao +--- + include/configs/mt7622.h | 3 ++- + include/configs/mt7623.h | 1 + + include/configs/mt7629.h | 1 + + 3 files changed, 4 insertions(+), 1 deletion(-) + +--- a/include/configs/mt7622.h ++++ b/include/configs/mt7622.h +@@ -36,6 +36,7 @@ + + /* Ethernet */ + #define CONFIG_IPADDR 192.168.1.1 +-#define CONFIG_SERVERIP 192.168.1.3 ++#define CONFIG_SERVERIP 192.168.1.2 ++#define CONFIG_NETMASK 255.255.255.0 + + #endif +--- a/include/configs/mt7623.h ++++ b/include/configs/mt7623.h +@@ -54,6 +54,7 @@ + /* Ethernet */ + #define CONFIG_IPADDR 192.168.1.1 + #define CONFIG_SERVERIP 192.168.1.2 ++#define CONFIG_NETMASK 255.255.255.0 + + #ifdef CONFIG_DISTRO_DEFAULTS + +--- a/include/configs/mt7629.h ++++ b/include/configs/mt7629.h +@@ -52,5 +52,6 @@ + /* Ethernet */ + #define CONFIG_IPADDR 192.168.1.1 + #define CONFIG_SERVERIP 192.168.1.2 ++#define CONFIG_NETMASK 255.255.255.0 + + #endif diff --git a/package/boot/uboot-mediatek/patches/000-mtk-10-mmc-mtk-sd-increase-the-minimum-bus-frequency.patch b/package/boot/uboot-mediatek/patches/000-mtk-10-mmc-mtk-sd-increase-the-minimum-bus-frequency.patch new file mode 100644 index 0000000000..658cbc1232 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-10-mmc-mtk-sd-increase-the-minimum-bus-frequency.patch @@ -0,0 +1,38 @@ +From ed880b7572e1135e3bd8382d4670a375f7d9c91b Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 2 Mar 2021 15:56:17 +0800 +Subject: [PATCH 10/21] mmc: mtk-sd: increase the minimum bus frequency + +With a 48MHz input clock, the lowest bus frequency can be as low as +48000000 / (4 * 4095) = 2930Hz. Such an extremely low frequency will cause +the mmc framework take seconds to finish the initialization. + +Limiting the minimum bus frequency to a slightly higher value can solve the +issue without any side effects. + +Signed-off-by: Weijie Gao +--- + drivers/mmc/mtk-sd.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/mmc/mtk-sd.c ++++ b/drivers/mmc/mtk-sd.c +@@ -232,6 +232,8 @@ + + #define SCLK_CYCLES_SHIFT 20 + ++#define MIN_BUS_CLK 260000 ++ + #define CMD_INTS_MASK \ + (MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO) + +@@ -1639,6 +1641,9 @@ static int msdc_drv_probe(struct udevice + else + cfg->f_min = host->src_clk_freq / (4 * 4095); + ++ if (cfg->f_min < MIN_BUS_CLK) ++ cfg->f_min = MIN_BUS_CLK; ++ + cfg->f_max = host->src_clk_freq; + + cfg->b_max = 1024; diff --git a/package/boot/uboot-mediatek/patches/000-mtk-11-serial-serial-mtk-rewrite-the-setbrg-function.patch b/package/boot/uboot-mediatek/patches/000-mtk-11-serial-serial-mtk-rewrite-the-setbrg-function.patch new file mode 100644 index 0000000000..2ce7733314 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-11-serial-serial-mtk-rewrite-the-setbrg-function.patch @@ -0,0 +1,141 @@ +From d8bde59186dafdea5bbe8d29d3a6ae7cac98e9d0 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jan 2021 11:19:08 +0800 +Subject: [PATCH 11/21] serial: serial-mtk: rewrite the setbrg function + +Currently the setbrg logic of serial-mtk is messy, and should be rewritten. +Also an option is added to make it possible to use highspeed=3 mode for all +bauds. + +The new logic is: +1. If baud clock > 12MHz + a) If baud <= 115200, highspeed=0 mode will be used (ns16550 compatible) + b) If baud <= 576000, highspeed=2 mode will be used + c) any baud > 576000, highspeed=3 mode will be used +2. If baud clock <= 12MHz + Always uses highspeed=3 mode + a) If baud <= 115200, calculates the divisor using DIV_ROUND_CLOSEST + b) any baud > 115200, the same as 1. c) + +Signed-off-by: Weijie Gao +--- + drivers/serial/serial_mtk.c | 74 +++++++++++++++++-------------------- + 1 file changed, 33 insertions(+), 41 deletions(-) + +--- a/drivers/serial/serial_mtk.c ++++ b/drivers/serial/serial_mtk.c +@@ -73,74 +73,64 @@ struct mtk_serial_regs { + struct mtk_serial_priv { + struct mtk_serial_regs __iomem *regs; + u32 clock; ++ bool force_highspeed; + }; + + static void _mtk_serial_setbrg(struct mtk_serial_priv *priv, int baud) + { +- bool support_clk12m_baud115200; +- u32 quot, samplecount, realbaud; ++ u32 quot, realbaud, samplecount = 1; + +- if ((baud <= 115200) && (priv->clock == 12000000)) +- support_clk12m_baud115200 = true; +- else +- support_clk12m_baud115200 = false; ++ /* Special case for low baud clock */ ++ if ((baud <= 115200) && (priv->clock == 12000000)) { ++ writel(3, &priv->regs->highspeed); ++ ++ quot = DIV_ROUND_CLOSEST(priv->clock, 256 * baud); ++ if (quot == 0) ++ quot = 1; ++ ++ samplecount = DIV_ROUND_CLOSEST(priv->clock, quot * baud); ++ ++ realbaud = priv->clock / samplecount / quot; ++ if ((realbaud > BAUD_ALLOW_MAX(baud)) || ++ (realbaud < BAUD_ALLOW_MIX(baud))) { ++ pr_info("baud %d can't be handled\n", baud); ++ } ++ ++ goto set_baud; ++ } ++ ++ if (priv->force_highspeed) ++ goto use_hs3; + + if (baud <= 115200) { + writel(0, &priv->regs->highspeed); + quot = DIV_ROUND_CLOSEST(priv->clock, 16 * baud); +- +- if (support_clk12m_baud115200) { +- writel(3, &priv->regs->highspeed); +- quot = DIV_ROUND_CLOSEST(priv->clock, 256 * baud); +- if (quot == 0) +- quot = 1; +- +- samplecount = DIV_ROUND_CLOSEST(priv->clock, +- quot * baud); +- if (samplecount != 0) { +- realbaud = priv->clock / samplecount / quot; +- if ((realbaud > BAUD_ALLOW_MAX(baud)) || +- (realbaud < BAUD_ALLOW_MIX(baud))) { +- pr_info("baud %d can't be handled\n", +- baud); +- } +- } else { +- pr_info("samplecount is 0\n"); +- } +- } + } else if (baud <= 576000) { + writel(2, &priv->regs->highspeed); + + /* Set to next lower baudrate supported */ + if ((baud == 500000) || (baud == 576000)) + baud = 460800; ++ + quot = DIV_ROUND_UP(priv->clock, 4 * baud); + } else { ++use_hs3: + writel(3, &priv->regs->highspeed); ++ + quot = DIV_ROUND_UP(priv->clock, 256 * baud); ++ samplecount = DIV_ROUND_CLOSEST(priv->clock, quot * baud); + } + ++set_baud: + /* set divisor */ + writel(UART_LCR_WLS_8 | UART_LCR_DLAB, &priv->regs->lcr); + writel(quot & 0xff, &priv->regs->dll); + writel((quot >> 8) & 0xff, &priv->regs->dlm); + writel(UART_LCR_WLS_8, &priv->regs->lcr); + +- if (baud > 460800) { +- u32 tmp; +- +- tmp = DIV_ROUND_CLOSEST(priv->clock, quot * baud); +- writel(tmp - 1, &priv->regs->sample_count); +- writel((tmp - 2) >> 1, &priv->regs->sample_point); +- } else { +- writel(0, &priv->regs->sample_count); +- writel(0xff, &priv->regs->sample_point); +- } +- +- if (support_clk12m_baud115200) { +- writel(samplecount - 1, &priv->regs->sample_count); +- writel((samplecount - 2) >> 1, &priv->regs->sample_point); +- } ++ /* set highspeed mode sample count & point */ ++ writel(samplecount - 1, &priv->regs->sample_count); ++ writel((samplecount - 2) >> 1, &priv->regs->sample_point); + } + + static int _mtk_serial_putc(struct mtk_serial_priv *priv, const char ch) +@@ -248,6 +238,8 @@ static int mtk_serial_of_to_plat(struct + return -EINVAL; + } + ++ priv->force_highspeed = dev_read_bool(dev, "mediatek,force-highspeed"); ++ + return 0; + } + diff --git a/package/boot/uboot-mediatek/patches/000-mtk-12-board-mt7629-enable-compression-of-u-boot-to-reduce-.patch b/package/boot/uboot-mediatek/patches/000-mtk-12-board-mt7629-enable-compression-of-u-boot-to-reduce-.patch new file mode 100644 index 0000000000..3b5545db15 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-12-board-mt7629-enable-compression-of-u-boot-to-reduce-.patch @@ -0,0 +1,94 @@ +From a80ef99cb308904b82662deb66c5ed7a6ff59928 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 11:13:36 +0800 +Subject: [PATCH 12/21] board: mt7629: enable compression of u-boot to reduce + the size of final image + +This patch makes use of the decompression mechanism implemented for mt7628 +previously to reduce the total image size. Binman will be also removed. + +Signed-off-by: Weijie Gao +--- + Makefile | 3 +++ + arch/arm/dts/mt7629-rfb-u-boot.dtsi | 18 ------------------ + arch/arm/mach-mediatek/Kconfig | 1 - + configs/mt7629_rfb_defconfig | 6 ++++++ + 4 files changed, 9 insertions(+), 19 deletions(-) + +--- a/Makefile ++++ b/Makefile +@@ -1728,6 +1728,9 @@ u-boot-elf.lds: arch/u-boot-elf.lds prep + + ifeq ($(CONFIG_SPL),y) + spl/u-boot-spl-mtk.bin: spl/u-boot-spl ++ ++u-boot-mtk.bin: u-boot-with-spl.bin ++ $(call if_changed,copy) + else + MKIMAGEFLAGS_u-boot-mtk.bin = -T mtk_image \ + -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) \ +--- a/arch/arm/dts/mt7629-rfb-u-boot.dtsi ++++ b/arch/arm/dts/mt7629-rfb-u-boot.dtsi +@@ -5,24 +5,6 @@ + * Author: Weijie Gao + */ + +-#include +-/ { +- binman { +- filename = "u-boot-mtk.bin"; +- pad-byte = <0xff>; +- +-#ifdef CONFIG_SPL +- blob { +- filename = "spl/u-boot-spl-mtk.bin"; +- size = ; +- }; +- +- u-boot-img { +- }; +-#endif +- }; +-}; +- + &infracfg { + u-boot,dm-pre-reloc; + }; +--- a/arch/arm/mach-mediatek/Kconfig ++++ b/arch/arm/mach-mediatek/Kconfig +@@ -36,7 +36,6 @@ config TARGET_MT7629 + bool "MediaTek MT7629 SoC" + select CPU_V7A + select SPL +- select BINMAN + help + The MediaTek MT7629 is a ARM-based SoC with a dual-core Cortex-A7 + including DDR3, crypto engine, 3x3 11n/ac Wi-Fi, Gigabit Ethernet, +--- a/configs/mt7629_rfb_defconfig ++++ b/configs/mt7629_rfb_defconfig +@@ -10,7 +10,11 @@ CONFIG_SPL_TEXT_BASE=0x201000 + CONFIG_TARGET_MT7629=y + CONFIG_SPL_SERIAL_SUPPORT=y + CONFIG_SPL_DRIVERS_MISC_SUPPORT=y ++CONFIG_SPL_STACK_R_ADDR=0x40800000 ++CONFIG_SPL_PAYLOAD="u-boot-lzma.img" ++CONFIG_BUILD_TARGET="u-boot-mtk.bin" + CONFIG_DEFAULT_DEVICE_TREE="mt7629-rfb" ++CONFIG_SPL_IMAGE="spl/u-boot-spl-mtk.bin" + CONFIG_FIT=y + CONFIG_FIT_VERBOSE=y + CONFIG_BOOTDELAY=3 +@@ -18,6 +22,7 @@ CONFIG_DEFAULT_FDT_FILE="mt7629-rfb" + CONFIG_SYS_CONSOLE_IS_IN_ENV=y + # CONFIG_DISPLAY_BOARDINFO is not set + CONFIG_SPL_SYS_MALLOC_SIMPLE=y ++CONFIG_SPL_STACK_R=y + CONFIG_SPL_NOR_SUPPORT=y + CONFIG_SPL_WATCHDOG_SUPPORT=y + CONFIG_HUSH_PARSER=y +@@ -87,4 +92,5 @@ CONFIG_USB_STORAGE=y + CONFIG_USB_KEYBOARD=y + CONFIG_WDT_MTK=y + CONFIG_LZMA=y ++CONFIG_SPL_LZMA=y + # CONFIG_EFI_LOADER is not set diff --git a/package/boot/uboot-mediatek/patches/000-mtk-13-configs-mt7622-enable-debug-uart-for-mt7622_rfb_defc.patch b/package/boot/uboot-mediatek/patches/000-mtk-13-configs-mt7622-enable-debug-uart-for-mt7622_rfb_defc.patch new file mode 100644 index 0000000000..6b832148b9 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-13-configs-mt7622-enable-debug-uart-for-mt7622_rfb_defc.patch @@ -0,0 +1,26 @@ +From acd49b1549ff52286aace5e841420aa750325f8b Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 10:53:14 +0800 +Subject: [PATCH 13/21] configs: mt7622: enable debug uart for + mt7622_rfb_defconfig + +Enable debug uart for mt7622_rfb_defconfig + +Signed-off-by: Weijie Gao +--- + configs/mt7622_rfb_defconfig | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/configs/mt7622_rfb_defconfig ++++ b/configs/mt7622_rfb_defconfig +@@ -4,7 +4,10 @@ CONFIG_ARCH_MEDIATEK=y + CONFIG_SYS_TEXT_BASE=0x41e00000 + CONFIG_SYS_MALLOC_F_LEN=0x4000 + CONFIG_NR_DRAM_BANKS=1 ++CONFIG_DEBUG_UART_BASE=0x11002000 ++CONFIG_DEBUG_UART_CLOCK=25000000 + CONFIG_DEFAULT_DEVICE_TREE="mt7622-rfb" ++CONFIG_DEBUG_UART=y + CONFIG_FIT=y + CONFIG_DEFAULT_FDT_FILE="mt7622-rfb" + CONFIG_LOGLEVEL=7 diff --git a/package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch b/package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch new file mode 100644 index 0000000000..a01d9e2b17 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch @@ -0,0 +1,3698 @@ +From f22a055a9f589f1ec614045eba3cb0c5fd887feb Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 2 Mar 2021 16:58:01 +0800 +Subject: [PATCH 14/21] drivers: mtd: add support for MediaTek SPI-NAND flash + controller + +Add mtd driver for MediaTek SPI-NAND flash controller + +This driver is written from scratch, and uses standard mtd framework, not +the nand framework which only applies for raw parallel nand flashes so that +this driver can have a smaller size in binary. + +Signed-off-by: Weijie Gao +--- + drivers/mtd/Kconfig | 2 + + drivers/mtd/Makefile | 2 + + drivers/mtd/mtk-snand/Kconfig | 21 + + drivers/mtd/mtk-snand/Makefile | 11 + + drivers/mtd/mtk-snand/mtk-snand-def.h | 266 ++++ + drivers/mtd/mtk-snand/mtk-snand-ecc.c | 264 ++++ + drivers/mtd/mtk-snand/mtk-snand-ids.c | 511 +++++++ + drivers/mtd/mtk-snand/mtk-snand-mtd.c | 526 ++++++++ + drivers/mtd/mtk-snand/mtk-snand-os.c | 39 + + drivers/mtd/mtk-snand/mtk-snand-os.h | 120 ++ + drivers/mtd/mtk-snand/mtk-snand.c | 1776 +++++++++++++++++++++++++ + drivers/mtd/mtk-snand/mtk-snand.h | 77 ++ + 12 files changed, 3615 insertions(+) + create mode 100644 drivers/mtd/mtk-snand/Kconfig + create mode 100644 drivers/mtd/mtk-snand/Makefile + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-def.h + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-ecc.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-ids.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-mtd.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-os.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-os.h + create mode 100644 drivers/mtd/mtk-snand/mtk-snand.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand.h + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -108,6 +108,8 @@ config HBMC_AM654 + This is the driver for HyperBus controller on TI's AM65x and + other SoCs + ++source "drivers/mtd/mtk-snand/Kconfig" ++ + source "drivers/mtd/nand/Kconfig" + + source "drivers/mtd/spi/Kconfig" +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -40,3 +40,5 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPOR + obj-$(CONFIG_SPL_UBI) += ubispl/ + + endif ++ ++obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/ +--- /dev/null ++++ b/drivers/mtd/mtk-snand/Kconfig +@@ -0,0 +1,21 @@ ++# ++# Copyright (C) 2020 MediaTek Inc. All rights reserved. ++# Author: Weijie Gao ++# ++# SPDX-License-Identifier: GPL-2.0 ++# ++ ++config MTK_SPI_NAND ++ tristate "MediaTek SPI NAND flash controller driver" ++ depends on !MTD_SPI_NAND ++ help ++ This option enables access to SPI-NAND flashes through the ++ MediaTek SPI NAND Flash Controller ++ ++config MTK_SPI_NAND_MTD ++ tristate "MTD support for MediaTek SPI NAND flash controller" ++ depends on DM_MTD ++ depends on MTK_SPI_NAND ++ help ++ This option enables access to SPI-NAND flashes through the ++ MTD interface of MediaTek SPI NAND Flash Controller +--- /dev/null ++++ b/drivers/mtd/mtk-snand/Makefile +@@ -0,0 +1,11 @@ ++# ++# Copyright (C) 2020 MediaTek Inc. All rights reserved. ++# Author: Weijie Gao ++# ++# SPDX-License-Identifier: GPL-2.0 ++# ++ ++obj-y += mtk-snand.o mtk-snand-ecc.o mtk-snand-ids.o mtk-snand-os.o ++obj-$(CONFIG_MTK_SPI_NAND_MTD) += mtk-snand-mtd.o ++ ++ccflags-y += -DPRIVATE_MTK_SNAND_HEADER +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-def.h +@@ -0,0 +1,266 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _MTK_SNAND_DEF_H_ ++#define _MTK_SNAND_DEF_H_ ++ ++#include "mtk-snand-os.h" ++ ++#ifdef PRIVATE_MTK_SNAND_HEADER ++#include "mtk-snand.h" ++#else ++#include ++#endif ++ ++struct mtk_snand_plat_dev; ++ ++enum snand_flash_io { ++ SNAND_IO_1_1_1, ++ SNAND_IO_1_1_2, ++ SNAND_IO_1_2_2, ++ SNAND_IO_1_1_4, ++ SNAND_IO_1_4_4, ++ ++ __SNAND_IO_MAX ++}; ++ ++#define SPI_IO_1_1_1 BIT(SNAND_IO_1_1_1) ++#define SPI_IO_1_1_2 BIT(SNAND_IO_1_1_2) ++#define SPI_IO_1_2_2 BIT(SNAND_IO_1_2_2) ++#define SPI_IO_1_1_4 BIT(SNAND_IO_1_1_4) ++#define SPI_IO_1_4_4 BIT(SNAND_IO_1_4_4) ++ ++struct snand_opcode { ++ uint8_t opcode; ++ uint8_t dummy; ++}; ++ ++struct snand_io_cap { ++ uint8_t caps; ++ struct snand_opcode opcodes[__SNAND_IO_MAX]; ++}; ++ ++#define SNAND_OP(_io, _opcode, _dummy) [_io] = { .opcode = (_opcode), \ ++ .dummy = (_dummy) } ++ ++#define SNAND_IO_CAP(_name, _caps, ...) \ ++ struct snand_io_cap _name = { .caps = (_caps), \ ++ .opcodes = { __VA_ARGS__ } } ++ ++#define SNAND_MAX_ID_LEN 4 ++ ++enum snand_id_type { ++ SNAND_ID_DYMMY, ++ SNAND_ID_ADDR = SNAND_ID_DYMMY, ++ SNAND_ID_DIRECT, ++ ++ __SNAND_ID_TYPE_MAX ++}; ++ ++struct snand_id { ++ uint8_t type; /* enum snand_id_type */ ++ uint8_t len; ++ uint8_t id[SNAND_MAX_ID_LEN]; ++}; ++ ++#define SNAND_ID(_type, ...) \ ++ { .type = (_type), .id = { __VA_ARGS__ }, \ ++ .len = sizeof((uint8_t[]) { __VA_ARGS__ }) } ++ ++struct snand_mem_org { ++ uint16_t pagesize; ++ uint16_t sparesize; ++ uint16_t pages_per_block; ++ uint16_t blocks_per_die; ++ uint16_t planes_per_die; ++ uint16_t ndies; ++}; ++ ++#define SNAND_MEMORG(_ps, _ss, _ppb, _bpd, _ppd, _nd) \ ++ { .pagesize = (_ps), .sparesize = (_ss), .pages_per_block = (_ppb), \ ++ .blocks_per_die = (_bpd), .planes_per_die = (_ppd), .ndies = (_nd) } ++ ++typedef int (*snand_select_die_t)(struct mtk_snand *snf, uint32_t dieidx); ++ ++struct snand_flash_info { ++ const char *model; ++ struct snand_id id; ++ const struct snand_mem_org memorg; ++ const struct snand_io_cap *cap_rd; ++ const struct snand_io_cap *cap_pl; ++ snand_select_die_t select_die; ++}; ++ ++#define SNAND_INFO(_model, _id, _memorg, _cap_rd, _cap_pl, ...) \ ++ { .model = (_model), .id = _id, .memorg = _memorg, \ ++ .cap_rd = (_cap_rd), .cap_pl = (_cap_pl), __VA_ARGS__ } ++ ++const struct snand_flash_info *snand_flash_id_lookup(enum snand_id_type type, ++ const uint8_t *id); ++ ++struct mtk_snand_soc_data { ++ uint16_t sector_size; ++ uint16_t max_sectors; ++ uint16_t fdm_size; ++ uint16_t fdm_ecc_size; ++ uint16_t fifo_size; ++ ++ bool bbm_swap; ++ bool empty_page_check; ++ uint32_t mastersta_mask; ++ ++ const uint8_t *spare_sizes; ++ uint32_t num_spare_size; ++}; ++ ++enum mtk_ecc_regs { ++ ECC_DECDONE, ++}; ++ ++struct mtk_ecc_soc_data { ++ const uint8_t *ecc_caps; ++ uint32_t num_ecc_cap; ++ const uint32_t *regs; ++ uint16_t mode_shift; ++ uint8_t errnum_bits; ++ uint8_t errnum_shift; ++}; ++ ++struct mtk_snand { ++ struct mtk_snand_plat_dev *pdev; ++ ++ void __iomem *nfi_base; ++ void __iomem *ecc_base; ++ ++ enum mtk_snand_soc soc; ++ const struct mtk_snand_soc_data *nfi_soc; ++ const struct mtk_ecc_soc_data *ecc_soc; ++ bool snfi_quad_spi; ++ bool quad_spi_op; ++ ++ const char *model; ++ uint64_t size; ++ uint64_t die_size; ++ uint32_t erasesize; ++ uint32_t writesize; ++ uint32_t oobsize; ++ ++ uint32_t num_dies; ++ snand_select_die_t select_die; ++ ++ uint8_t opcode_rfc; ++ uint8_t opcode_pl; ++ uint8_t dummy_rfc; ++ uint8_t mode_rfc; ++ uint8_t mode_pl; ++ ++ uint32_t writesize_mask; ++ uint32_t writesize_shift; ++ uint32_t erasesize_mask; ++ uint32_t erasesize_shift; ++ uint64_t die_mask; ++ uint32_t die_shift; ++ ++ uint32_t spare_per_sector; ++ uint32_t raw_sector_size; ++ uint32_t ecc_strength; ++ uint32_t ecc_steps; ++ uint32_t ecc_bytes; ++ uint32_t ecc_parity_bits; ++ ++ uint8_t *page_cache; /* Used by read/write page */ ++ uint8_t *buf_cache; /* Used by block bad/markbad & auto_oob */ ++}; ++ ++enum mtk_snand_log_category { ++ SNAND_LOG_NFI, ++ SNAND_LOG_SNFI, ++ SNAND_LOG_ECC, ++ SNAND_LOG_CHIP, ++ ++ __SNAND_LOG_CAT_MAX ++}; ++ ++int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes, ++ uint32_t msg_size); ++int mtk_snand_ecc_encoder_start(struct mtk_snand *snf); ++void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf); ++int mtk_snand_ecc_decoder_start(struct mtk_snand *snf); ++void mtk_snand_ecc_decoder_stop(struct mtk_snand *snf); ++int mtk_ecc_wait_decoder_done(struct mtk_snand *snf); ++int mtk_ecc_check_decode_error(struct mtk_snand *snf, uint32_t page); ++ ++int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen, ++ uint8_t *in, uint32_t inlen); ++int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val); ++ ++int mtk_snand_log(struct mtk_snand_plat_dev *pdev, ++ enum mtk_snand_log_category cat, const char *fmt, ...); ++ ++#define snand_log_nfi(pdev, fmt, ...) \ ++ mtk_snand_log(pdev, SNAND_LOG_NFI, fmt, ##__VA_ARGS__) ++ ++#define snand_log_snfi(pdev, fmt, ...) \ ++ mtk_snand_log(pdev, SNAND_LOG_SNFI, fmt, ##__VA_ARGS__) ++ ++#define snand_log_ecc(pdev, fmt, ...) \ ++ mtk_snand_log(pdev, SNAND_LOG_ECC, fmt, ##__VA_ARGS__) ++ ++#define snand_log_chip(pdev, fmt, ...) \ ++ mtk_snand_log(pdev, SNAND_LOG_CHIP, fmt, ##__VA_ARGS__) ++ ++/* ffs64 */ ++static inline int mtk_snand_ffs64(uint64_t x) ++{ ++ if (!x) ++ return 0; ++ ++ if (!(x & 0xffffffff)) ++ return ffs((uint32_t)(x >> 32)) + 32; ++ ++ return ffs((uint32_t)(x & 0xffffffff)); ++} ++ ++/* NFI dummy commands */ ++#define NFI_CMD_DUMMY_READ 0x00 ++#define NFI_CMD_DUMMY_WRITE 0x80 ++ ++/* SPI-NAND opcodes */ ++#define SNAND_CMD_RESET 0xff ++#define SNAND_CMD_BLOCK_ERASE 0xd8 ++#define SNAND_CMD_READ_FROM_CACHE_QUAD 0xeb ++#define SNAND_CMD_WINBOND_SELECT_DIE 0xc2 ++#define SNAND_CMD_READ_FROM_CACHE_DUAL 0xbb ++#define SNAND_CMD_READID 0x9f ++#define SNAND_CMD_READ_FROM_CACHE_X4 0x6b ++#define SNAND_CMD_READ_FROM_CACHE_X2 0x3b ++#define SNAND_CMD_PROGRAM_LOAD_X4 0x32 ++#define SNAND_CMD_SET_FEATURE 0x1f ++#define SNAND_CMD_READ_TO_CACHE 0x13 ++#define SNAND_CMD_PROGRAM_EXECUTE 0x10 ++#define SNAND_CMD_GET_FEATURE 0x0f ++#define SNAND_CMD_READ_FROM_CACHE 0x0b ++#define SNAND_CMD_WRITE_ENABLE 0x06 ++#define SNAND_CMD_PROGRAM_LOAD 0x02 ++ ++/* SPI-NAND feature addresses */ ++#define SNAND_FEATURE_MICRON_DIE_ADDR 0xd0 ++#define SNAND_MICRON_DIE_SEL_1 BIT(6) ++ ++#define SNAND_FEATURE_STATUS_ADDR 0xc0 ++#define SNAND_STATUS_OIP BIT(0) ++#define SNAND_STATUS_WEL BIT(1) ++#define SNAND_STATUS_ERASE_FAIL BIT(2) ++#define SNAND_STATUS_PROGRAM_FAIL BIT(3) ++ ++#define SNAND_FEATURE_CONFIG_ADDR 0xb0 ++#define SNAND_FEATURE_QUAD_ENABLE BIT(0) ++#define SNAND_FEATURE_ECC_EN BIT(4) ++ ++#define SNAND_FEATURE_PROTECT_ADDR 0xa0 ++ ++#endif /* _MTK_SNAND_DEF_H_ */ +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-ecc.c +@@ -0,0 +1,264 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "mtk-snand-def.h" ++ ++/* ECC registers */ ++#define ECC_ENCCON 0x000 ++#define ENC_EN BIT(0) ++ ++#define ECC_ENCCNFG 0x004 ++#define ENC_MS_S 16 ++#define ENC_BURST_EN BIT(8) ++#define ENC_TNUM_S 0 ++ ++#define ECC_ENCIDLE 0x00c ++#define ENC_IDLE BIT(0) ++ ++#define ECC_DECCON 0x100 ++#define DEC_EN BIT(0) ++ ++#define ECC_DECCNFG 0x104 ++#define DEC_EMPTY_EN BIT(31) ++#define DEC_CS_S 16 ++#define DEC_CON_S 12 ++#define DEC_CON_CORRECT 3 ++#define DEC_BURST_EN BIT(8) ++#define DEC_TNUM_S 0 ++ ++#define ECC_DECIDLE 0x10c ++#define DEC_IDLE BIT(0) ++ ++#define ECC_DECENUM0 0x114 ++#define ECC_DECENUM(n) (ECC_DECENUM0 + (n) * 4) ++ ++/* ECC_ENCIDLE & ECC_DECIDLE */ ++#define ECC_IDLE BIT(0) ++ ++/* ENC_MODE & DEC_MODE */ ++#define ECC_MODE_NFI 1 ++ ++#define ECC_TIMEOUT 500000 ++ ++static const uint8_t mt7622_ecc_caps[] = { 4, 6, 8, 10, 12 }; ++ ++static const uint8_t mt7986_ecc_caps[] = { ++ 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 ++}; ++ ++static const uint32_t mt7622_ecc_regs[] = { ++ [ECC_DECDONE] = 0x11c, ++}; ++ ++static const uint32_t mt7986_ecc_regs[] = { ++ [ECC_DECDONE] = 0x124, ++}; ++ ++static const struct mtk_ecc_soc_data mtk_ecc_socs[__SNAND_SOC_MAX] = { ++ [SNAND_SOC_MT7622] = { ++ .ecc_caps = mt7622_ecc_caps, ++ .num_ecc_cap = ARRAY_SIZE(mt7622_ecc_caps), ++ .regs = mt7622_ecc_regs, ++ .mode_shift = 4, ++ .errnum_bits = 5, ++ .errnum_shift = 5, ++ }, ++ [SNAND_SOC_MT7629] = { ++ .ecc_caps = mt7622_ecc_caps, ++ .num_ecc_cap = ARRAY_SIZE(mt7622_ecc_caps), ++ .regs = mt7622_ecc_regs, ++ .mode_shift = 4, ++ .errnum_bits = 5, ++ .errnum_shift = 5, ++ }, ++ [SNAND_SOC_MT7986] = { ++ .ecc_caps = mt7986_ecc_caps, ++ .num_ecc_cap = ARRAY_SIZE(mt7986_ecc_caps), ++ .regs = mt7986_ecc_regs, ++ .mode_shift = 5, ++ .errnum_bits = 5, ++ .errnum_shift = 8, ++ }, ++}; ++ ++static inline uint32_t ecc_read32(struct mtk_snand *snf, uint32_t reg) ++{ ++ return readl(snf->ecc_base + reg); ++} ++ ++static inline void ecc_write32(struct mtk_snand *snf, uint32_t reg, ++ uint32_t val) ++{ ++ writel(val, snf->ecc_base + reg); ++} ++ ++static inline void ecc_write16(struct mtk_snand *snf, uint32_t reg, ++ uint16_t val) ++{ ++ writew(val, snf->ecc_base + reg); ++} ++ ++static int mtk_ecc_poll(struct mtk_snand *snf, uint32_t reg, uint32_t bits) ++{ ++ uint32_t val; ++ ++ return read16_poll_timeout(snf->ecc_base + reg, val, (val & bits), 0, ++ ECC_TIMEOUT); ++} ++ ++static int mtk_ecc_wait_idle(struct mtk_snand *snf, uint32_t reg) ++{ ++ int ret; ++ ++ ret = mtk_ecc_poll(snf, reg, ECC_IDLE); ++ if (ret) { ++ snand_log_ecc(snf->pdev, "ECC engine is busy\n"); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes, ++ uint32_t msg_size) ++{ ++ uint32_t i, val, ecc_msg_bits, ecc_strength; ++ int ret; ++ ++ snf->ecc_soc = &mtk_ecc_socs[snf->soc]; ++ ++ snf->ecc_parity_bits = fls(1 + 8 * msg_size); ++ ecc_strength = max_ecc_bytes * 8 / snf->ecc_parity_bits; ++ ++ for (i = snf->ecc_soc->num_ecc_cap - 1; i >= 0; i--) { ++ if (snf->ecc_soc->ecc_caps[i] <= ecc_strength) ++ break; ++ } ++ ++ if (unlikely(i < 0)) { ++ snand_log_ecc(snf->pdev, "Page size %u+%u is not supported\n", ++ snf->writesize, snf->oobsize); ++ return -ENOTSUPP; ++ } ++ ++ snf->ecc_strength = snf->ecc_soc->ecc_caps[i]; ++ snf->ecc_bytes = DIV_ROUND_UP(snf->ecc_strength * snf->ecc_parity_bits, ++ 8); ++ ++ /* Encoder config */ ++ ecc_write16(snf, ECC_ENCCON, 0); ++ ret = mtk_ecc_wait_idle(snf, ECC_ENCIDLE); ++ if (ret) ++ return ret; ++ ++ ecc_msg_bits = msg_size * 8; ++ val = (ecc_msg_bits << ENC_MS_S) | ++ (ECC_MODE_NFI << snf->ecc_soc->mode_shift) | i; ++ ecc_write32(snf, ECC_ENCCNFG, val); ++ ++ /* Decoder config */ ++ ecc_write16(snf, ECC_DECCON, 0); ++ ret = mtk_ecc_wait_idle(snf, ECC_DECIDLE); ++ if (ret) ++ return ret; ++ ++ ecc_msg_bits += snf->ecc_strength * snf->ecc_parity_bits; ++ val = DEC_EMPTY_EN | (ecc_msg_bits << DEC_CS_S) | ++ (DEC_CON_CORRECT << DEC_CON_S) | ++ (ECC_MODE_NFI << snf->ecc_soc->mode_shift) | i; ++ ecc_write32(snf, ECC_DECCNFG, val); ++ ++ return 0; ++} ++ ++int mtk_snand_ecc_encoder_start(struct mtk_snand *snf) ++{ ++ int ret; ++ ++ ret = mtk_ecc_wait_idle(snf, ECC_ENCIDLE); ++ if (ret) { ++ ecc_write16(snf, ECC_ENCCON, 0); ++ mtk_ecc_wait_idle(snf, ECC_ENCIDLE); ++ } ++ ++ ecc_write16(snf, ECC_ENCCON, ENC_EN); ++ ++ return 0; ++} ++ ++void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf) ++{ ++ mtk_ecc_wait_idle(snf, ECC_ENCIDLE); ++ ecc_write16(snf, ECC_ENCCON, 0); ++} ++ ++int mtk_snand_ecc_decoder_start(struct mtk_snand *snf) ++{ ++ int ret; ++ ++ ret = mtk_ecc_wait_idle(snf, ECC_DECIDLE); ++ if (ret) { ++ ecc_write16(snf, ECC_DECCON, 0); ++ mtk_ecc_wait_idle(snf, ECC_DECIDLE); ++ } ++ ++ ecc_write16(snf, ECC_DECCON, DEC_EN); ++ ++ return 0; ++} ++ ++void mtk_snand_ecc_decoder_stop(struct mtk_snand *snf) ++{ ++ mtk_ecc_wait_idle(snf, ECC_DECIDLE); ++ ecc_write16(snf, ECC_DECCON, 0); ++} ++ ++int mtk_ecc_wait_decoder_done(struct mtk_snand *snf) ++{ ++ uint16_t val, step_mask = (1 << snf->ecc_steps) - 1; ++ uint32_t reg = snf->ecc_soc->regs[ECC_DECDONE]; ++ int ret; ++ ++ ret = read16_poll_timeout(snf->ecc_base + reg, val, ++ (val & step_mask) == step_mask, 0, ++ ECC_TIMEOUT); ++ if (ret) ++ snand_log_ecc(snf->pdev, "ECC decoder is busy\n"); ++ ++ return ret; ++} ++ ++int mtk_ecc_check_decode_error(struct mtk_snand *snf, uint32_t page) ++{ ++ uint32_t i, regi, fi, errnum; ++ uint32_t errnum_shift = snf->ecc_soc->errnum_shift; ++ uint32_t errnum_mask = (1 << snf->ecc_soc->errnum_bits) - 1; ++ int ret = 0; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ regi = i / 4; ++ fi = i % 4; ++ ++ errnum = ecc_read32(snf, ECC_DECENUM(regi)); ++ errnum = (errnum >> (fi * errnum_shift)) & errnum_mask; ++ if (!errnum) ++ continue; ++ ++ if (errnum <= snf->ecc_strength) { ++ if (ret >= 0) ++ ret += errnum; ++ continue; ++ } ++ ++ snand_log_ecc(snf->pdev, ++ "Uncorrectable bitflips in page %u sect %u\n", ++ page, i); ++ ret = -EBADMSG; ++ } ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-ids.c +@@ -0,0 +1,511 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "mtk-snand-def.h" ++ ++static int mtk_snand_winbond_select_die(struct mtk_snand *snf, uint32_t dieidx); ++static int mtk_snand_micron_select_die(struct mtk_snand *snf, uint32_t dieidx); ++ ++#define SNAND_MEMORG_512M_2K_64 SNAND_MEMORG(2048, 64, 64, 512, 1, 1) ++#define SNAND_MEMORG_1G_2K_64 SNAND_MEMORG(2048, 64, 64, 1024, 1, 1) ++#define SNAND_MEMORG_2G_2K_64 SNAND_MEMORG(2048, 64, 64, 2048, 1, 1) ++#define SNAND_MEMORG_2G_2K_120 SNAND_MEMORG(2048, 120, 64, 2048, 1, 1) ++#define SNAND_MEMORG_4G_2K_64 SNAND_MEMORG(2048, 64, 64, 4096, 1, 1) ++#define SNAND_MEMORG_1G_2K_120 SNAND_MEMORG(2048, 120, 64, 1024, 1, 1) ++#define SNAND_MEMORG_1G_2K_128 SNAND_MEMORG(2048, 128, 64, 1024, 1, 1) ++#define SNAND_MEMORG_2G_2K_128 SNAND_MEMORG(2048, 128, 64, 2048, 1, 1) ++#define SNAND_MEMORG_4G_2K_128 SNAND_MEMORG(2048, 128, 64, 4096, 1, 1) ++#define SNAND_MEMORG_4G_4K_240 SNAND_MEMORG(4096, 240, 64, 2048, 1, 1) ++#define SNAND_MEMORG_4G_4K_256 SNAND_MEMORG(4096, 256, 64, 2048, 1, 1) ++#define SNAND_MEMORG_8G_4K_256 SNAND_MEMORG(4096, 256, 64, 4096, 1, 1) ++#define SNAND_MEMORG_2G_2K_64_2P SNAND_MEMORG(2048, 64, 64, 2048, 2, 1) ++#define SNAND_MEMORG_2G_2K_64_2D SNAND_MEMORG(2048, 64, 64, 1024, 1, 2) ++#define SNAND_MEMORG_2G_2K_128_2P SNAND_MEMORG(2048, 128, 64, 2048, 2, 1) ++#define SNAND_MEMORG_4G_2K_64_2P SNAND_MEMORG(2048, 64, 64, 4096, 2, 1) ++#define SNAND_MEMORG_4G_2K_128_2P_2D SNAND_MEMORG(2048, 128, 64, 2048, 2, 2) ++#define SNAND_MEMORG_8G_4K_256_2D SNAND_MEMORG(4096, 256, 64, 2048, 1, 2) ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_quad, ++ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | ++ SPI_IO_1_4_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), ++ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 4), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), ++ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 4)); ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_quad_q2d, ++ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | ++ SPI_IO_1_4_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), ++ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 4), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), ++ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 2)); ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_quad_a8d, ++ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | ++ SPI_IO_1_4_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), ++ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 8), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), ++ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 8)); ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_x4, ++ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_1_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8)); ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_x4_only, ++ SPI_IO_1_1_1 | SPI_IO_1_1_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8)); ++ ++static const SNAND_IO_CAP(snand_cap_program_load_x1, ++ SPI_IO_1_1_1, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_PROGRAM_LOAD, 0)); ++ ++static const SNAND_IO_CAP(snand_cap_program_load_x4, ++ SPI_IO_1_1_1 | SPI_IO_1_1_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_PROGRAM_LOAD, 0), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_PROGRAM_LOAD_X4, 0)); ++ ++static const struct snand_flash_info snand_flash_ids[] = { ++ SNAND_INFO("W25N512GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x20), ++ SNAND_MEMORG_512M_2K_64, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("W25N01GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x21), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("W25M02GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xab, 0x21), ++ SNAND_MEMORG_2G_2K_64_2D, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4, ++ mtk_snand_winbond_select_die), ++ SNAND_INFO("W25N02KV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x22), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("GD5F1GQ4UAWxx", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0x10), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F1GQ4UExIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd1), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F1GQ4UExxH", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd9), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F1GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf1), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F2GQ4UExIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd2), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F2GQ5UExxH", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0x32), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_a8d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F2GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf2), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F4GQ4UBxIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd4), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F4GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf4), ++ SNAND_MEMORG_4G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F2GQ5UExxG", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x52), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F4GQ4UCxIG", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0xb4), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("MX35LF1GE4AB", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x12), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF1G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x14), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX31LF1GE4BC", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x1e), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF2GE4AB", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x22), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF2G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x24), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF2GE4AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x26), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF2G14AC", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x20), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF4G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x35), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF4GE4AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x37), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("MT29F1G01AAADD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x12), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("MT29F1G01ABAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x14), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MT29F2G01AAAED", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x9f), ++ SNAND_MEMORG_2G_2K_64_2P, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("MT29F2G01ABAGD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x24), ++ SNAND_MEMORG_2G_2K_128_2P, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MT29F4G01AAADD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x32), ++ SNAND_MEMORG_4G_2K_64_2P, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("MT29F4G01ABAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x34), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MT29F4G01ADAGD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x36), ++ SNAND_MEMORG_4G_2K_128_2P_2D, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4, ++ mtk_snand_micron_select_die), ++ SNAND_INFO("MT29F8G01ADAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x46), ++ SNAND_MEMORG_8G_4K_256_2D, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4, ++ mtk_snand_micron_select_die), ++ ++ SNAND_INFO("TC58CVG0S3HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xc2), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("TC58CVG1S3HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xcb), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("TC58CVG2S0HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xcd), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("TC58CVG0S3HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xe2), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("TC58CVG1S3HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xeb), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("TC58CVG2S0HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xed), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("TH58CVG3S0HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xe4), ++ SNAND_MEMORG_8G_4K_256, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("F50L512M41A", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x20), ++ SNAND_MEMORG_512M_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("F50L1G41A", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x21), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("F50L1G41LB", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x01), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("F50L2G41LB", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x0a), ++ SNAND_MEMORG_2G_2K_64_2D, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4, ++ mtk_snand_winbond_select_die), ++ ++ SNAND_INFO("CS11G0T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x00), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G0G0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x10), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G0S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x20), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G1T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x01), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G1S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x21), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G2T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x02), ++ SNAND_MEMORG_4G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G2S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x22), ++ SNAND_MEMORG_4G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("EM73B044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x01), ++ SNAND_MEMORG_512M_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x11), ++ SNAND_MEMORG_1G_2K_120, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x09), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x18), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x19), ++ SNAND_MEMORG(2048, 64, 128, 512, 1, 1), ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044VCD", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1c), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1d), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1e), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044VCC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x22), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044VCF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x25), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x31), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SNC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0a), ++ SNAND_MEMORG_2G_2K_120, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x12), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SNF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x10), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x13), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x14), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCD", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x17), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCH", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1b), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1d), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCG", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1f), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCE", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x20), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCL", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2e), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x32), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x03), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0b), ++ SNAND_MEMORG_4G_4K_240, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x23), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2c), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044VCB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2f), ++ SNAND_MEMORG_4G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73F044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x24), ++ SNAND_MEMORG_8G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73F044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2d), ++ SNAND_MEMORG_8G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044SNE", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0e), ++ SNAND_MEMORG_8G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNG", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0c), ++ SNAND_MEMORG_1G_2K_120, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCN", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0f), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("FM35Q1GA", SNAND_ID(SNAND_ID_DYMMY, 0xe5, 0x71), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("PN26G01A", SNAND_ID(SNAND_ID_DYMMY, 0xa1, 0xe1), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("PN26G02A", SNAND_ID(SNAND_ID_DYMMY, 0xa1, 0xe2), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("IS37SML01G1", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x21), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("ATO25D1GA", SNAND_ID(SNAND_ID_DYMMY, 0x9b, 0x12), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4_only, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("HYF1GQ4U", SNAND_ID(SNAND_ID_DYMMY, 0xc9, 0x51), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("HYF2GQ4U", SNAND_ID(SNAND_ID_DYMMY, 0xc9, 0x52), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++}; ++ ++static int mtk_snand_winbond_select_die(struct mtk_snand *snf, uint32_t dieidx) ++{ ++ uint8_t op[2]; ++ ++ if (dieidx > 1) { ++ snand_log_chip(snf->pdev, "Invalid die index %u\n", dieidx); ++ return -EINVAL; ++ } ++ ++ op[0] = SNAND_CMD_WINBOND_SELECT_DIE; ++ op[1] = (uint8_t)dieidx; ++ ++ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); ++} ++ ++static int mtk_snand_micron_select_die(struct mtk_snand *snf, uint32_t dieidx) ++{ ++ int ret; ++ ++ if (dieidx > 1) { ++ snand_log_chip(snf->pdev, "Invalid die index %u\n", dieidx); ++ return -EINVAL; ++ } ++ ++ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_MICRON_DIE_ADDR, ++ SNAND_MICRON_DIE_SEL_1); ++ if (ret) { ++ snand_log_chip(snf->pdev, ++ "Failed to set die selection feature\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++const struct snand_flash_info *snand_flash_id_lookup(enum snand_id_type type, ++ const uint8_t *id) ++{ ++ const struct snand_id *fid; ++ uint32_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(snand_flash_ids); i++) { ++ if (snand_flash_ids[i].id.type != type) ++ continue; ++ ++ fid = &snand_flash_ids[i].id; ++ if (memcmp(fid->id, id, fid->len)) ++ continue; ++ ++ return &snand_flash_ids[i]; ++ } ++ ++ return NULL; ++} +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-mtd.c +@@ -0,0 +1,526 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mtk-snand.h" ++ ++struct mtk_snand_mtd { ++ struct udevice *dev; ++ struct mtk_snand *snf; ++ struct mtk_snand_chip_info cinfo; ++ uint8_t *page_cache; ++}; ++ ++static const char snand_mtd_name_prefix[] = "spi-nand"; ++ ++static u32 snandidx; ++ ++static inline struct mtk_snand_mtd *mtd_to_msm(struct mtd_info *mtd) ++{ ++ return mtd->priv; ++} ++ ++static int mtk_snand_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ u64 start_addr, end_addr; ++ int ret; ++ ++ /* Do not allow write past end of device */ ++ if ((instr->addr + instr->len) > mtd->size) { ++ pr_debug("%s: attempt to erase beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ start_addr = instr->addr & (~mtd->erasesize_mask); ++ end_addr = instr->addr + instr->len; ++ if (end_addr & mtd->erasesize_mask) { ++ end_addr = (end_addr + mtd->erasesize_mask) & ++ (~mtd->erasesize_mask); ++ } ++ ++ instr->state = MTD_ERASING; ++ ++ while (start_addr < end_addr) { ++ WATCHDOG_RESET(); ++ ++ if (mtk_snand_block_isbad(msm->snf, start_addr)) { ++ if (!instr->scrub) { ++ instr->fail_addr = start_addr; ++ ret = -EIO; ++ break; ++ } ++ } ++ ++ ret = mtk_snand_erase_block(msm->snf, start_addr); ++ if (ret) { ++ instr->fail_addr = start_addr; ++ break; ++ } ++ ++ start_addr += mtd->erasesize; ++ } ++ ++ if (ret) ++ instr->state = MTD_ERASE_FAILED; ++ else ++ instr->state = MTD_ERASE_DONE; ++ ++ if (!ret) ++ mtd_erase_callback(instr); ++ else ++ ret = -EIO; ++ ++ return ret; ++} ++ ++static int mtk_snand_mtd_read_data(struct mtk_snand_mtd *msm, uint64_t addr, ++ struct mtd_oob_ops *ops) ++{ ++ struct mtd_info *mtd = dev_get_uclass_priv(msm->dev); ++ size_t len, ooblen, maxooblen, chklen; ++ uint32_t col, ooboffs; ++ uint8_t *datcache, *oobcache; ++ bool raw = ops->mode == MTD_OPS_RAW ? true : false; ++ int ret; ++ ++ col = addr & mtd->writesize_mask; ++ addr &= ~mtd->writesize_mask; ++ maxooblen = mtd_oobavail(mtd, ops); ++ ooboffs = ops->ooboffs; ++ ooblen = ops->ooblen; ++ len = ops->len; ++ ++ datcache = len ? msm->page_cache : NULL; ++ oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL; ++ ++ ops->oobretlen = 0; ++ ops->retlen = 0; ++ ++ while (len || ooblen) { ++ WATCHDOG_RESET(); ++ ++ if (ops->mode == MTD_OPS_AUTO_OOB) ++ ret = mtk_snand_read_page_auto_oob(msm->snf, addr, ++ datcache, oobcache, maxooblen, NULL, raw); ++ else ++ ret = mtk_snand_read_page(msm->snf, addr, datcache, ++ oobcache, raw); ++ ++ if (ret < 0) ++ return ret; ++ ++ if (len) { ++ /* Move data */ ++ chklen = mtd->writesize - col; ++ if (chklen > len) ++ chklen = len; ++ ++ memcpy(ops->datbuf + ops->retlen, datcache + col, ++ chklen); ++ len -= chklen; ++ col = 0; /* (col + chklen) % */ ++ ops->retlen += chklen; ++ } ++ ++ if (ooblen) { ++ /* Move oob */ ++ chklen = maxooblen - ooboffs; ++ if (chklen > ooblen) ++ chklen = ooblen; ++ ++ memcpy(ops->oobbuf + ops->oobretlen, oobcache + ooboffs, ++ chklen); ++ ooblen -= chklen; ++ ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ ++ ops->oobretlen += chklen; ++ } ++ ++ addr += mtd->writesize; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_mtd_read_oob(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ uint32_t maxooblen; ++ ++ if (!ops->oobbuf && !ops->datbuf) { ++ if (ops->ooblen || ops->len) ++ return -EINVAL; ++ ++ return 0; ++ } ++ ++ switch (ops->mode) { ++ case MTD_OPS_PLACE_OOB: ++ case MTD_OPS_AUTO_OOB: ++ case MTD_OPS_RAW: ++ break; ++ default: ++ pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode); ++ return -EINVAL; ++ } ++ ++ maxooblen = mtd_oobavail(mtd, ops); ++ ++ /* Do not allow read past end of device */ ++ if (ops->datbuf && (from + ops->len) > mtd->size) { ++ pr_debug("%s: attempt to read beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (unlikely(ops->ooboffs >= maxooblen)) { ++ pr_debug("%s: attempt to start read outside oob\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (unlikely(from >= mtd->size || ++ ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) - ++ (from >> mtd->writesize_shift)) * maxooblen)) { ++ pr_debug("%s: attempt to read beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return mtk_snand_mtd_read_data(msm, from, ops); ++} ++ ++static int mtk_snand_mtd_write_data(struct mtk_snand_mtd *msm, uint64_t addr, ++ struct mtd_oob_ops *ops) ++{ ++ struct mtd_info *mtd = dev_get_uclass_priv(msm->dev); ++ size_t len, ooblen, maxooblen, chklen, oobwrlen; ++ uint32_t col, ooboffs; ++ uint8_t *datcache, *oobcache; ++ bool raw = ops->mode == MTD_OPS_RAW ? true : false; ++ int ret; ++ ++ col = addr & mtd->writesize_mask; ++ addr &= ~mtd->writesize_mask; ++ maxooblen = mtd_oobavail(mtd, ops); ++ ooboffs = ops->ooboffs; ++ ooblen = ops->ooblen; ++ len = ops->len; ++ ++ datcache = len ? msm->page_cache : NULL; ++ oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL; ++ ++ ops->oobretlen = 0; ++ ops->retlen = 0; ++ ++ while (len || ooblen) { ++ WATCHDOG_RESET(); ++ ++ if (len) { ++ /* Move data */ ++ chklen = mtd->writesize - col; ++ if (chklen > len) ++ chklen = len; ++ ++ memset(datcache, 0xff, col); ++ memcpy(datcache + col, ops->datbuf + ops->retlen, ++ chklen); ++ memset(datcache + col + chklen, 0xff, ++ mtd->writesize - col - chklen); ++ len -= chklen; ++ col = 0; /* (col + chklen) % */ ++ ops->retlen += chklen; ++ } ++ ++ oobwrlen = 0; ++ if (ooblen) { ++ /* Move oob */ ++ chklen = maxooblen - ooboffs; ++ if (chklen > ooblen) ++ chklen = ooblen; ++ ++ memset(oobcache, 0xff, ooboffs); ++ memcpy(oobcache + ooboffs, ++ ops->oobbuf + ops->oobretlen, chklen); ++ memset(oobcache + ooboffs + chklen, 0xff, ++ mtd->oobsize - ooboffs - chklen); ++ oobwrlen = chklen + ooboffs; ++ ooblen -= chklen; ++ ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ ++ ops->oobretlen += chklen; ++ } ++ ++ if (ops->mode == MTD_OPS_AUTO_OOB) ++ ret = mtk_snand_write_page_auto_oob(msm->snf, addr, ++ datcache, oobcache, oobwrlen, NULL, raw); ++ else ++ ret = mtk_snand_write_page(msm->snf, addr, datcache, ++ oobcache, raw); ++ ++ if (ret) ++ return ret; ++ ++ addr += mtd->writesize; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_mtd_write_oob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ uint32_t maxooblen; ++ ++ if (!ops->oobbuf && !ops->datbuf) { ++ if (ops->ooblen || ops->len) ++ return -EINVAL; ++ ++ return 0; ++ } ++ ++ switch (ops->mode) { ++ case MTD_OPS_PLACE_OOB: ++ case MTD_OPS_AUTO_OOB: ++ case MTD_OPS_RAW: ++ break; ++ default: ++ pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode); ++ return -EINVAL; ++ } ++ ++ maxooblen = mtd_oobavail(mtd, ops); ++ ++ /* Do not allow write past end of device */ ++ if (ops->datbuf && (to + ops->len) > mtd->size) { ++ pr_debug("%s: attempt to write beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (unlikely(ops->ooboffs >= maxooblen)) { ++ pr_debug("%s: attempt to start write outside oob\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (unlikely(to >= mtd->size || ++ ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) - ++ (to >> mtd->writesize_shift)) * maxooblen)) { ++ pr_debug("%s: attempt to write beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return mtk_snand_mtd_write_data(msm, to, ops); ++} ++ ++static int mtk_snand_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .datbuf = buf, ++ .len = len, ++ }; ++ int ret; ++ ++ ret = mtk_snand_mtd_read_oob(mtd, from, &ops); ++ ++ if (retlen) ++ *retlen = ops.retlen; ++ ++ return ret; ++} ++ ++static int mtk_snand_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .datbuf = (void *)buf, ++ .len = len, ++ }; ++ int ret; ++ ++ ret = mtk_snand_mtd_write_oob(mtd, to, &ops); ++ ++ if (retlen) ++ *retlen = ops.retlen; ++ ++ return ret; ++} ++ ++static int mtk_snand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ ++ return mtk_snand_block_isbad(msm->snf, offs); ++} ++ ++static int mtk_snand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ ++ return mtk_snand_block_markbad(msm->snf, offs); ++} ++ ++static int mtk_snand_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobecc) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oobecc->offset = msm->cinfo.fdm_size * msm->cinfo.num_sectors; ++ oobecc->length = mtd->oobsize - oobecc->offset; ++ ++ return 0; ++} ++ ++static int mtk_snand_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobfree) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ ++ if (section >= msm->cinfo.num_sectors) ++ return -ERANGE; ++ ++ oobfree->length = msm->cinfo.fdm_size - 1; ++ oobfree->offset = section * msm->cinfo.fdm_size + 1; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops mtk_snand_ooblayout = { ++ .ecc = mtk_snand_ooblayout_ecc, ++ .rfree = mtk_snand_ooblayout_free, ++}; ++ ++static int mtk_snand_mtd_probe(struct udevice *dev) ++{ ++ struct mtk_snand_mtd *msm = dev_get_priv(dev); ++ struct mtd_info *mtd = dev_get_uclass_priv(dev); ++ struct mtk_snand_platdata mtk_snand_pdata = {}; ++ size_t namelen; ++ fdt_addr_t base; ++ int ret; ++ ++ base = dev_read_addr_name(dev, "nfi"); ++ if (base == FDT_ADDR_T_NONE) ++ return -EINVAL; ++ mtk_snand_pdata.nfi_base = map_sysmem(base, 0); ++ ++ base = dev_read_addr_name(dev, "ecc"); ++ if (base == FDT_ADDR_T_NONE) ++ return -EINVAL; ++ mtk_snand_pdata.ecc_base = map_sysmem(base, 0); ++ ++ mtk_snand_pdata.soc = dev_get_driver_data(dev); ++ mtk_snand_pdata.quad_spi = dev_read_bool(dev, "quad-spi"); ++ ++ ret = mtk_snand_init(NULL, &mtk_snand_pdata, &msm->snf); ++ if (ret) ++ return ret; ++ ++ mtk_snand_get_chip_info(msm->snf, &msm->cinfo); ++ ++ msm->page_cache = malloc(msm->cinfo.pagesize + msm->cinfo.sparesize); ++ if (!msm->page_cache) { ++ printf("%s: failed to allocate memory for page cache\n", ++ __func__); ++ ret = -ENOMEM; ++ goto errout1; ++ } ++ ++ namelen = sizeof(snand_mtd_name_prefix) + 12; ++ ++ mtd->name = malloc(namelen); ++ if (!mtd->name) { ++ printf("%s: failed to allocate memory for MTD name\n", ++ __func__); ++ ret = -ENOMEM; ++ goto errout2; ++ } ++ ++ msm->dev = dev; ++ ++ snprintf(mtd->name, namelen, "%s%u", snand_mtd_name_prefix, snandidx++); ++ ++ mtd->priv = msm; ++ mtd->dev = dev; ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ ++ mtd->size = msm->cinfo.chipsize; ++ mtd->erasesize = msm->cinfo.blocksize; ++ mtd->writesize = msm->cinfo.pagesize; ++ mtd->writebufsize = mtd->writesize; ++ mtd->oobsize = msm->cinfo.sparesize; ++ mtd->oobavail = msm->cinfo.num_sectors * (msm->cinfo.fdm_size - 1); ++ ++ mtd->ooblayout = &mtk_snand_ooblayout; ++ ++ mtd->ecc_strength = msm->cinfo.ecc_strength * msm->cinfo.num_sectors; ++ mtd->bitflip_threshold = (mtd->ecc_strength * 3) / 4; ++ mtd->ecc_step_size = msm->cinfo.sector_size; ++ ++ mtd->_read = mtk_snand_mtd_read; ++ mtd->_write = mtk_snand_mtd_write; ++ mtd->_erase = mtk_snand_mtd_erase; ++ mtd->_read_oob = mtk_snand_mtd_read_oob; ++ mtd->_write_oob = mtk_snand_mtd_write_oob; ++ mtd->_block_isbad = mtk_snand_mtd_block_isbad; ++ mtd->_block_markbad = mtk_snand_mtd_block_markbad; ++ ++ ret = add_mtd_device(mtd); ++ if (ret) { ++ printf("%s: failed to add SPI-NAND MTD device\n", __func__); ++ ret = -ENODEV; ++ goto errout3; ++ } ++ ++ printf("SPI-NAND: %s (%lluMB)\n", msm->cinfo.model, ++ msm->cinfo.chipsize >> 20); ++ ++ return 0; ++ ++errout3: ++ free(mtd->name); ++ ++errout2: ++ free(msm->page_cache); ++ ++errout1: ++ mtk_snand_cleanup(msm->snf); ++ ++ return ret; ++} ++ ++static const struct udevice_id mtk_snand_ids[] = { ++ { .compatible = "mediatek,mt7622-snand", .data = SNAND_SOC_MT7622 }, ++ { .compatible = "mediatek,mt7629-snand", .data = SNAND_SOC_MT7629 }, ++ { .compatible = "mediatek,mt7986-snand", .data = SNAND_SOC_MT7986 }, ++ { /* sentinel */ }, ++}; ++ ++U_BOOT_DRIVER(spinand) = { ++ .name = "mtk-snand", ++ .id = UCLASS_MTD, ++ .of_match = mtk_snand_ids, ++ .priv_auto = sizeof(struct mtk_snand_mtd), ++ .probe = mtk_snand_mtd_probe, ++}; +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-os.c +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "mtk-snand-def.h" ++ ++int mtk_snand_log(struct mtk_snand_plat_dev *pdev, ++ enum mtk_snand_log_category cat, const char *fmt, ...) ++{ ++ const char *catname = ""; ++ va_list ap; ++ int ret; ++ ++ switch (cat) { ++ case SNAND_LOG_NFI: ++ catname = "NFI: "; ++ break; ++ case SNAND_LOG_SNFI: ++ catname = "SNFI: "; ++ break; ++ case SNAND_LOG_ECC: ++ catname = "ECC: "; ++ break; ++ default: ++ break; ++ } ++ ++ puts("SPI-NAND: "); ++ puts(catname); ++ ++ va_start(ap, fmt); ++ ret = vprintf(fmt, ap); ++ va_end(ap); ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-os.h +@@ -0,0 +1,120 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _MTK_SNAND_OS_H_ ++#define _MTK_SNAND_OS_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef ARCH_DMA_MINALIGN ++#define ARCH_DMA_MINALIGN 64 ++#endif ++ ++struct mtk_snand_plat_dev { ++ ulong unused; ++}; ++ ++/* Polling helpers */ ++#define read16_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ ++ readw_poll_timeout((addr), (val), (cond), (timeout_us)) ++ ++#define read32_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ ++ readl_poll_timeout((addr), (val), (cond), (timeout_us)) ++ ++/* Timer helpers */ ++typedef uint64_t mtk_snand_time_t; ++ ++static inline mtk_snand_time_t timer_get_ticks(void) ++{ ++ return get_ticks(); ++} ++ ++static inline mtk_snand_time_t timer_time_to_tick(uint32_t timeout_us) ++{ ++ return usec_to_tick(timeout_us); ++} ++ ++static inline bool timer_is_timeout(mtk_snand_time_t start_tick, ++ mtk_snand_time_t timeout_tick) ++{ ++ return get_ticks() - start_tick > timeout_tick; ++} ++ ++/* Memory helpers */ ++static inline void *generic_mem_alloc(struct mtk_snand_plat_dev *pdev, ++ size_t size) ++{ ++ return calloc(1, size); ++} ++ ++static inline void generic_mem_free(struct mtk_snand_plat_dev *pdev, void *ptr) ++{ ++ free(ptr); ++} ++ ++static inline void *dma_mem_alloc(struct mtk_snand_plat_dev *pdev, size_t size) ++{ ++ return memalign(ARCH_DMA_MINALIGN, size); ++} ++ ++static inline void dma_mem_free(struct mtk_snand_plat_dev *pdev, void *ptr) ++{ ++ free(ptr); ++} ++ ++static inline int dma_mem_map(struct mtk_snand_plat_dev *pdev, void *vaddr, ++ uintptr_t *dma_addr, size_t size, bool to_device) ++{ ++ size_t cachelen = roundup(size, ARCH_DMA_MINALIGN); ++ uintptr_t endaddr = (uintptr_t)vaddr + cachelen; ++ ++ if (to_device) ++ flush_dcache_range((uintptr_t)vaddr, endaddr); ++ else ++ invalidate_dcache_range((uintptr_t)vaddr, endaddr); ++ ++ *dma_addr = (uintptr_t)vaddr; ++ ++ return 0; ++} ++ ++static inline void dma_mem_unmap(struct mtk_snand_plat_dev *pdev, ++ uintptr_t dma_addr, size_t size, ++ bool to_device) ++{ ++} ++ ++/* Interrupt helpers */ ++static inline void irq_completion_done(struct mtk_snand_plat_dev *pdev) ++{ ++} ++ ++static inline void irq_completion_init(struct mtk_snand_plat_dev *pdev) ++{ ++} ++ ++static inline int irq_completion_wait(struct mtk_snand_plat_dev *pdev, ++ void __iomem *reg, uint32_t bit, ++ uint32_t timeout_us) ++{ ++ uint32_t val; ++ ++ return read32_poll_timeout(reg, val, val & bit, 0, timeout_us); ++} ++ ++#endif /* _MTK_SNAND_OS_H_ */ +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand.c +@@ -0,0 +1,1776 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "mtk-snand-def.h" ++ ++/* NFI registers */ ++#define NFI_CNFG 0x000 ++#define CNFG_OP_MODE_S 12 ++#define CNFG_OP_MODE_CUST 6 ++#define CNFG_OP_MODE_PROGRAM 3 ++#define CNFG_AUTO_FMT_EN BIT(9) ++#define CNFG_HW_ECC_EN BIT(8) ++#define CNFG_DMA_BURST_EN BIT(2) ++#define CNFG_READ_MODE BIT(1) ++#define CNFG_DMA_MODE BIT(0) ++ ++#define NFI_PAGEFMT 0x0004 ++#define NFI_SPARE_SIZE_LS_S 16 ++#define NFI_FDM_ECC_NUM_S 12 ++#define NFI_FDM_NUM_S 8 ++#define NFI_SPARE_SIZE_S 4 ++#define NFI_SEC_SEL_512 BIT(2) ++#define NFI_PAGE_SIZE_S 0 ++#define NFI_PAGE_SIZE_512_2K 0 ++#define NFI_PAGE_SIZE_2K_4K 1 ++#define NFI_PAGE_SIZE_4K_8K 2 ++#define NFI_PAGE_SIZE_8K_16K 3 ++ ++#define NFI_CON 0x008 ++#define CON_SEC_NUM_S 12 ++#define CON_BWR BIT(9) ++#define CON_BRD BIT(8) ++#define CON_NFI_RST BIT(1) ++#define CON_FIFO_FLUSH BIT(0) ++ ++#define NFI_INTR_EN 0x010 ++#define NFI_INTR_STA 0x014 ++#define NFI_IRQ_INTR_EN BIT(31) ++#define NFI_IRQ_CUS_READ BIT(8) ++#define NFI_IRQ_CUS_PG BIT(7) ++ ++#define NFI_CMD 0x020 ++ ++#define NFI_STRDATA 0x040 ++#define STR_DATA BIT(0) ++ ++#define NFI_STA 0x060 ++#define NFI_NAND_FSM GENMASK(28, 24) ++#define NFI_FSM GENMASK(19, 16) ++#define READ_EMPTY BIT(12) ++ ++#define NFI_FIFOSTA 0x064 ++#define FIFO_WR_REMAIN_S 8 ++#define FIFO_RD_REMAIN_S 0 ++ ++#define NFI_STRADDR 0x080 ++ ++#define NFI_FDM0L 0x0a0 ++#define NFI_FDM0M 0x0a4 ++#define NFI_FDML(n) (NFI_FDM0L + (n) * 8) ++#define NFI_FDMM(n) (NFI_FDM0M + (n) * 8) ++ ++#define NFI_DEBUG_CON1 0x220 ++#define WBUF_EN BIT(2) ++ ++#define NFI_MASTERSTA 0x224 ++#define MAS_ADDR GENMASK(11, 9) ++#define MAS_RD GENMASK(8, 6) ++#define MAS_WR GENMASK(5, 3) ++#define MAS_RDDLY GENMASK(2, 0) ++#define NFI_MASTERSTA_MASK_7622 (MAS_ADDR | MAS_RD | MAS_WR | MAS_RDDLY) ++#define AHB_BUS_BUSY BIT(1) ++#define BUS_BUSY BIT(0) ++#define NFI_MASTERSTA_MASK_7986 (AHB_BUS_BUSY | BUS_BUSY) ++ ++/* SNFI registers */ ++#define SNF_MAC_CTL 0x500 ++#define MAC_XIO_SEL BIT(4) ++#define SF_MAC_EN BIT(3) ++#define SF_TRIG BIT(2) ++#define WIP_READY BIT(1) ++#define WIP BIT(0) ++ ++#define SNF_MAC_OUTL 0x504 ++#define SNF_MAC_INL 0x508 ++ ++#define SNF_RD_CTL2 0x510 ++#define DATA_READ_DUMMY_S 8 ++#define DATA_READ_CMD_S 0 ++ ++#define SNF_RD_CTL3 0x514 ++ ++#define SNF_PG_CTL1 0x524 ++#define PG_LOAD_CMD_S 8 ++ ++#define SNF_PG_CTL2 0x528 ++ ++#define SNF_MISC_CTL 0x538 ++#define SW_RST BIT(28) ++#define FIFO_RD_LTC_S 25 ++#define PG_LOAD_X4_EN BIT(20) ++#define DATA_READ_MODE_S 16 ++#define DATA_READ_MODE GENMASK(18, 16) ++#define DATA_READ_MODE_X1 0 ++#define DATA_READ_MODE_X2 1 ++#define DATA_READ_MODE_X4 2 ++#define DATA_READ_MODE_DUAL 5 ++#define DATA_READ_MODE_QUAD 6 ++#define PG_LOAD_CUSTOM_EN BIT(7) ++#define DATARD_CUSTOM_EN BIT(6) ++#define CS_DESELECT_CYC_S 0 ++ ++#define SNF_MISC_CTL2 0x53c ++#define PROGRAM_LOAD_BYTE_NUM_S 16 ++#define READ_DATA_BYTE_NUM_S 11 ++ ++#define SNF_DLY_CTL3 0x548 ++#define SFCK_SAM_DLY_S 0 ++ ++#define SNF_STA_CTL1 0x550 ++#define CUS_PG_DONE BIT(28) ++#define CUS_READ_DONE BIT(27) ++#define SPI_STATE_S 0 ++#define SPI_STATE GENMASK(3, 0) ++ ++#define SNF_CFG 0x55c ++#define SPI_MODE BIT(0) ++ ++#define SNF_GPRAM 0x800 ++#define SNF_GPRAM_SIZE 0xa0 ++ ++#define SNFI_POLL_INTERVAL 1000000 ++ ++static const uint8_t mt7622_spare_sizes[] = { 16, 26, 27, 28 }; ++ ++static const uint8_t mt7986_spare_sizes[] = { ++ 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, ++ 67, 74 ++}; ++ ++static const struct mtk_snand_soc_data mtk_snand_socs[__SNAND_SOC_MAX] = { ++ [SNAND_SOC_MT7622] = { ++ .sector_size = 512, ++ .max_sectors = 8, ++ .fdm_size = 8, ++ .fdm_ecc_size = 1, ++ .fifo_size = 32, ++ .bbm_swap = false, ++ .empty_page_check = false, ++ .mastersta_mask = NFI_MASTERSTA_MASK_7622, ++ .spare_sizes = mt7622_spare_sizes, ++ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) ++ }, ++ [SNAND_SOC_MT7629] = { ++ .sector_size = 512, ++ .max_sectors = 8, ++ .fdm_size = 8, ++ .fdm_ecc_size = 1, ++ .fifo_size = 32, ++ .bbm_swap = true, ++ .empty_page_check = false, ++ .mastersta_mask = NFI_MASTERSTA_MASK_7622, ++ .spare_sizes = mt7622_spare_sizes, ++ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) ++ }, ++ [SNAND_SOC_MT7986] = { ++ .sector_size = 1024, ++ .max_sectors = 16, ++ .fdm_size = 8, ++ .fdm_ecc_size = 1, ++ .fifo_size = 64, ++ .bbm_swap = true, ++ .empty_page_check = true, ++ .mastersta_mask = NFI_MASTERSTA_MASK_7986, ++ .spare_sizes = mt7986_spare_sizes, ++ .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes) ++ }, ++}; ++ ++static inline uint32_t nfi_read32(struct mtk_snand *snf, uint32_t reg) ++{ ++ return readl(snf->nfi_base + reg); ++} ++ ++static inline void nfi_write32(struct mtk_snand *snf, uint32_t reg, ++ uint32_t val) ++{ ++ writel(val, snf->nfi_base + reg); ++} ++ ++static inline void nfi_write16(struct mtk_snand *snf, uint32_t reg, ++ uint16_t val) ++{ ++ writew(val, snf->nfi_base + reg); ++} ++ ++static inline void nfi_rmw32(struct mtk_snand *snf, uint32_t reg, uint32_t clr, ++ uint32_t set) ++{ ++ uint32_t val; ++ ++ val = readl(snf->nfi_base + reg); ++ val &= ~clr; ++ val |= set; ++ writel(val, snf->nfi_base + reg); ++} ++ ++static void nfi_write_data(struct mtk_snand *snf, uint32_t reg, ++ const uint8_t *data, uint32_t len) ++{ ++ uint32_t i, val = 0, es = sizeof(uint32_t); ++ ++ for (i = reg; i < reg + len; i++) { ++ val |= ((uint32_t)*data++) << (8 * (i % es)); ++ ++ if (i % es == es - 1 || i == reg + len - 1) { ++ nfi_write32(snf, i & ~(es - 1), val); ++ val = 0; ++ } ++ } ++} ++ ++static void nfi_read_data(struct mtk_snand *snf, uint32_t reg, uint8_t *data, ++ uint32_t len) ++{ ++ uint32_t i, val = 0, es = sizeof(uint32_t); ++ ++ for (i = reg; i < reg + len; i++) { ++ if (i == reg || i % es == 0) ++ val = nfi_read32(snf, i & ~(es - 1)); ++ ++ *data++ = (uint8_t)(val >> (8 * (i % es))); ++ } ++} ++ ++static inline void do_bm_swap(uint8_t *bm1, uint8_t *bm2) ++{ ++ uint8_t tmp = *bm1; ++ *bm1 = *bm2; ++ *bm2 = tmp; ++} ++ ++static void mtk_snand_bm_swap_raw(struct mtk_snand *snf) ++{ ++ uint32_t fdm_bbm_pos; ++ ++ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) ++ return; ++ ++ fdm_bbm_pos = (snf->ecc_steps - 1) * snf->raw_sector_size + ++ snf->nfi_soc->sector_size; ++ do_bm_swap(&snf->page_cache[fdm_bbm_pos], ++ &snf->page_cache[snf->writesize]); ++} ++ ++static void mtk_snand_bm_swap(struct mtk_snand *snf) ++{ ++ uint32_t buf_bbm_pos, fdm_bbm_pos; ++ ++ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) ++ return; ++ ++ buf_bbm_pos = snf->writesize - ++ (snf->ecc_steps - 1) * snf->spare_per_sector; ++ fdm_bbm_pos = snf->writesize + ++ (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size; ++ do_bm_swap(&snf->page_cache[fdm_bbm_pos], ++ &snf->page_cache[buf_bbm_pos]); ++} ++ ++static void mtk_snand_fdm_bm_swap_raw(struct mtk_snand *snf) ++{ ++ uint32_t fdm_bbm_pos1, fdm_bbm_pos2; ++ ++ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) ++ return; ++ ++ fdm_bbm_pos1 = snf->nfi_soc->sector_size; ++ fdm_bbm_pos2 = (snf->ecc_steps - 1) * snf->raw_sector_size + ++ snf->nfi_soc->sector_size; ++ do_bm_swap(&snf->page_cache[fdm_bbm_pos1], ++ &snf->page_cache[fdm_bbm_pos2]); ++} ++ ++static void mtk_snand_fdm_bm_swap(struct mtk_snand *snf) ++{ ++ uint32_t fdm_bbm_pos1, fdm_bbm_pos2; ++ ++ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) ++ return; ++ ++ fdm_bbm_pos1 = snf->writesize; ++ fdm_bbm_pos2 = snf->writesize + ++ (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size; ++ do_bm_swap(&snf->page_cache[fdm_bbm_pos1], ++ &snf->page_cache[fdm_bbm_pos2]); ++} ++ ++static int mtk_nfi_reset(struct mtk_snand *snf) ++{ ++ uint32_t val, fifo_mask; ++ int ret; ++ ++ nfi_write32(snf, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST); ++ ++ ret = read16_poll_timeout(snf->nfi_base + NFI_MASTERSTA, val, ++ !(val & snf->nfi_soc->mastersta_mask), 0, ++ SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "NFI master is still busy after reset\n"); ++ return ret; ++ } ++ ++ ret = read32_poll_timeout(snf->nfi_base + NFI_STA, val, ++ !(val & (NFI_FSM | NFI_NAND_FSM)), 0, ++ SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, "Failed to reset NFI\n"); ++ return ret; ++ } ++ ++ fifo_mask = ((snf->nfi_soc->fifo_size - 1) << FIFO_RD_REMAIN_S) | ++ ((snf->nfi_soc->fifo_size - 1) << FIFO_WR_REMAIN_S); ++ ret = read16_poll_timeout(snf->nfi_base + NFI_FIFOSTA, val, ++ !(val & fifo_mask), 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, "NFI FIFOs are not empty\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_mac_reset(struct mtk_snand *snf) ++{ ++ int ret; ++ uint32_t val; ++ ++ nfi_rmw32(snf, SNF_MISC_CTL, 0, SW_RST); ++ ++ ret = read32_poll_timeout(snf->nfi_base + SNF_STA_CTL1, val, ++ !(val & SPI_STATE), 0, SNFI_POLL_INTERVAL); ++ if (ret) ++ snand_log_snfi(snf->pdev, "Failed to reset SNFI MAC\n"); ++ ++ nfi_write32(snf, SNF_MISC_CTL, (2 << FIFO_RD_LTC_S) | ++ (10 << CS_DESELECT_CYC_S)); ++ ++ return ret; ++} ++ ++static int mtk_snand_mac_trigger(struct mtk_snand *snf, uint32_t outlen, ++ uint32_t inlen) ++{ ++ int ret; ++ uint32_t val; ++ ++ nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN); ++ nfi_write32(snf, SNF_MAC_OUTL, outlen); ++ nfi_write32(snf, SNF_MAC_INL, inlen); ++ ++ nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN | SF_TRIG); ++ ++ ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, ++ val & WIP_READY, 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_snfi(snf->pdev, "Timed out waiting for WIP_READY\n"); ++ goto cleanup; ++ } ++ ++ ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, ++ !(val & WIP), 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_snfi(snf->pdev, ++ "Timed out waiting for WIP cleared\n"); ++ } ++ ++cleanup: ++ nfi_write32(snf, SNF_MAC_CTL, 0); ++ ++ return ret; ++} ++ ++int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen, ++ uint8_t *in, uint32_t inlen) ++{ ++ int ret; ++ ++ if (outlen + inlen > SNF_GPRAM_SIZE) ++ return -EINVAL; ++ ++ mtk_snand_mac_reset(snf); ++ ++ nfi_write_data(snf, SNF_GPRAM, out, outlen); ++ ++ ret = mtk_snand_mac_trigger(snf, outlen, inlen); ++ if (ret) ++ return ret; ++ ++ if (!inlen) ++ return 0; ++ ++ nfi_read_data(snf, SNF_GPRAM + outlen, in, inlen); ++ ++ return 0; ++} ++ ++static int mtk_snand_get_feature(struct mtk_snand *snf, uint32_t addr) ++{ ++ uint8_t op[2], val; ++ int ret; ++ ++ op[0] = SNAND_CMD_GET_FEATURE; ++ op[1] = (uint8_t)addr; ++ ++ ret = mtk_snand_mac_io(snf, op, sizeof(op), &val, 1); ++ if (ret) ++ return ret; ++ ++ return val; ++} ++ ++int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val) ++{ ++ uint8_t op[3]; ++ ++ op[0] = SNAND_CMD_SET_FEATURE; ++ op[1] = (uint8_t)addr; ++ op[2] = (uint8_t)val; ++ ++ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); ++} ++ ++static int mtk_snand_poll_status(struct mtk_snand *snf, uint32_t wait_us) ++{ ++ int val; ++ mtk_snand_time_t time_start, tmo; ++ ++ time_start = timer_get_ticks(); ++ tmo = timer_time_to_tick(wait_us); ++ ++ do { ++ val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR); ++ if (!(val & SNAND_STATUS_OIP)) ++ return val & (SNAND_STATUS_ERASE_FAIL | ++ SNAND_STATUS_PROGRAM_FAIL); ++ } while (!timer_is_timeout(time_start, tmo)); ++ ++ return -ETIMEDOUT; ++} ++ ++int mtk_snand_chip_reset(struct mtk_snand *snf) ++{ ++ uint8_t op = SNAND_CMD_RESET; ++ int ret; ++ ++ ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mtk_snand_config_feature(struct mtk_snand *snf, uint8_t clr, ++ uint8_t set) ++{ ++ int val, newval; ++ int ret; ++ ++ val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR); ++ if (val < 0) { ++ snand_log_chip(snf->pdev, ++ "Failed to get configuration feature\n"); ++ return val; ++ } ++ ++ newval = (val & (~clr)) | set; ++ ++ if (newval == val) ++ return 0; ++ ++ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_CONFIG_ADDR, ++ (uint8_t)newval); ++ if (val < 0) { ++ snand_log_chip(snf->pdev, ++ "Failed to set configuration feature\n"); ++ return ret; ++ } ++ ++ val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR); ++ if (val < 0) { ++ snand_log_chip(snf->pdev, ++ "Failed to get configuration feature\n"); ++ return val; ++ } ++ ++ if (newval != val) ++ return -ENOTSUPP; ++ ++ return 0; ++} ++ ++static int mtk_snand_ondie_ecc_control(struct mtk_snand *snf, bool enable) ++{ ++ int ret; ++ ++ if (enable) ++ ret = mtk_snand_config_feature(snf, 0, SNAND_FEATURE_ECC_EN); ++ else ++ ret = mtk_snand_config_feature(snf, SNAND_FEATURE_ECC_EN, 0); ++ ++ if (ret) { ++ snand_log_chip(snf->pdev, "Failed to %s On-Die ECC engine\n", ++ enable ? "enable" : "disable"); ++ } ++ ++ return ret; ++} ++ ++static int mtk_snand_qspi_control(struct mtk_snand *snf, bool enable) ++{ ++ int ret; ++ ++ if (enable) { ++ ret = mtk_snand_config_feature(snf, 0, ++ SNAND_FEATURE_QUAD_ENABLE); ++ } else { ++ ret = mtk_snand_config_feature(snf, ++ SNAND_FEATURE_QUAD_ENABLE, 0); ++ } ++ ++ if (ret) { ++ snand_log_chip(snf->pdev, "Failed to %s quad spi\n", ++ enable ? "enable" : "disable"); ++ } ++ ++ return ret; ++} ++ ++static int mtk_snand_unlock(struct mtk_snand *snf) ++{ ++ int ret; ++ ++ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_PROTECT_ADDR, 0); ++ if (ret) { ++ snand_log_chip(snf->pdev, "Failed to set protection feature\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_write_enable(struct mtk_snand *snf) ++{ ++ uint8_t op = SNAND_CMD_WRITE_ENABLE; ++ int ret, val; ++ ++ ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0); ++ if (ret) ++ return ret; ++ ++ val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR); ++ if (val < 0) ++ return ret; ++ ++ if (val & SNAND_STATUS_WEL) ++ return 0; ++ ++ snand_log_chip(snf->pdev, "Failed to send write-enable command\n"); ++ ++ return -ENOTSUPP; ++} ++ ++static int mtk_snand_select_die(struct mtk_snand *snf, uint32_t dieidx) ++{ ++ if (!snf->select_die) ++ return 0; ++ ++ return snf->select_die(snf, dieidx); ++} ++ ++static uint64_t mtk_snand_select_die_address(struct mtk_snand *snf, ++ uint64_t addr) ++{ ++ uint32_t dieidx; ++ ++ if (!snf->select_die) ++ return addr; ++ ++ dieidx = addr >> snf->die_shift; ++ ++ mtk_snand_select_die(snf, dieidx); ++ ++ return addr & snf->die_mask; ++} ++ ++static uint32_t mtk_snand_get_plane_address(struct mtk_snand *snf, ++ uint32_t page) ++{ ++ uint32_t pages_per_block; ++ ++ pages_per_block = 1 << (snf->erasesize_shift - snf->writesize_shift); ++ ++ if (page & pages_per_block) ++ return 1 << (snf->writesize_shift + 1); ++ ++ return 0; ++} ++ ++static int mtk_snand_page_op(struct mtk_snand *snf, uint32_t page, uint8_t cmd) ++{ ++ uint8_t op[4]; ++ ++ op[0] = cmd; ++ op[1] = (page >> 16) & 0xff; ++ op[2] = (page >> 8) & 0xff; ++ op[3] = page & 0xff; ++ ++ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); ++} ++ ++static void mtk_snand_read_fdm(struct mtk_snand *snf, uint8_t *buf) ++{ ++ uint32_t vall, valm; ++ uint8_t *oobptr = buf; ++ int i, j; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ vall = nfi_read32(snf, NFI_FDML(i)); ++ valm = nfi_read32(snf, NFI_FDMM(i)); ++ ++ for (j = 0; j < snf->nfi_soc->fdm_size; j++) ++ oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); ++ ++ oobptr += snf->nfi_soc->fdm_size; ++ } ++} ++ ++static int mtk_snand_read_cache(struct mtk_snand *snf, uint32_t page, bool raw) ++{ ++ uint32_t coladdr, rwbytes, mode, len; ++ uintptr_t dma_addr; ++ int ret; ++ ++ /* Column address with plane bit */ ++ coladdr = mtk_snand_get_plane_address(snf, page); ++ ++ mtk_snand_mac_reset(snf); ++ mtk_nfi_reset(snf); ++ ++ /* Command and dummy cycles */ ++ nfi_write32(snf, SNF_RD_CTL2, ++ ((uint32_t)snf->dummy_rfc << DATA_READ_DUMMY_S) | ++ (snf->opcode_rfc << DATA_READ_CMD_S)); ++ ++ /* Column address */ ++ nfi_write32(snf, SNF_RD_CTL3, coladdr); ++ ++ /* Set read mode */ ++ mode = (uint32_t)snf->mode_rfc << DATA_READ_MODE_S; ++ nfi_rmw32(snf, SNF_MISC_CTL, DATA_READ_MODE, mode | DATARD_CUSTOM_EN); ++ ++ /* Set bytes to read */ ++ rwbytes = snf->ecc_steps * snf->raw_sector_size; ++ nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) | ++ rwbytes); ++ ++ /* NFI read prepare */ ++ mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; ++ nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_CUST << CNFG_OP_MODE_S) | ++ CNFG_DMA_BURST_EN | CNFG_READ_MODE | CNFG_DMA_MODE | mode); ++ ++ nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S)); ++ ++ /* Prepare for DMA read */ ++ len = snf->writesize + snf->oobsize; ++ ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, false); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "DMA map from device failed with %d\n", ret); ++ return ret; ++ } ++ ++ nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr); ++ ++ if (!raw) ++ mtk_snand_ecc_decoder_start(snf); ++ ++ /* Prepare for custom read interrupt */ ++ nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_READ); ++ irq_completion_init(snf->pdev); ++ ++ /* Trigger NFI into custom mode */ ++ nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_READ); ++ ++ /* Start DMA read */ ++ nfi_rmw32(snf, NFI_CON, 0, CON_BRD); ++ nfi_write16(snf, NFI_STRDATA, STR_DATA); ++ ++ /* Wait for operation finished */ ++ ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1, ++ CUS_READ_DONE, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "DMA timed out for reading from cache\n"); ++ goto cleanup; ++ } ++ ++ if (!raw) { ++ ret = mtk_ecc_wait_decoder_done(snf); ++ if (ret) ++ goto cleanup; ++ ++ mtk_snand_read_fdm(snf, snf->page_cache + snf->writesize); ++ ++ /* ++ * For new IPs, ecc error may occur on empty pages. ++ * Use an specific indication bit to check empty page. ++ */ ++ if (snf->nfi_soc->empty_page_check && ++ (nfi_read32(snf, NFI_STA) & READ_EMPTY)) ++ ret = 0; ++ else ++ ret = mtk_ecc_check_decode_error(snf, page); ++ ++ mtk_snand_ecc_decoder_stop(snf); ++ } ++ ++cleanup: ++ /* DMA cleanup */ ++ dma_mem_unmap(snf->pdev, dma_addr, len, false); ++ ++ /* Stop read */ ++ nfi_write32(snf, NFI_CON, 0); ++ ++ /* Clear SNF done flag */ ++ nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE); ++ nfi_write32(snf, SNF_STA_CTL1, 0); ++ ++ /* Disable interrupt */ ++ nfi_read32(snf, NFI_INTR_STA); ++ nfi_write32(snf, NFI_INTR_EN, 0); ++ ++ nfi_rmw32(snf, SNF_MISC_CTL, DATARD_CUSTOM_EN, 0); ++ ++ return ret; ++} ++ ++static void mtk_snand_from_raw_page(struct mtk_snand *snf, void *buf, void *oob) ++{ ++ uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; ++ uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size; ++ uint8_t *bufptr = buf, *oobptr = oob, *raw_sector; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ raw_sector = snf->page_cache + i * snf->raw_sector_size; ++ ++ if (buf) { ++ memcpy(bufptr, raw_sector, snf->nfi_soc->sector_size); ++ bufptr += snf->nfi_soc->sector_size; ++ } ++ ++ raw_sector += snf->nfi_soc->sector_size; ++ ++ if (oob) { ++ memcpy(oobptr, raw_sector, snf->nfi_soc->fdm_size); ++ oobptr += snf->nfi_soc->fdm_size; ++ raw_sector += snf->nfi_soc->fdm_size; ++ ++ memcpy(eccptr, raw_sector, ecc_bytes); ++ eccptr += ecc_bytes; ++ } ++ } ++} ++ ++static int mtk_snand_do_read_page(struct mtk_snand *snf, uint64_t addr, ++ void *buf, void *oob, bool raw, bool format) ++{ ++ uint64_t die_addr; ++ uint32_t page; ++ int ret; ++ ++ die_addr = mtk_snand_select_die_address(snf, addr); ++ page = die_addr >> snf->writesize_shift; ++ ++ ret = mtk_snand_page_op(snf, page, SNAND_CMD_READ_TO_CACHE); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); ++ if (ret < 0) { ++ snand_log_chip(snf->pdev, "Read to cache command timed out\n"); ++ return ret; ++ } ++ ++ ret = mtk_snand_read_cache(snf, page, raw); ++ if (ret < 0 && ret != -EBADMSG) ++ return ret; ++ ++ if (raw) { ++ if (format) { ++ mtk_snand_bm_swap_raw(snf); ++ mtk_snand_fdm_bm_swap_raw(snf); ++ mtk_snand_from_raw_page(snf, buf, oob); ++ } else { ++ if (buf) ++ memcpy(buf, snf->page_cache, snf->writesize); ++ ++ if (oob) { ++ memset(oob, 0xff, snf->oobsize); ++ memcpy(oob, snf->page_cache + snf->writesize, ++ snf->ecc_steps * snf->spare_per_sector); ++ } ++ } ++ } else { ++ mtk_snand_bm_swap(snf); ++ mtk_snand_fdm_bm_swap(snf); ++ ++ if (buf) ++ memcpy(buf, snf->page_cache, snf->writesize); ++ ++ if (oob) { ++ memset(oob, 0xff, snf->oobsize); ++ memcpy(oob, snf->page_cache + snf->writesize, ++ snf->ecc_steps * snf->nfi_soc->fdm_size); ++ } ++ } ++ ++ return ret; ++} ++ ++int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf, ++ void *oob, bool raw) ++{ ++ if (!snf || (!buf && !oob)) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ return mtk_snand_do_read_page(snf, addr, buf, oob, raw, true); ++} ++ ++static void mtk_snand_write_fdm(struct mtk_snand *snf, const uint8_t *buf) ++{ ++ uint32_t vall, valm, fdm_size = snf->nfi_soc->fdm_size; ++ const uint8_t *oobptr = buf; ++ int i, j; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ vall = 0; ++ valm = 0; ++ ++ for (j = 0; j < 8; j++) { ++ if (j < 4) ++ vall |= (j < fdm_size ? oobptr[j] : 0xff) ++ << (j * 8); ++ else ++ valm |= (j < fdm_size ? oobptr[j] : 0xff) ++ << ((j - 4) * 8); ++ } ++ ++ nfi_write32(snf, NFI_FDML(i), vall); ++ nfi_write32(snf, NFI_FDMM(i), valm); ++ ++ oobptr += fdm_size; ++ } ++} ++ ++static int mtk_snand_program_load(struct mtk_snand *snf, uint32_t page, ++ bool raw) ++{ ++ uint32_t coladdr, rwbytes, mode, len; ++ uintptr_t dma_addr; ++ int ret; ++ ++ /* Column address with plane bit */ ++ coladdr = mtk_snand_get_plane_address(snf, page); ++ ++ mtk_snand_mac_reset(snf); ++ mtk_nfi_reset(snf); ++ ++ /* Write FDM registers if necessary */ ++ if (!raw) ++ mtk_snand_write_fdm(snf, snf->page_cache + snf->writesize); ++ ++ /* Command */ ++ nfi_write32(snf, SNF_PG_CTL1, (snf->opcode_pl << PG_LOAD_CMD_S)); ++ ++ /* Column address */ ++ nfi_write32(snf, SNF_PG_CTL2, coladdr); ++ ++ /* Set write mode */ ++ mode = snf->mode_pl ? PG_LOAD_X4_EN : 0; ++ nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_X4_EN, mode | PG_LOAD_CUSTOM_EN); ++ ++ /* Set bytes to write */ ++ rwbytes = snf->ecc_steps * snf->raw_sector_size; ++ nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) | ++ rwbytes); ++ ++ /* NFI write prepare */ ++ mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; ++ nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_PROGRAM << CNFG_OP_MODE_S) | ++ CNFG_DMA_BURST_EN | CNFG_DMA_MODE | mode); ++ ++ nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S)); ++ ++ /* Prepare for DMA write */ ++ len = snf->writesize + snf->oobsize; ++ ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, true); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "DMA map to device failed with %d\n", ret); ++ return ret; ++ } ++ ++ nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr); ++ ++ if (!raw) ++ mtk_snand_ecc_encoder_start(snf); ++ ++ /* Prepare for custom write interrupt */ ++ nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_PG); ++ irq_completion_init(snf->pdev); ++ ++ /* Trigger NFI into custom mode */ ++ nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_WRITE); ++ ++ /* Start DMA write */ ++ nfi_rmw32(snf, NFI_CON, 0, CON_BWR); ++ nfi_write16(snf, NFI_STRDATA, STR_DATA); ++ ++ /* Wait for operation finished */ ++ ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1, ++ CUS_PG_DONE, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "DMA timed out for program load\n"); ++ goto cleanup; ++ } ++ ++ if (!raw) ++ mtk_snand_ecc_encoder_stop(snf); ++ ++cleanup: ++ /* DMA cleanup */ ++ dma_mem_unmap(snf->pdev, dma_addr, len, true); ++ ++ /* Stop write */ ++ nfi_write16(snf, NFI_CON, 0); ++ ++ /* Clear SNF done flag */ ++ nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_PG_DONE); ++ nfi_write32(snf, SNF_STA_CTL1, 0); ++ ++ /* Disable interrupt */ ++ nfi_read32(snf, NFI_INTR_STA); ++ nfi_write32(snf, NFI_INTR_EN, 0); ++ ++ nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_CUSTOM_EN, 0); ++ ++ return ret; ++} ++ ++static void mtk_snand_to_raw_page(struct mtk_snand *snf, ++ const void *buf, const void *oob, ++ bool empty_ecc) ++{ ++ uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; ++ const uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size; ++ const uint8_t *bufptr = buf, *oobptr = oob; ++ uint8_t *raw_sector; ++ ++ memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize); ++ for (i = 0; i < snf->ecc_steps; i++) { ++ raw_sector = snf->page_cache + i * snf->raw_sector_size; ++ ++ if (buf) { ++ memcpy(raw_sector, bufptr, snf->nfi_soc->sector_size); ++ bufptr += snf->nfi_soc->sector_size; ++ } ++ ++ raw_sector += snf->nfi_soc->sector_size; ++ ++ if (oob) { ++ memcpy(raw_sector, oobptr, snf->nfi_soc->fdm_size); ++ oobptr += snf->nfi_soc->fdm_size; ++ raw_sector += snf->nfi_soc->fdm_size; ++ ++ if (empty_ecc) ++ memset(raw_sector, 0xff, ecc_bytes); ++ else ++ memcpy(raw_sector, eccptr, ecc_bytes); ++ eccptr += ecc_bytes; ++ } ++ } ++} ++ ++static bool mtk_snand_is_empty_page(struct mtk_snand *snf, const void *buf, ++ const void *oob) ++{ ++ const uint8_t *p = buf; ++ uint32_t i, j; ++ ++ if (buf) { ++ for (i = 0; i < snf->writesize; i++) { ++ if (p[i] != 0xff) ++ return false; ++ } ++ } ++ ++ if (oob) { ++ for (j = 0; j < snf->ecc_steps; j++) { ++ p = oob + j * snf->nfi_soc->fdm_size; ++ ++ for (i = 0; i < snf->nfi_soc->fdm_ecc_size; i++) { ++ if (p[i] != 0xff) ++ return false; ++ } ++ } ++ } ++ ++ return true; ++} ++ ++static int mtk_snand_do_write_page(struct mtk_snand *snf, uint64_t addr, ++ const void *buf, const void *oob, ++ bool raw, bool format) ++{ ++ uint64_t die_addr; ++ bool empty_ecc = false; ++ uint32_t page; ++ int ret; ++ ++ die_addr = mtk_snand_select_die_address(snf, addr); ++ page = die_addr >> snf->writesize_shift; ++ ++ if (!raw && mtk_snand_is_empty_page(snf, buf, oob)) { ++ /* ++ * If the data in the page to be ecc-ed is full 0xff, ++ * change to raw write mode ++ */ ++ raw = true; ++ format = true; ++ ++ /* fill ecc parity code region with 0xff */ ++ empty_ecc = true; ++ } ++ ++ if (raw) { ++ if (format) { ++ mtk_snand_to_raw_page(snf, buf, oob, empty_ecc); ++ mtk_snand_fdm_bm_swap_raw(snf); ++ mtk_snand_bm_swap_raw(snf); ++ } else { ++ memset(snf->page_cache, 0xff, ++ snf->writesize + snf->oobsize); ++ ++ if (buf) ++ memcpy(snf->page_cache, buf, snf->writesize); ++ ++ if (oob) { ++ memcpy(snf->page_cache + snf->writesize, oob, ++ snf->ecc_steps * snf->spare_per_sector); ++ } ++ } ++ } else { ++ memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize); ++ if (buf) ++ memcpy(snf->page_cache, buf, snf->writesize); ++ ++ if (oob) { ++ memcpy(snf->page_cache + snf->writesize, oob, ++ snf->ecc_steps * snf->nfi_soc->fdm_size); ++ } ++ ++ mtk_snand_fdm_bm_swap(snf); ++ mtk_snand_bm_swap(snf); ++ } ++ ++ ret = mtk_snand_write_enable(snf); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_program_load(snf, page, raw); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_page_op(snf, page, SNAND_CMD_PROGRAM_EXECUTE); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); ++ if (ret < 0) { ++ snand_log_chip(snf->pdev, ++ "Page program command timed out on page %u\n", ++ page); ++ return ret; ++ } ++ ++ if (ret & SNAND_STATUS_PROGRAM_FAIL) { ++ snand_log_chip(snf->pdev, ++ "Page program failed on page %u\n", page); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf, ++ const void *oob, bool raw) ++{ ++ if (!snf || (!buf && !oob)) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ return mtk_snand_do_write_page(snf, addr, buf, oob, raw, true); ++} ++ ++int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr) ++{ ++ uint64_t die_addr; ++ uint32_t page, block; ++ int ret; ++ ++ if (!snf) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ die_addr = mtk_snand_select_die_address(snf, addr); ++ block = die_addr >> snf->erasesize_shift; ++ page = block << (snf->erasesize_shift - snf->writesize_shift); ++ ++ ret = mtk_snand_write_enable(snf); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_page_op(snf, page, SNAND_CMD_BLOCK_ERASE); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); ++ if (ret < 0) { ++ snand_log_chip(snf->pdev, ++ "Block erase command timed out on block %u\n", ++ block); ++ return ret; ++ } ++ ++ if (ret & SNAND_STATUS_ERASE_FAIL) { ++ snand_log_chip(snf->pdev, ++ "Block erase failed on block %u\n", block); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_block_isbad_std(struct mtk_snand *snf, uint64_t addr) ++{ ++ int ret; ++ ++ ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true, ++ false); ++ if (ret && ret != -EBADMSG) ++ return ret; ++ ++ return snf->buf_cache[0] != 0xff; ++} ++ ++static int mtk_snand_block_isbad_mtk(struct mtk_snand *snf, uint64_t addr) ++{ ++ int ret; ++ ++ ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true, ++ true); ++ if (ret && ret != -EBADMSG) ++ return ret; ++ ++ return snf->buf_cache[0] != 0xff; ++} ++ ++int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr) ++{ ++ if (!snf) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ addr &= ~snf->erasesize_mask; ++ ++ if (snf->nfi_soc->bbm_swap) ++ return mtk_snand_block_isbad_std(snf, addr); ++ ++ return mtk_snand_block_isbad_mtk(snf, addr); ++} ++ ++static int mtk_snand_block_markbad_std(struct mtk_snand *snf, uint64_t addr) ++{ ++ /* Standard BBM position */ ++ memset(snf->buf_cache, 0xff, snf->oobsize); ++ snf->buf_cache[0] = 0; ++ ++ return mtk_snand_do_write_page(snf, addr, NULL, snf->buf_cache, true, ++ false); ++} ++ ++static int mtk_snand_block_markbad_mtk(struct mtk_snand *snf, uint64_t addr) ++{ ++ /* Write the whole page with zeros */ ++ memset(snf->buf_cache, 0, snf->writesize + snf->oobsize); ++ ++ return mtk_snand_do_write_page(snf, addr, snf->buf_cache, ++ snf->buf_cache + snf->writesize, true, ++ true); ++} ++ ++int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr) ++{ ++ if (!snf) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ addr &= ~snf->erasesize_mask; ++ ++ if (snf->nfi_soc->bbm_swap) ++ return mtk_snand_block_markbad_std(snf, addr); ++ ++ return mtk_snand_block_markbad_mtk(snf, addr); ++} ++ ++int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw, ++ const uint8_t *oobbuf, size_t ooblen) ++{ ++ size_t len = ooblen, sect_fdm_len; ++ const uint8_t *oob = oobbuf; ++ uint32_t step = 0; ++ ++ if (!snf || !oobraw || !oob) ++ return -EINVAL; ++ ++ while (len && step < snf->ecc_steps) { ++ sect_fdm_len = snf->nfi_soc->fdm_size - 1; ++ if (sect_fdm_len > len) ++ sect_fdm_len = len; ++ ++ memcpy(oobraw + step * snf->nfi_soc->fdm_size + 1, oob, ++ sect_fdm_len); ++ ++ len -= sect_fdm_len; ++ oob += sect_fdm_len; ++ step++; ++ } ++ ++ return len; ++} ++ ++int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf, ++ size_t ooblen, const uint8_t *oobraw) ++{ ++ size_t len = ooblen, sect_fdm_len; ++ uint8_t *oob = oobbuf; ++ uint32_t step = 0; ++ ++ if (!snf || !oobraw || !oob) ++ return -EINVAL; ++ ++ while (len && step < snf->ecc_steps) { ++ sect_fdm_len = snf->nfi_soc->fdm_size - 1; ++ if (sect_fdm_len > len) ++ sect_fdm_len = len; ++ ++ memcpy(oob, oobraw + step * snf->nfi_soc->fdm_size + 1, ++ sect_fdm_len); ++ ++ len -= sect_fdm_len; ++ oob += sect_fdm_len; ++ step++; ++ } ++ ++ return len; ++} ++ ++int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr, ++ void *buf, void *oob, size_t ooblen, ++ size_t *actualooblen, bool raw) ++{ ++ int ret, oobremain; ++ ++ if (!snf) ++ return -EINVAL; ++ ++ if (!oob) ++ return mtk_snand_read_page(snf, addr, buf, NULL, raw); ++ ++ ret = mtk_snand_read_page(snf, addr, buf, snf->buf_cache, raw); ++ if (ret && ret != -EBADMSG) { ++ if (actualooblen) ++ *actualooblen = 0; ++ return ret; ++ } ++ ++ oobremain = mtk_snand_transfer_oob(snf, oob, ooblen, snf->buf_cache); ++ if (actualooblen) ++ *actualooblen = ooblen - oobremain; ++ ++ return ret; ++} ++ ++int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr, ++ const void *buf, const void *oob, ++ size_t ooblen, size_t *actualooblen, bool raw) ++{ ++ int oobremain; ++ ++ if (!snf) ++ return -EINVAL; ++ ++ if (!oob) ++ return mtk_snand_write_page(snf, addr, buf, NULL, raw); ++ ++ memset(snf->buf_cache, 0xff, snf->oobsize); ++ oobremain = mtk_snand_fill_oob(snf, snf->buf_cache, oob, ooblen); ++ if (actualooblen) ++ *actualooblen = ooblen - oobremain; ++ ++ return mtk_snand_write_page(snf, addr, buf, snf->buf_cache, raw); ++} ++ ++int mtk_snand_get_chip_info(struct mtk_snand *snf, ++ struct mtk_snand_chip_info *info) ++{ ++ if (!snf || !info) ++ return -EINVAL; ++ ++ info->model = snf->model; ++ info->chipsize = snf->size; ++ info->blocksize = snf->erasesize; ++ info->pagesize = snf->writesize; ++ info->sparesize = snf->oobsize; ++ info->spare_per_sector = snf->spare_per_sector; ++ info->fdm_size = snf->nfi_soc->fdm_size; ++ info->fdm_ecc_size = snf->nfi_soc->fdm_ecc_size; ++ info->num_sectors = snf->ecc_steps; ++ info->sector_size = snf->nfi_soc->sector_size; ++ info->ecc_strength = snf->ecc_strength; ++ info->ecc_bytes = snf->ecc_bytes; ++ ++ return 0; ++} ++ ++int mtk_snand_irq_process(struct mtk_snand *snf) ++{ ++ uint32_t sta, ien; ++ ++ if (!snf) ++ return -EINVAL; ++ ++ sta = nfi_read32(snf, NFI_INTR_STA); ++ ien = nfi_read32(snf, NFI_INTR_EN); ++ ++ if (!(sta & ien)) ++ return 0; ++ ++ nfi_write32(snf, NFI_INTR_EN, 0); ++ irq_completion_done(snf->pdev); ++ ++ return 1; ++} ++ ++static int mtk_snand_select_spare_per_sector(struct mtk_snand *snf) ++{ ++ uint32_t spare_per_step = snf->oobsize / snf->ecc_steps; ++ int i, mul = 1; ++ ++ /* ++ * If we're using the 1KB sector size, HW will automatically ++ * double the spare size. So we should only use half of the value. ++ */ ++ if (snf->nfi_soc->sector_size == 1024) ++ mul = 2; ++ ++ spare_per_step /= mul; ++ ++ for (i = snf->nfi_soc->num_spare_size - 1; i >= 0; i--) { ++ if (snf->nfi_soc->spare_sizes[i] <= spare_per_step) { ++ snf->spare_per_sector = snf->nfi_soc->spare_sizes[i]; ++ snf->spare_per_sector *= mul; ++ return i; ++ } ++ } ++ ++ snand_log_nfi(snf->pdev, ++ "Page size %u+%u is not supported\n", snf->writesize, ++ snf->oobsize); ++ ++ return -1; ++} ++ ++static int mtk_snand_pagefmt_setup(struct mtk_snand *snf) ++{ ++ uint32_t spare_size_idx, spare_size_shift, pagesize_idx; ++ uint32_t sector_size_512; ++ ++ if (snf->nfi_soc->sector_size == 512) { ++ sector_size_512 = NFI_SEC_SEL_512; ++ spare_size_shift = NFI_SPARE_SIZE_S; ++ } else { ++ sector_size_512 = 0; ++ spare_size_shift = NFI_SPARE_SIZE_LS_S; ++ } ++ ++ switch (snf->writesize) { ++ case SZ_512: ++ pagesize_idx = NFI_PAGE_SIZE_512_2K; ++ break; ++ case SZ_2K: ++ if (snf->nfi_soc->sector_size == 512) ++ pagesize_idx = NFI_PAGE_SIZE_2K_4K; ++ else ++ pagesize_idx = NFI_PAGE_SIZE_512_2K; ++ break; ++ case SZ_4K: ++ if (snf->nfi_soc->sector_size == 512) ++ pagesize_idx = NFI_PAGE_SIZE_4K_8K; ++ else ++ pagesize_idx = NFI_PAGE_SIZE_2K_4K; ++ break; ++ case SZ_8K: ++ if (snf->nfi_soc->sector_size == 512) ++ pagesize_idx = NFI_PAGE_SIZE_8K_16K; ++ else ++ pagesize_idx = NFI_PAGE_SIZE_4K_8K; ++ break; ++ case SZ_16K: ++ pagesize_idx = NFI_PAGE_SIZE_8K_16K; ++ break; ++ default: ++ snand_log_nfi(snf->pdev, "Page size %u is not supported\n", ++ snf->writesize); ++ return -ENOTSUPP; ++ } ++ ++ spare_size_idx = mtk_snand_select_spare_per_sector(snf); ++ if (unlikely(spare_size_idx < 0)) ++ return -ENOTSUPP; ++ ++ snf->raw_sector_size = snf->nfi_soc->sector_size + ++ snf->spare_per_sector; ++ ++ /* Setup page format */ ++ nfi_write32(snf, NFI_PAGEFMT, ++ (snf->nfi_soc->fdm_ecc_size << NFI_FDM_ECC_NUM_S) | ++ (snf->nfi_soc->fdm_size << NFI_FDM_NUM_S) | ++ (spare_size_idx << spare_size_shift) | ++ (pagesize_idx << NFI_PAGE_SIZE_S) | ++ sector_size_512); ++ ++ return 0; ++} ++ ++static enum snand_flash_io mtk_snand_select_opcode(struct mtk_snand *snf, ++ uint32_t snfi_caps, uint8_t *opcode, ++ uint8_t *dummy, ++ const struct snand_io_cap *op_cap) ++{ ++ uint32_t i, caps; ++ ++ caps = snfi_caps & op_cap->caps; ++ ++ i = fls(caps); ++ if (i > 0) { ++ *opcode = op_cap->opcodes[i - 1].opcode; ++ if (dummy) ++ *dummy = op_cap->opcodes[i - 1].dummy; ++ return i - 1; ++ } ++ ++ return __SNAND_IO_MAX; ++} ++ ++static int mtk_snand_select_opcode_rfc(struct mtk_snand *snf, ++ uint32_t snfi_caps, ++ const struct snand_io_cap *op_cap) ++{ ++ enum snand_flash_io idx; ++ ++ static const uint8_t rfc_modes[__SNAND_IO_MAX] = { ++ [SNAND_IO_1_1_1] = DATA_READ_MODE_X1, ++ [SNAND_IO_1_1_2] = DATA_READ_MODE_X2, ++ [SNAND_IO_1_2_2] = DATA_READ_MODE_DUAL, ++ [SNAND_IO_1_1_4] = DATA_READ_MODE_X4, ++ [SNAND_IO_1_4_4] = DATA_READ_MODE_QUAD, ++ }; ++ ++ idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_rfc, ++ &snf->dummy_rfc, op_cap); ++ if (idx >= __SNAND_IO_MAX) { ++ snand_log_snfi(snf->pdev, ++ "No capable opcode for read from cache\n"); ++ return -ENOTSUPP; ++ } ++ ++ snf->mode_rfc = rfc_modes[idx]; ++ ++ if (idx == SNAND_IO_1_1_4 || idx == SNAND_IO_1_4_4) ++ snf->quad_spi_op = true; ++ ++ return 0; ++} ++ ++static int mtk_snand_select_opcode_pl(struct mtk_snand *snf, uint32_t snfi_caps, ++ const struct snand_io_cap *op_cap) ++{ ++ enum snand_flash_io idx; ++ ++ static const uint8_t pl_modes[__SNAND_IO_MAX] = { ++ [SNAND_IO_1_1_1] = 0, ++ [SNAND_IO_1_1_4] = 1, ++ }; ++ ++ idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_pl, ++ NULL, op_cap); ++ if (idx >= __SNAND_IO_MAX) { ++ snand_log_snfi(snf->pdev, ++ "No capable opcode for program load\n"); ++ return -ENOTSUPP; ++ } ++ ++ snf->mode_pl = pl_modes[idx]; ++ ++ if (idx == SNAND_IO_1_1_4) ++ snf->quad_spi_op = true; ++ ++ return 0; ++} ++ ++static int mtk_snand_setup(struct mtk_snand *snf, ++ const struct snand_flash_info *snand_info) ++{ ++ const struct snand_mem_org *memorg = &snand_info->memorg; ++ uint32_t i, msg_size, snfi_caps; ++ int ret; ++ ++ /* Calculate flash memory organization */ ++ snf->model = snand_info->model; ++ snf->writesize = memorg->pagesize; ++ snf->oobsize = memorg->sparesize; ++ snf->erasesize = snf->writesize * memorg->pages_per_block; ++ snf->die_size = (uint64_t)snf->erasesize * memorg->blocks_per_die; ++ snf->size = snf->die_size * memorg->ndies; ++ snf->num_dies = memorg->ndies; ++ ++ snf->writesize_mask = snf->writesize - 1; ++ snf->erasesize_mask = snf->erasesize - 1; ++ snf->die_mask = snf->die_size - 1; ++ ++ snf->writesize_shift = ffs(snf->writesize) - 1; ++ snf->erasesize_shift = ffs(snf->erasesize) - 1; ++ snf->die_shift = mtk_snand_ffs64(snf->die_size) - 1; ++ ++ snf->select_die = snand_info->select_die; ++ ++ /* Determine opcodes for read from cache/program load */ ++ snfi_caps = SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2; ++ if (snf->snfi_quad_spi) ++ snfi_caps |= SPI_IO_1_1_4 | SPI_IO_1_4_4; ++ ++ ret = mtk_snand_select_opcode_rfc(snf, snfi_caps, snand_info->cap_rd); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_select_opcode_pl(snf, snfi_caps, snand_info->cap_pl); ++ if (ret) ++ return ret; ++ ++ /* ECC and page format */ ++ snf->ecc_steps = snf->writesize / snf->nfi_soc->sector_size; ++ if (snf->ecc_steps > snf->nfi_soc->max_sectors) { ++ snand_log_nfi(snf->pdev, "Page size %u is not supported\n", ++ snf->writesize); ++ return -ENOTSUPP; ++ } ++ ++ ret = mtk_snand_pagefmt_setup(snf); ++ if (ret) ++ return ret; ++ ++ msg_size = snf->nfi_soc->sector_size + snf->nfi_soc->fdm_ecc_size; ++ ret = mtk_ecc_setup(snf, snf->nfi_base + NFI_FDM0L, ++ snf->spare_per_sector - snf->nfi_soc->fdm_size, ++ msg_size); ++ if (ret) ++ return ret; ++ ++ nfi_write16(snf, NFI_CNFG, 0); ++ ++ /* Tuning options */ ++ nfi_write16(snf, NFI_DEBUG_CON1, WBUF_EN); ++ nfi_write32(snf, SNF_DLY_CTL3, (40 << SFCK_SAM_DLY_S)); ++ ++ /* Interrupts */ ++ nfi_read32(snf, NFI_INTR_STA); ++ nfi_write32(snf, NFI_INTR_EN, 0); ++ ++ /* Clear SNF done flag */ ++ nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE | CUS_PG_DONE); ++ nfi_write32(snf, SNF_STA_CTL1, 0); ++ ++ /* Initialization on all dies */ ++ for (i = 0; i < snf->num_dies; i++) { ++ mtk_snand_select_die(snf, i); ++ ++ /* Disable On-Die ECC engine */ ++ ret = mtk_snand_ondie_ecc_control(snf, false); ++ if (ret) ++ return ret; ++ ++ /* Disable block protection */ ++ mtk_snand_unlock(snf); ++ ++ /* Enable/disable quad-spi */ ++ mtk_snand_qspi_control(snf, snf->quad_spi_op); ++ } ++ ++ mtk_snand_select_die(snf, 0); ++ ++ return 0; ++} ++ ++static int mtk_snand_id_probe(struct mtk_snand *snf, ++ const struct snand_flash_info **snand_info) ++{ ++ uint8_t id[4], op[2]; ++ int ret; ++ ++ /* Read SPI-NAND JEDEC ID, OP + dummy/addr + ID */ ++ op[0] = SNAND_CMD_READID; ++ op[1] = 0; ++ ret = mtk_snand_mac_io(snf, op, 2, id, sizeof(id)); ++ if (ret) ++ return ret; ++ ++ *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id); ++ if (*snand_info) ++ return 0; ++ ++ /* Read SPI-NAND JEDEC ID, OP + ID */ ++ op[0] = SNAND_CMD_READID; ++ ret = mtk_snand_mac_io(snf, op, 1, id, sizeof(id)); ++ if (ret) ++ return ret; ++ ++ *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id); ++ if (*snand_info) ++ return 0; ++ ++ snand_log_chip(snf->pdev, ++ "Unrecognized SPI-NAND ID: %02x %02x %02x %02x\n", ++ id[0], id[1], id[2], id[3]); ++ ++ return -EINVAL; ++} ++ ++int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata, ++ struct mtk_snand **psnf) ++{ ++ const struct snand_flash_info *snand_info; ++ struct mtk_snand tmpsnf, *snf; ++ uint32_t rawpage_size; ++ int ret; ++ ++ if (!pdata || !psnf) ++ return -EINVAL; ++ ++ if (pdata->soc >= __SNAND_SOC_MAX) { ++ snand_log_chip(dev, "Invalid SOC %u for MTK-SNAND\n", ++ pdata->soc); ++ return -EINVAL; ++ } ++ ++ /* Dummy instance only for initial reset and id probe */ ++ tmpsnf.nfi_base = pdata->nfi_base; ++ tmpsnf.ecc_base = pdata->ecc_base; ++ tmpsnf.soc = pdata->soc; ++ tmpsnf.nfi_soc = &mtk_snand_socs[pdata->soc]; ++ tmpsnf.pdev = dev; ++ ++ /* Switch to SNFI mode */ ++ writel(SPI_MODE, tmpsnf.nfi_base + SNF_CFG); ++ ++ /* Reset SNFI & NFI */ ++ mtk_snand_mac_reset(&tmpsnf); ++ mtk_nfi_reset(&tmpsnf); ++ ++ /* Reset SPI-NAND chip */ ++ ret = mtk_snand_chip_reset(&tmpsnf); ++ if (ret) { ++ snand_log_chip(dev, "Failed to reset SPI-NAND chip\n"); ++ return ret; ++ } ++ ++ /* Probe SPI-NAND flash by JEDEC ID */ ++ ret = mtk_snand_id_probe(&tmpsnf, &snand_info); ++ if (ret) ++ return ret; ++ ++ rawpage_size = snand_info->memorg.pagesize + ++ snand_info->memorg.sparesize; ++ ++ /* Allocate memory for instance and cache */ ++ snf = generic_mem_alloc(dev, sizeof(*snf) + rawpage_size); ++ if (!snf) { ++ snand_log_chip(dev, "Failed to allocate memory for instance\n"); ++ return -ENOMEM; ++ } ++ ++ snf->buf_cache = (uint8_t *)((uintptr_t)snf + sizeof(*snf)); ++ ++ /* Allocate memory for DMA buffer */ ++ snf->page_cache = dma_mem_alloc(dev, rawpage_size); ++ if (!snf->page_cache) { ++ generic_mem_free(dev, snf); ++ snand_log_chip(dev, ++ "Failed to allocate memory for DMA buffer\n"); ++ return -ENOMEM; ++ } ++ ++ /* Fill up instance */ ++ snf->pdev = dev; ++ snf->nfi_base = pdata->nfi_base; ++ snf->ecc_base = pdata->ecc_base; ++ snf->soc = pdata->soc; ++ snf->nfi_soc = &mtk_snand_socs[pdata->soc]; ++ snf->snfi_quad_spi = pdata->quad_spi; ++ ++ /* Initialize SNFI & ECC engine */ ++ ret = mtk_snand_setup(snf, snand_info); ++ if (ret) { ++ dma_mem_free(dev, snf->page_cache); ++ generic_mem_free(dev, snf); ++ return ret; ++ } ++ ++ *psnf = snf; ++ ++ return 0; ++} ++ ++int mtk_snand_cleanup(struct mtk_snand *snf) ++{ ++ if (!snf) ++ return 0; ++ ++ dma_mem_free(snf->pdev, snf->page_cache); ++ generic_mem_free(snf->pdev, snf); ++ ++ return 0; ++} +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand.h +@@ -0,0 +1,77 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _MTK_SNAND_H_ ++#define _MTK_SNAND_H_ ++ ++#ifndef PRIVATE_MTK_SNAND_HEADER ++#include ++#include ++#include ++#endif ++ ++enum mtk_snand_soc { ++ SNAND_SOC_MT7622, ++ SNAND_SOC_MT7629, ++ SNAND_SOC_MT7986, ++ ++ __SNAND_SOC_MAX ++}; ++ ++struct mtk_snand_platdata { ++ void *nfi_base; ++ void *ecc_base; ++ enum mtk_snand_soc soc; ++ bool quad_spi; ++}; ++ ++struct mtk_snand_chip_info { ++ const char *model; ++ uint64_t chipsize; ++ uint32_t blocksize; ++ uint32_t pagesize; ++ uint32_t sparesize; ++ uint32_t spare_per_sector; ++ uint32_t fdm_size; ++ uint32_t fdm_ecc_size; ++ uint32_t num_sectors; ++ uint32_t sector_size; ++ uint32_t ecc_strength; ++ uint32_t ecc_bytes; ++}; ++ ++struct mtk_snand; ++struct snand_flash_info; ++ ++int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata, ++ struct mtk_snand **psnf); ++int mtk_snand_cleanup(struct mtk_snand *snf); ++ ++int mtk_snand_chip_reset(struct mtk_snand *snf); ++int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf, ++ void *oob, bool raw); ++int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf, ++ const void *oob, bool raw); ++int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr); ++int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr); ++int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr); ++int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw, ++ const uint8_t *oobbuf, size_t ooblen); ++int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf, ++ size_t ooblen, const uint8_t *oobraw); ++int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr, ++ void *buf, void *oob, size_t ooblen, ++ size_t *actualooblen, bool raw); ++int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr, ++ const void *buf, const void *oob, ++ size_t ooblen, size_t *actualooblen, ++ bool raw); ++int mtk_snand_get_chip_info(struct mtk_snand *snf, ++ struct mtk_snand_chip_info *info); ++int mtk_snand_irq_process(struct mtk_snand *snf); ++ ++#endif /* _MTK_SNAND_H_ */ diff --git a/package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch b/package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch new file mode 100644 index 0000000000..0ec20eb91c --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch @@ -0,0 +1,174 @@ +From 0c857d4c9cd9dc8e8ebba18cf9e9b10513ccb35d Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 08:57:29 +0800 +Subject: [PATCH 15/21] mtd: mtk-snand: add support for SPL + +Add support to initialize SPI-NAND in SPL. +Add implementation for SPL NAND loader. + +Signed-off-by: Weijie Gao +--- + drivers/mtd/mtk-snand/Kconfig | 6 ++ + drivers/mtd/mtk-snand/Makefile | 4 + + drivers/mtd/mtk-snand/mtk-snand-spl.c | 132 ++++++++++++++++++++++++++ + 3 files changed, 142 insertions(+) + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-spl.c + +--- a/drivers/mtd/mtk-snand/Kconfig ++++ b/drivers/mtd/mtk-snand/Kconfig +@@ -19,3 +19,9 @@ config MTK_SPI_NAND_MTD + help + This option enables access to SPI-NAND flashes through the + MTD interface of MediaTek SPI NAND Flash Controller ++ ++config SPL_MTK_SPI_NAND ++ tristate "SPL support for MediaTek SPI NAND flash controller" ++ depends on MTK_SPI_NAND ++ help ++ This option enables access to SPI-NAND flashes in the SPL stage +--- a/drivers/mtd/mtk-snand/Makefile ++++ b/drivers/mtd/mtk-snand/Makefile +@@ -8,4 +8,8 @@ + obj-y += mtk-snand.o mtk-snand-ecc.o mtk-snand-ids.o mtk-snand-os.o + obj-$(CONFIG_MTK_SPI_NAND_MTD) += mtk-snand-mtd.o + ++ifdef CONFIG_SPL_BUILD ++obj-$(CONFIG_SPL_MTK_SPI_NAND) += mtk-snand-spl.o ++endif ++ + ccflags-y += -DPRIVATE_MTK_SNAND_HEADER +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-spl.c +@@ -0,0 +1,132 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mtk-snand.h" ++ ++static struct mtk_snand *snf; ++static struct mtk_snand_chip_info cinfo; ++static u32 oobavail; ++ ++static u8 *page_cache; ++ ++int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) ++{ ++ u32 sizeremain = size, chunksize, leading; ++ uint32_t off = offs, writesize_mask = cinfo.pagesize - 1; ++ uint8_t *ptr = dst; ++ int ret; ++ ++ if (!snf) ++ return -ENODEV; ++ ++ while (sizeremain) { ++ WATCHDOG_RESET(); ++ ++ leading = off & writesize_mask; ++ chunksize = cinfo.pagesize - leading; ++ if (chunksize > sizeremain) ++ chunksize = sizeremain; ++ ++ if (chunksize == cinfo.pagesize) { ++ ret = mtk_snand_read_page(snf, off - leading, ptr, ++ NULL, false); ++ if (ret) ++ break; ++ } else { ++ ret = mtk_snand_read_page(snf, off - leading, ++ page_cache, NULL, false); ++ if (ret) ++ break; ++ ++ memcpy(ptr, page_cache + leading, chunksize); ++ } ++ ++ off += chunksize; ++ ptr += chunksize; ++ sizeremain -= chunksize; ++ } ++ ++ return ret; ++} ++ ++void nand_init(void) ++{ ++ struct mtk_snand_platdata mtk_snand_pdata = {}; ++ struct udevice *dev; ++ fdt_addr_t base; ++ int ret; ++ ++ ret = uclass_get_device_by_driver(UCLASS_MTD, DM_DRIVER_GET(mtk_snand), ++ &dev); ++ if (ret) { ++ printf("mtk-snand-spl: Device instance not found!\n"); ++ return; ++ } ++ ++ base = dev_read_addr_name(dev, "nfi"); ++ if (base == FDT_ADDR_T_NONE) { ++ printf("mtk-snand-spl: NFI base not set\n"); ++ return; ++ } ++ mtk_snand_pdata.nfi_base = map_sysmem(base, 0); ++ ++ base = dev_read_addr_name(dev, "ecc"); ++ if (base == FDT_ADDR_T_NONE) { ++ printf("mtk-snand-spl: ECC base not set\n"); ++ return; ++ } ++ mtk_snand_pdata.ecc_base = map_sysmem(base, 0); ++ ++ mtk_snand_pdata.soc = dev_get_driver_data(dev); ++ mtk_snand_pdata.quad_spi = dev_read_bool(dev, "quad-spi"); ++ ++ ret = mtk_snand_init(NULL, &mtk_snand_pdata, &snf); ++ if (ret) { ++ printf("mtk-snand-spl: failed to initialize SPI-NAND\n"); ++ return; ++ } ++ ++ mtk_snand_get_chip_info(snf, &cinfo); ++ ++ oobavail = cinfo.num_sectors * (cinfo.fdm_size - 1); ++ ++ printf("SPI-NAND: %s (%uMB)\n", cinfo.model, ++ (u32)(cinfo.chipsize >> 20)); ++ ++ page_cache = malloc(cinfo.pagesize + cinfo.sparesize); ++ if (!page_cache) { ++ mtk_snand_cleanup(snf); ++ printf("mtk-snand-spl: failed to allocate page cache\n"); ++ } ++} ++ ++void nand_deselect(void) ++{ ++ ++} ++ ++static const struct udevice_id mtk_snand_ids[] = { ++ { .compatible = "mediatek,mt7622-snand", .data = SNAND_SOC_MT7622 }, ++ { .compatible = "mediatek,mt7629-snand", .data = SNAND_SOC_MT7629 }, ++ { .compatible = "mediatek,mt7986-snand", .data = SNAND_SOC_MT7986 }, ++ { /* sentinel */ }, ++}; ++ ++U_BOOT_DRIVER(mtk_snand) = { ++ .name = "mtk-snand", ++ .id = UCLASS_MTD, ++ .of_match = mtk_snand_ids, ++ .flags = DM_FLAG_PRE_RELOC, ++}; diff --git a/package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch b/package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch new file mode 100644 index 0000000000..932c4b736a --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch @@ -0,0 +1,409 @@ +From eaa9bc597e0bf8bcd1486ea49c8c7c070a37a8aa Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 10:11:32 +0800 +Subject: [PATCH 16/21] env: add support for generic MTD device + +Add an env driver for generic MTD device. + +Signed-off-by: Weijie Gao +--- + cmd/nvedit.c | 3 +- + env/Kconfig | 37 +++++- + env/Makefile | 1 + + env/env.c | 3 + + env/mtd.c | 256 +++++++++++++++++++++++++++++++++++++++++ + include/env_internal.h | 1 + + tools/Makefile | 1 + + 7 files changed, 299 insertions(+), 3 deletions(-) + create mode 100644 env/mtd.c + +--- a/cmd/nvedit.c ++++ b/cmd/nvedit.c +@@ -50,6 +50,7 @@ DECLARE_GLOBAL_DATA_PTR; + defined(CONFIG_ENV_IS_IN_MMC) || \ + defined(CONFIG_ENV_IS_IN_FAT) || \ + defined(CONFIG_ENV_IS_IN_EXT4) || \ ++ defined(CONFIG_ENV_IS_IN_MTD) || \ + defined(CONFIG_ENV_IS_IN_NAND) || \ + defined(CONFIG_ENV_IS_IN_NVRAM) || \ + defined(CONFIG_ENV_IS_IN_ONENAND) || \ +@@ -64,7 +65,7 @@ DECLARE_GLOBAL_DATA_PTR; + + #if !defined(ENV_IS_IN_DEVICE) && \ + !defined(CONFIG_ENV_IS_NOWHERE) +-# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|\ ++# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|MTD|\ + NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE + #endif + +--- a/env/Kconfig ++++ b/env/Kconfig +@@ -19,7 +19,7 @@ config ENV_IS_NOWHERE + !ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \ + !ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \ + !ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \ +- !ENV_IS_IN_UBI ++ !ENV_IS_IN_UBI && !ENV_IS_IN_MTD + help + Define this if you don't want to or can't have an environment stored + on a storage medium. In this case the environment will still exist +@@ -207,6 +207,27 @@ config ENV_IS_IN_MMC + This value is also in units of bytes, but must also be aligned to + an MMC sector boundary. + ++config ENV_IS_IN_MTD ++ bool "Environment in a MTD device" ++ depends on !CHAIN_OF_TRUST ++ depends on MTD ++ help ++ Define this if you have a MTD device which you want to use for ++ the environment. ++ ++ - CONFIG_ENV_MTD_NAME: ++ - CONFIG_ENV_OFFSET: ++ - CONFIG_ENV_SIZE: ++ ++ These three #defines specify the MTD device where the environment ++ is stored, offset and size of the environment area within the MTD ++ device. CONFIG_ENV_OFFSET must be aligned to an erase block boundary. ++ ++ - CONFIG_ENV_SIZE_REDUND: ++ ++ This #define specify the maximum size allowed for read/write/erase ++ with skipped bad blocks starting from ENV_OFFSET. ++ + config ENV_IS_IN_NAND + bool "Environment in a NAND device" + depends on !CHAIN_OF_TRUST +@@ -513,10 +534,16 @@ config ENV_ADDR_REDUND + Offset from the start of the device (or partition) of the redundant + environment location. + ++config ENV_MTD_NAME ++ string "Name of the MTD device storing the environment" ++ depends on ENV_IS_IN_MTD ++ help ++ Name of the MTD device that stores the environment ++ + config ENV_OFFSET + hex "Environment offset" + depends on ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \ +- ENV_IS_IN_SPI_FLASH ++ ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD + default 0x3f8000 if ARCH_ROCKCHIP && ENV_IS_IN_MMC + default 0x140000 if ARCH_ROCKCHIP && ENV_IS_IN_SPI_FLASH + default 0x88000 if ARCH_SUNXI +@@ -560,6 +587,12 @@ config ENV_SECT_SIZE + help + Size of the sector containing the environment. + ++config ENV_SIZE_REDUND ++ hex "Redundant environment size" ++ depends on ENV_IS_IN_MTD ++ help ++ The maximum size allowed for read/write/erase with skipped bad blocks. ++ + config ENV_UBI_PART + string "UBI partition name" + depends on ENV_IS_IN_UBI +--- a/env/Makefile ++++ b/env/Makefile +@@ -26,6 +26,7 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_NOWHERE) + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MMC) += mmc.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) += fat.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_EXT4) += ext4.o ++obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MTD) += mtd.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NAND) += nand.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_SPI_FLASH) += sf.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o +--- a/env/env.c ++++ b/env/env.c +@@ -69,6 +69,9 @@ static enum env_location env_locations[] + #ifdef CONFIG_ENV_IS_IN_MMC + ENVL_MMC, + #endif ++#ifdef CONFIG_ENV_IS_IN_MTD ++ ENVL_MTD, ++#endif + #ifdef CONFIG_ENV_IS_IN_NAND + ENVL_NAND, + #endif +--- /dev/null ++++ b/env/mtd.c +@@ -0,0 +1,256 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2021 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if CONFIG_ENV_SIZE_REDUND < CONFIG_ENV_SIZE ++#undef CONFIG_ENV_SIZE_REDUND ++#define CONFIG_ENV_SIZE_REDUND CONFIG_ENV_SIZE ++#endif ++ ++#if defined(ENV_IS_EMBEDDED) ++env_t *env_ptr = &environment; ++#else /* ! ENV_IS_EMBEDDED */ ++env_t *env_ptr; ++#endif /* ENV_IS_EMBEDDED */ ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++static int env_mtd_init(void) ++{ ++#if defined(ENV_IS_EMBEDDED) ++ int crc1_ok = 0, crc2_ok = 0; ++ env_t *tmp_env1; ++ ++ tmp_env1 = env_ptr; ++ crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc; ++ ++ if (!crc1_ok && !crc2_ok) { ++ gd->env_addr = 0; ++ gd->env_valid = ENV_INVALID; ++ ++ return 0; ++ } else if (crc1_ok && !crc2_ok) { ++ gd->env_valid = ENV_VALID; ++ } ++ ++ if (gd->env_valid == ENV_VALID) ++ env_ptr = tmp_env1; ++ ++ gd->env_addr = (ulong)env_ptr->data; ++ ++#else /* ENV_IS_EMBEDDED */ ++ gd->env_addr = (ulong)&default_environment[0]; ++ gd->env_valid = ENV_VALID; ++#endif /* ENV_IS_EMBEDDED */ ++ ++ return 0; ++} ++ ++static struct mtd_info *env_mtd_get_dev(void) ++{ ++ struct mtd_info *mtd; ++ ++ mtd_probe_devices(); ++ ++ mtd = get_mtd_device_nm(CONFIG_ENV_MTD_NAME); ++ if (IS_ERR(mtd) || !mtd) { ++ printf("MTD device '%s' not found\n", CONFIG_ENV_MTD_NAME); ++ return NULL; ++ } ++ ++ return mtd; ++} ++ ++static inline bool mtd_addr_is_block_aligned(struct mtd_info *mtd, u64 addr) ++{ ++ return (addr & mtd->erasesize_mask) == 0; ++} ++ ++static int mtd_io_skip_bad(struct mtd_info *mtd, bool read, loff_t offset, ++ size_t length, size_t redund, u8 *buffer) ++{ ++ struct mtd_oob_ops io_op = {}; ++ size_t remaining = length; ++ loff_t off, end; ++ int ret; ++ ++ io_op.mode = MTD_OPS_PLACE_OOB; ++ io_op.len = mtd->writesize; ++ io_op.datbuf = (void *)buffer; ++ ++ /* Search for the first good block after the given offset */ ++ off = offset; ++ end = (off + redund) | (mtd->erasesize - 1); ++ while (mtd_block_isbad(mtd, off) && off < end) ++ off += mtd->erasesize; ++ ++ /* Reached end position */ ++ if (off >= end) ++ return -EIO; ++ ++ /* Loop over the pages to do the actual read/write */ ++ while (remaining) { ++ /* Skip the block if it is bad */ ++ if (mtd_addr_is_block_aligned(mtd, off) && ++ mtd_block_isbad(mtd, off)) { ++ off += mtd->erasesize; ++ continue; ++ } ++ ++ if (read) ++ ret = mtd_read_oob(mtd, off, &io_op); ++ else ++ ret = mtd_write_oob(mtd, off, &io_op); ++ ++ if (ret) { ++ printf("Failure while %s at offset 0x%llx\n", ++ read ? "reading" : "writing", off); ++ break; ++ } ++ ++ off += io_op.retlen; ++ remaining -= io_op.retlen; ++ io_op.datbuf += io_op.retlen; ++ io_op.oobbuf += io_op.oobretlen; ++ ++ /* Reached end position */ ++ if (off >= end) ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_CMD_SAVEENV ++static int mtd_erase_skip_bad(struct mtd_info *mtd, loff_t offset, ++ size_t length, size_t redund) ++{ ++ struct erase_info erase_op = {}; ++ loff_t end = (offset + redund) | (mtd->erasesize - 1); ++ int ret; ++ ++ erase_op.mtd = mtd; ++ erase_op.addr = offset; ++ erase_op.len = length; ++ ++ while (erase_op.len) { ++ ret = mtd_erase(mtd, &erase_op); ++ ++ /* Abort if its not a bad block error */ ++ if (ret != -EIO) ++ return ret; ++ ++ printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr); ++ ++ /* Skip bad block and continue behind it */ ++ erase_op.len -= erase_op.fail_addr - erase_op.addr; ++ erase_op.len -= mtd->erasesize; ++ erase_op.addr = erase_op.fail_addr + mtd->erasesize; ++ ++ /* Reached end position */ ++ if (erase_op.addr >= end) ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int env_mtd_save(void) ++{ ++ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); ++ struct mtd_info *mtd; ++ int ret = 0; ++ ++ ret = env_export(env_new); ++ if (ret) ++ return ret; ++ ++ mtd = env_mtd_get_dev(); ++ if (!mtd) ++ return 1; ++ ++ printf("Erasing on MTD device '%s'... ", mtd->name); ++ ++ ret = mtd_erase_skip_bad(mtd, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, ++ CONFIG_ENV_SIZE_REDUND); ++ ++ puts(ret ? "FAILED\n" : "OK\n"); ++ ++ if (ret) { ++ put_mtd_device(mtd); ++ return 1; ++ } ++ ++ printf("Writing to MTD device '%s'... ", mtd->name); ++ ++ ret = mtd_io_skip_bad(mtd, false, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, ++ CONFIG_ENV_SIZE_REDUND, (u8 *)env_new); ++ ++ puts(ret ? "FAILED\n" : "OK\n"); ++ ++ put_mtd_device(mtd); ++ ++ return !!ret; ++} ++#endif /* CONFIG_CMD_SAVEENV */ ++ ++static int readenv(size_t offset, u_char *buf) ++{ ++ struct mtd_info *mtd; ++ int ret; ++ ++ mtd = env_mtd_get_dev(); ++ if (!mtd) ++ return 1; ++ ++ ret = mtd_io_skip_bad(mtd, true, offset, CONFIG_ENV_SIZE, ++ CONFIG_ENV_SIZE_REDUND, buf); ++ ++ put_mtd_device(mtd); ++ ++ return !!ret; ++} ++ ++static int env_mtd_load(void) ++{ ++#if !defined(ENV_IS_EMBEDDED) ++ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); ++ int ret; ++ ++ ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf); ++ if (ret) { ++ env_set_default("readenv() failed", 0); ++ return -EIO; ++ } ++ ++ return env_import(buf, 1, H_EXTERNAL); ++#endif /* ! ENV_IS_EMBEDDED */ ++ ++ return 0; ++} ++ ++U_BOOT_ENV_LOCATION(mtd) = { ++ .location = ENVL_MTD, ++ ENV_NAME("MTD") ++ .load = env_mtd_load, ++#if defined(CONFIG_CMD_SAVEENV) ++ .save = env_save_ptr(env_mtd_save), ++#endif ++ .init = env_mtd_init, ++}; +--- a/include/env_internal.h ++++ b/include/env_internal.h +@@ -131,6 +131,7 @@ enum env_location { + ENVL_FAT, + ENVL_FLASH, + ENVL_MMC, ++ ENVL_MTD, + ENVL_NAND, + ENVL_NVRAM, + ENVL_ONENAND, +--- a/tools/Makefile ++++ b/tools/Makefile +@@ -22,6 +22,7 @@ ENVCRC-$(CONFIG_ENV_IS_EMBEDDED) = y + ENVCRC-$(CONFIG_ENV_IS_IN_EEPROM) = y + ENVCRC-$(CONFIG_ENV_IS_IN_FLASH) = y + ENVCRC-$(CONFIG_ENV_IS_IN_ONENAND) = y ++ENVCRC-$(CONFIG_ENV_IS_IN_MTD) = y + ENVCRC-$(CONFIG_ENV_IS_IN_NAND) = y + ENVCRC-$(CONFIG_ENV_IS_IN_NVRAM) = y + ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y diff --git a/package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch b/package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch new file mode 100644 index 0000000000..aa38b2745e --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch @@ -0,0 +1,266 @@ +From 47b386259625061b376f538055a4f3fbd0ab7fef Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 10:48:53 +0800 +Subject: [PATCH 17/21] board: mt7629: add support for booting from SPI-NAND + +Add support for mt7629 to boot from SPI-NAND. +Add a new defconfig for mt7629+spi-nand configuration. + +Signed-off-by: Weijie Gao +--- + arch/arm/dts/mt7629-rfb-u-boot.dtsi | 8 ++ + arch/arm/dts/mt7629-rfb.dts | 10 +++ + arch/arm/dts/mt7629.dtsi | 16 ++++ + board/mediatek/mt7629/Kconfig | 35 ++++++++- + configs/mt7629_nand_rfb_defconfig | 111 ++++++++++++++++++++++++++++ + include/configs/mt7629.h | 7 ++ + 6 files changed, 186 insertions(+), 1 deletion(-) + create mode 100644 configs/mt7629_nand_rfb_defconfig + +--- a/arch/arm/dts/mt7629-rfb-u-boot.dtsi ++++ b/arch/arm/dts/mt7629-rfb-u-boot.dtsi +@@ -40,3 +40,11 @@ + &snfi { + u-boot,dm-pre-reloc; + }; ++ ++&pinctrl { ++ u-boot,dm-pre-reloc; ++}; ++ ++&snand { ++ u-boot,dm-pre-reloc; ++}; +--- a/arch/arm/dts/mt7629-rfb.dts ++++ b/arch/arm/dts/mt7629-rfb.dts +@@ -47,9 +47,12 @@ + }; + + snfi_pins: snfi-pins { ++ u-boot,dm-pre-reloc; ++ + mux { + function = "flash"; + groups = "snfi"; ++ u-boot,dm-pre-reloc; + }; + }; + +@@ -102,6 +105,13 @@ + }; + }; + ++&snand { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&snfi_pins>; ++ status = "okay"; ++ quad-spi; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; +--- a/arch/arm/dts/mt7629.dtsi ++++ b/arch/arm/dts/mt7629.dtsi +@@ -229,6 +229,22 @@ + #size-cells = <0>; + }; + ++ snand: snand@1100d000 { ++ compatible = "mediatek,mt7629-snand"; ++ reg = <0x1100d000 0x1000>, ++ <0x1100e000 0x1000>; ++ reg-names = "nfi", "ecc"; ++ clocks = <&pericfg CLK_PERI_NFI_PD>, ++ <&pericfg CLK_PERI_SNFI_PD>, ++ <&pericfg CLK_PERI_NFIECC_PD>; ++ clock-names = "nfi_clk", "pad_clk", "ecc_clk"; ++ assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>, ++ <&topckgen CLK_TOP_NFI_INFRA_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>, ++ <&topckgen CLK_TOP_UNIVPLL2_D8>; ++ status = "disabled"; ++ }; ++ + snor: snor@11014000 { + compatible = "mediatek,mtk-snor"; + reg = <0x11014000 0x1000>; +--- a/board/mediatek/mt7629/Kconfig ++++ b/board/mediatek/mt7629/Kconfig +@@ -12,6 +12,39 @@ config MTK_SPL_PAD_SIZE + + config MTK_BROM_HEADER_INFO + string +- default "media=nor" ++ default "media=nor" if BOOT_FROM_SNOR ++ default "media=snand;nandinfo=2k+64" if BOOT_FROM_SNAND_2K_64 ++ default "media=snand;nandinfo=2k+128" if BOOT_FROM_SNAND_2K_128 ++ default "media=snand;nandinfo=4k+128" if BOOT_FROM_SNAND_4K_128 ++ default "media=snand;nandinfo=4k+256" if BOOT_FROM_SNAND_4K_256 ++ ++choice ++ prompt "Boot device" ++ default BOOT_FROM_SNOR ++ ++config BOOT_FROM_SNOR ++ bool "SPI-NOR" ++ ++config BOOT_FROM_SNAND_2K_64 ++ bool "SPI-NAND (2K+64)" ++ select MT7629_BOOT_FROM_SNAND ++ ++config BOOT_FROM_SNAND_2K_128 ++ bool "SPI-NAND (2K+128)" ++ select MT7629_BOOT_FROM_SNAND ++ ++config BOOT_FROM_SNAND_4K_128 ++ bool "SPI-NAND (4K+128)" ++ select MT7629_BOOT_FROM_SNAND ++ ++config BOOT_FROM_SNAND_4K_256 ++ bool "SPI-NAND (4K+256)" ++ select MT7629_BOOT_FROM_SNAND ++ ++endchoice ++ ++config MT7629_BOOT_FROM_SNAND ++ bool ++ default n + + endif +--- /dev/null ++++ b/configs/mt7629_nand_rfb_defconfig +@@ -0,0 +1,111 @@ ++CONFIG_ARM=y ++CONFIG_SYS_ARCH_TIMER=y ++CONFIG_SYS_THUMB_BUILD=y ++CONFIG_ARCH_MEDIATEK=y ++CONFIG_SYS_TEXT_BASE=0x41e00000 ++CONFIG_SYS_MALLOC_F_LEN=0x4000 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_ENV_SIZE=0x20000 ++CONFIG_ENV_OFFSET=0x100000 ++CONFIG_SPL_TEXT_BASE=0x201000 ++CONFIG_TARGET_MT7629=y ++CONFIG_BOOT_FROM_SNAND_2K_64=y ++CONFIG_SPL_SERIAL_SUPPORT=y ++CONFIG_SPL_DRIVERS_MISC_SUPPORT=y ++CONFIG_SPL_STACK_R_ADDR=0x40800000 ++CONFIG_SPL_PAYLOAD="u-boot.img" ++CONFIG_BUILD_TARGET="u-boot-mtk.bin" ++CONFIG_DEFAULT_DEVICE_TREE="mt7629-rfb" ++CONFIG_SPL_IMAGE="spl/u-boot-spl-mtk.bin" ++CONFIG_FIT=y ++CONFIG_FIT_VERBOSE=y ++CONFIG_BOOTDELAY=3 ++CONFIG_DEFAULT_FDT_FILE="mt7629-rfb" ++CONFIG_SYS_CONSOLE_IS_IN_ENV=y ++CONFIG_SYS_STDIO_DEREGISTER=y ++# CONFIG_DISPLAY_BOARDINFO is not set ++CONFIG_SPL_SYS_MALLOC_SIMPLE=y ++CONFIG_SPL_STACK_R=y ++CONFIG_SPL_MTD_SUPPORT=y ++CONFIG_SPL_NAND_SUPPORT=y ++CONFIG_SPL_WATCHDOG_SUPPORT=y ++CONFIG_HUSH_PARSER=y ++CONFIG_SYS_PROMPT="U-Boot> " ++CONFIG_CMD_BOOTMENU=y ++# CONFIG_BOOTM_NETBSD is not set ++# CONFIG_BOOTM_PLAN9 is not set ++# CONFIG_BOOTM_RTEMS is not set ++# CONFIG_BOOTM_VXWORKS is not set ++# CONFIG_CMD_ELF is not set ++# CONFIG_CMD_XIMG is not set ++CONFIG_CMD_BIND=y ++CONFIG_CMD_DM=y ++# CONFIG_CMD_FLASH is not set ++CONFIG_CMD_GPIO=y ++CONFIG_CMD_MTD=y ++CONFIG_CMD_USB=y ++# CONFIG_CMD_SETEXPR is not set ++# CONFIG_CMD_NFS is not set ++CONFIG_CMD_PING=y ++CONFIG_CMD_FAT=y ++CONFIG_CMD_FS_GENERIC=y ++CONFIG_CMD_LOG=y ++CONFIG_EFI_PARTITION=y ++# CONFIG_SPL_PARTITION_UUIDS is not set ++CONFIG_PARTITION_TYPE_GUID=y ++CONFIG_OF_SPL_REMOVE_PROPS="interrupt-parent assigned-clocks assigned-clock-parents" ++CONFIG_ENV_OVERWRITE=y ++CONFIG_ENV_IS_IN_MTD=y ++CONFIG_ENV_MTD_NAME="spi-nand0" ++CONFIG_ENV_SIZE_REDUND=0x40000 ++CONFIG_SYS_RELOC_GD_ENV_ADDR=y ++CONFIG_NET_RANDOM_ETHADDR=y ++CONFIG_SPL_DM_SEQ_ALIAS=y ++CONFIG_REGMAP=y ++CONFIG_SPL_REGMAP=y ++CONFIG_SYSCON=y ++CONFIG_SPL_SYSCON=y ++CONFIG_BLK=y ++CONFIG_CLK=y ++CONFIG_SPL_CLK=y ++# CONFIG_MMC is not set ++CONFIG_MTD=y ++CONFIG_DM_MTD=y ++CONFIG_MTK_SPI_NAND=y ++CONFIG_MTK_SPI_NAND_MTD=y ++CONFIG_SPL_MTK_SPI_NAND=y ++CONFIG_DM_ETH=y ++CONFIG_MEDIATEK_ETH=y ++CONFIG_PHY=y ++CONFIG_PHY_MTK_TPHY=y ++CONFIG_PINCTRL=y ++CONFIG_PINCONF=y ++CONFIG_SPL_PINCTRL=y ++CONFIG_SPL_PINCONF=y ++CONFIG_PINCTRL_MT7629=y ++CONFIG_POWER_DOMAIN=y ++CONFIG_MTK_POWER_DOMAIN=y ++CONFIG_DM_REGULATOR=y ++CONFIG_DM_REGULATOR_FIXED=y ++CONFIG_RAM=y ++CONFIG_SPL_RAM=y ++CONFIG_DM_SERIAL=y ++CONFIG_MTK_SERIAL=y ++CONFIG_SPI=y ++CONFIG_DM_SPI=y ++CONFIG_SPI_MEM=y ++CONFIG_MTK_SNFI_SPI=y ++CONFIG_SYSRESET=y ++CONFIG_SPL_SYSRESET=y ++CONFIG_SYSRESET_WATCHDOG=y ++CONFIG_USB=y ++CONFIG_DM_USB=y ++# CONFIG_SPL_DM_USB is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_MTK=y ++CONFIG_USB_STORAGE=y ++CONFIG_WDT_MTK=y ++CONFIG_FAT_WRITE=y ++CONFIG_LZMA=y ++CONFIG_SPL_LZMA=y ++# CONFIG_EFI_LOADER is not set +--- a/include/configs/mt7629.h ++++ b/include/configs/mt7629.h +@@ -30,12 +30,19 @@ + + /* Defines for SPL */ + #define CONFIG_SPL_STACK 0x106000 ++#ifdef CONFIG_MT7629_BOOT_FROM_SNAND ++#define CONFIG_SPL_MAX_SIZE SZ_128K ++#define CONFIG_SPL_MAX_FOOTPRINT SZ_128K ++#define CONFIG_SPL_PAD_TO 0x20000 ++#define CONFIG_SYS_NAND_U_BOOT_OFFS CONFIG_SPL_PAD_TO ++#else + #define CONFIG_SPL_MAX_SIZE SZ_64K + #define CONFIG_SPL_MAX_FOOTPRINT SZ_64K + #define CONFIG_SPL_PAD_TO 0x10000 + + #define CONFIG_SPI_ADDR 0x30000000 + #define CONFIG_SYS_UBOOT_BASE (CONFIG_SPI_ADDR + CONFIG_SPL_PAD_TO) ++#endif + + /* SPL -> Uboot */ + #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_TEXT_BASE + SZ_2M - \ diff --git a/package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch new file mode 100644 index 0000000000..2202911950 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch @@ -0,0 +1,76 @@ +From ec0d1899b035700a657721761ff6370b940450ab Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 10:51:43 +0800 +Subject: [PATCH 18/21] board: mt7622: use new spi-nand driver + +Enable new spi-nand driver support for mt7622_rfb_defconfig + +Signed-off-by: Weijie Gao +--- + arch/arm/dts/mt7622-rfb.dts | 7 +++++++ + arch/arm/dts/mt7622.dtsi | 16 ++++++++++++++++ + configs/mt7622_rfb_defconfig | 5 +++++ + 3 files changed, 28 insertions(+) + +--- a/arch/arm/dts/mt7622-rfb.dts ++++ b/arch/arm/dts/mt7622-rfb.dts +@@ -188,6 +188,13 @@ + }; + }; + ++&snand { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&snfi_pins>; ++ status = "okay"; ++ quad-spi; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; +--- a/arch/arm/dts/mt7622.dtsi ++++ b/arch/arm/dts/mt7622.dtsi +@@ -53,6 +53,22 @@ + #size-cells = <0>; + }; + ++ snand: snand@1100d000 { ++ compatible = "mediatek,mt7622-snand"; ++ reg = <0x1100d000 0x1000>, ++ <0x1100e000 0x1000>; ++ reg-names = "nfi", "ecc"; ++ clocks = <&pericfg CLK_PERI_NFI_PD>, ++ <&pericfg CLK_PERI_SNFI_PD>, ++ <&pericfg CLK_PERI_NFIECC_PD>; ++ clock-names = "nfi_clk", "pad_clk", "ecc_clk"; ++ assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>, ++ <&topckgen CLK_TOP_NFI_INFRA_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>, ++ <&topckgen CLK_TOP_UNIVPLL2_D8>; ++ status = "disabled"; ++ }; ++ + snor: snor@11014000 { + compatible = "mediatek,mtk-snor"; + reg = <0x11014000 0x1000>; +--- a/configs/mt7622_rfb_defconfig ++++ b/configs/mt7622_rfb_defconfig +@@ -15,6 +15,7 @@ CONFIG_LOG=y + CONFIG_SYS_PROMPT="MT7622> " + CONFIG_CMD_BOOTMENU=y + CONFIG_CMD_MMC=y ++CONFIG_CMD_MTD=y + CONFIG_CMD_PCI=y + CONFIG_CMD_SF_TEST=y + CONFIG_CMD_PING=y +@@ -28,6 +29,10 @@ CONFIG_CLK=y + CONFIG_DM_MMC=y + CONFIG_MMC_HS200_SUPPORT=y + CONFIG_MMC_MTK=y ++CONFIG_MTD=y ++CONFIG_DM_MTD=y ++CONFIG_MTK_SPI_NAND=y ++CONFIG_MTK_SPI_NAND_MTD=y + CONFIG_DM_SPI_FLASH=y + CONFIG_SPI_FLASH_EON=y + CONFIG_SPI_FLASH_GIGADEVICE=y diff --git a/package/boot/uboot-mediatek/patches/000-mtk-19-configs-mt7629-remove-unused-options-and-add-dm-comm.patch b/package/boot/uboot-mediatek/patches/000-mtk-19-configs-mt7629-remove-unused-options-and-add-dm-comm.patch new file mode 100644 index 0000000000..fadb274bfd --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-19-configs-mt7629-remove-unused-options-and-add-dm-comm.patch @@ -0,0 +1,31 @@ +From 2f7aaf3c2c127bd53d5e8bfe39e808fdd6eb99be Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 12:12:39 +0800 +Subject: [PATCH 19/21] configs: mt7629: remove unused options and add dm + command + +Remove unused bootm options +Add dm command + +Signed-off-by: Weijie Gao +--- + configs/mt7629_rfb_defconfig | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/configs/mt7629_rfb_defconfig ++++ b/configs/mt7629_rfb_defconfig +@@ -28,9 +28,14 @@ CONFIG_SPL_WATCHDOG_SUPPORT=y + CONFIG_HUSH_PARSER=y + CONFIG_SYS_PROMPT="U-Boot> " + CONFIG_CMD_BOOTMENU=y ++# CONFIG_BOOTM_NETBSD is not set ++# CONFIG_BOOTM_PLAN9 is not set ++# CONFIG_BOOTM_RTEMS is not set ++# CONFIG_BOOTM_VXWORKS is not set + # CONFIG_CMD_ELF is not set + # CONFIG_CMD_XIMG is not set + CONFIG_CMD_BIND=y ++CONFIG_CMD_DM=y + # CONFIG_CMD_FLASH is not set + CONFIG_CMD_GPIO=y + CONFIG_CMD_SF_TEST=y diff --git a/package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch b/package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch new file mode 100644 index 0000000000..228c3351ca --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch @@ -0,0 +1,33 @@ +From e5a71a0eebadfb3d75d8619a8b317eec58b2bca2 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Sat, 6 Mar 2021 16:29:33 +0800 +Subject: [PATCH 20/21] configs: mt7622: enable environment for mt7622_rfb + +Enable environment vairables for mt7622_rfb + +Signed-off-by: Weijie Gao +--- + configs/mt7622_rfb_defconfig | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/configs/mt7622_rfb_defconfig ++++ b/configs/mt7622_rfb_defconfig +@@ -4,6 +4,8 @@ CONFIG_ARCH_MEDIATEK=y + CONFIG_SYS_TEXT_BASE=0x41e00000 + CONFIG_SYS_MALLOC_F_LEN=0x4000 + CONFIG_NR_DRAM_BANKS=1 ++CONFIG_ENV_SIZE=0x20000 ++CONFIG_ENV_OFFSET=0x280000 + CONFIG_DEBUG_UART_BASE=0x11002000 + CONFIG_DEBUG_UART_CLOCK=25000000 + CONFIG_DEFAULT_DEVICE_TREE="mt7622-rfb" +@@ -21,6 +23,9 @@ CONFIG_CMD_SF_TEST=y + CONFIG_CMD_PING=y + CONFIG_CMD_SMC=y + CONFIG_ENV_OVERWRITE=y ++CONFIG_ENV_IS_IN_MTD=y ++CONFIG_ENV_MTD_NAME="spi-nand0" ++CONFIG_ENV_SIZE_REDUND=0x40000 + CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y + CONFIG_NET_RANDOM_ETHADDR=y + CONFIG_REGMAP=y diff --git a/package/boot/uboot-mediatek/patches/000-mtk-21-mmc-mtk-sd-don-t-ignore-max-frequency-from-device-tr.patch b/package/boot/uboot-mediatek/patches/000-mtk-21-mmc-mtk-sd-don-t-ignore-max-frequency-from-device-tr.patch new file mode 100644 index 0000000000..37810f9d74 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/000-mtk-21-mmc-mtk-sd-don-t-ignore-max-frequency-from-device-tr.patch @@ -0,0 +1,31 @@ +From 4bdab0ea008113dda4e001ab8d6863945000c1b2 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 11 Mar 2021 14:58:26 +0000 +Subject: [PATCH 21/21] mmc: mtk-sd: don't ignore max-frequency from device + tree + +commit e58e68d9 ("mmc: mtk-sd: assign plat->cfg.f_max with a correct value") +wrongly assumed that plat->cfg.f_max is always unset at the time +mscd_drv_probe() is run. This is not true in case max-frequency being +defined in device tree, as it is then already set by mmc_of_parser() +in msdc_of_to_plat(). +Only set plat->cfg.f_max to the default maximum value in case it is +not already set to a sane value. + +Signed-off-by: Daniel Golle +--- + drivers/mmc/mtk-sd.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/mmc/mtk-sd.c ++++ b/drivers/mmc/mtk-sd.c +@@ -1644,7 +1644,8 @@ static int msdc_drv_probe(struct udevice + if (cfg->f_min < MIN_BUS_CLK) + cfg->f_min = MIN_BUS_CLK; + +- cfg->f_max = host->src_clk_freq; ++ if (cfg->f_max < cfg->f_min || cfg->f_max > host->src_clk_freq) ++ cfg->f_max = host->src_clk_freq; + + cfg->b_max = 1024; + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch deleted file mode 100644 index 5d3e94ac86..0000000000 --- a/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch +++ /dev/null @@ -1,8540 +0,0 @@ -From de8b6cf615be20b25d0f3c817866de2c0d46a704 Mon Sep 17 00:00:00 2001 -From: Sam Shih -Date: Mon, 20 Apr 2020 17:10:05 +0800 -Subject: [PATCH 1/3] nand: add spi nand driver - -Add spi nand driver support for mt7622 based on nfi controller - -Signed-off-by: Xiangsheng Hou ---- - drivers/mtd/Kconfig | 7 + - drivers/mtd/Makefile | 4 + - drivers/mtd/nand/raw/nand.c | 2 + - drivers/mtd/nandx/NOTICE | 52 + - drivers/mtd/nandx/Nandx.config | 17 + - drivers/mtd/nandx/Nandx.mk | 91 ++ - drivers/mtd/nandx/README | 31 + - drivers/mtd/nandx/core/Nandx.mk | 38 + - drivers/mtd/nandx/core/core_io.c | 735 +++++++++ - drivers/mtd/nandx/core/core_io.h | 39 + - drivers/mtd/nandx/core/nand/device_spi.c | 200 +++ - drivers/mtd/nandx/core/nand/device_spi.h | 132 ++ - drivers/mtd/nandx/core/nand/nand_spi.c | 526 +++++++ - drivers/mtd/nandx/core/nand/nand_spi.h | 35 + - drivers/mtd/nandx/core/nand_base.c | 304 ++++ - drivers/mtd/nandx/core/nand_base.h | 71 + - drivers/mtd/nandx/core/nand_chip.c | 272 ++++ - drivers/mtd/nandx/core/nand_chip.h | 103 ++ - drivers/mtd/nandx/core/nand_device.c | 285 ++++ - drivers/mtd/nandx/core/nand_device.h | 608 ++++++++ - drivers/mtd/nandx/core/nfi.h | 51 + - drivers/mtd/nandx/core/nfi/nfi_base.c | 1357 +++++++++++++++++ - drivers/mtd/nandx/core/nfi/nfi_base.h | 95 ++ - drivers/mtd/nandx/core/nfi/nfi_regs.h | 114 ++ - drivers/mtd/nandx/core/nfi/nfi_spi.c | 689 +++++++++ - drivers/mtd/nandx/core/nfi/nfi_spi.h | 44 + - drivers/mtd/nandx/core/nfi/nfi_spi_regs.h | 64 + - drivers/mtd/nandx/core/nfi/nfiecc.c | 510 +++++++ - drivers/mtd/nandx/core/nfi/nfiecc.h | 90 ++ - drivers/mtd/nandx/core/nfi/nfiecc_regs.h | 51 + - drivers/mtd/nandx/driver/Nandx.mk | 18 + - drivers/mtd/nandx/driver/bbt/bbt.c | 408 +++++ - drivers/mtd/nandx/driver/uboot/driver.c | 574 +++++++ - drivers/mtd/nandx/include/Nandx.mk | 16 + - drivers/mtd/nandx/include/internal/bbt.h | 62 + - .../mtd/nandx/include/internal/nandx_core.h | 250 +++ - .../mtd/nandx/include/internal/nandx_errno.h | 40 + - .../mtd/nandx/include/internal/nandx_util.h | 221 +++ - drivers/mtd/nandx/include/uboot/nandx_os.h | 78 + - include/configs/mt7622.h | 25 + - 40 files changed, 8309 insertions(+) - create mode 100644 drivers/mtd/nandx/NOTICE - create mode 100644 drivers/mtd/nandx/Nandx.config - create mode 100644 drivers/mtd/nandx/Nandx.mk - create mode 100644 drivers/mtd/nandx/README - create mode 100644 drivers/mtd/nandx/core/Nandx.mk - create mode 100644 drivers/mtd/nandx/core/core_io.c - create mode 100644 drivers/mtd/nandx/core/core_io.h - create mode 100644 drivers/mtd/nandx/core/nand/device_spi.c - create mode 100644 drivers/mtd/nandx/core/nand/device_spi.h - create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.c - create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.h - create mode 100644 drivers/mtd/nandx/core/nand_base.c - create mode 100644 drivers/mtd/nandx/core/nand_base.h - create mode 100644 drivers/mtd/nandx/core/nand_chip.c - create mode 100644 drivers/mtd/nandx/core/nand_chip.h - create mode 100644 drivers/mtd/nandx/core/nand_device.c - create mode 100644 drivers/mtd/nandx/core/nand_device.h - create mode 100644 drivers/mtd/nandx/core/nfi.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.c - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_regs.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.c - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi_regs.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.c - create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc_regs.h - create mode 100644 drivers/mtd/nandx/driver/Nandx.mk - create mode 100644 drivers/mtd/nandx/driver/bbt/bbt.c - create mode 100644 drivers/mtd/nandx/driver/uboot/driver.c - create mode 100644 drivers/mtd/nandx/include/Nandx.mk - create mode 100644 drivers/mtd/nandx/include/internal/bbt.h - create mode 100644 drivers/mtd/nandx/include/internal/nandx_core.h - create mode 100644 drivers/mtd/nandx/include/internal/nandx_errno.h - create mode 100644 drivers/mtd/nandx/include/internal/nandx_util.h - create mode 100644 drivers/mtd/nandx/include/uboot/nandx_os.h - ---- a/drivers/mtd/Kconfig -+++ b/drivers/mtd/Kconfig -@@ -108,6 +108,13 @@ config HBMC_AM654 - This is the driver for HyperBus controller on TI's AM65x and - other SoCs - -+config MTK_SPI_NAND -+ tristate "Mediatek SPI Nand" -+ depends on DM_MTD -+ help -+ This option will support SPI Nand device via Mediatek -+ NFI controller. -+ - source "drivers/mtd/nand/Kconfig" - - source "drivers/mtd/spi/Kconfig" ---- a/drivers/mtd/Makefile -+++ b/drivers/mtd/Makefile -@@ -41,3 +41,7 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPOR - obj-$(CONFIG_SPL_UBI) += ubispl/ - - endif -+ -+ifeq ($(CONFIG_MTK_SPI_NAND), y) -+include $(srctree)/drivers/mtd/nandx/Nandx.mk -+endif ---- a/drivers/mtd/nand/raw/nand.c -+++ b/drivers/mtd/nand/raw/nand.c -@@ -91,8 +91,10 @@ static void nand_init_chip(int i) - if (board_nand_init(nand)) - return; - -+#ifndef CONFIG_MTK_SPI_NAND - if (nand_scan(mtd, maxchips)) - return; -+#endif - - nand_register(i, mtd); - } ---- /dev/null -+++ b/drivers/mtd/nandx/NOTICE -@@ -0,0 +1,52 @@ -+ -+/* -+ * Nandx - Mediatek Common Nand Driver -+ * Copyright (C) 2017 MediaTek Inc. -+ * -+ * Nandx is dual licensed: you can use it either under the terms of -+ * the GPL, or the BSD license, at your option. -+ * -+ * a) 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 library 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. -+ * -+ * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. -+ * -+ * Alternatively, -+ * -+ * b) Redistribution and use in source and binary forms, with or -+ * without modification, are permitted provided that the following -+ * conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above -+ * copyright notice, this list of conditions and the following -+ * disclaimer. -+ * 2. Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following -+ * disclaimer in the documentation and/or other materials -+ * provided with the distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#################################################################################################### -\ No newline at end of file ---- /dev/null -+++ b/drivers/mtd/nandx/Nandx.config -@@ -0,0 +1,17 @@ -+NANDX_SIMULATOR_SUPPORT := n -+NANDX_CTP_SUPPORT := n -+NANDX_DA_SUPPORT := n -+NANDX_PRELOADER_SUPPORT := n -+NANDX_LK_SUPPORT := n -+NANDX_KERNEL_SUPPORT := n -+NANDX_BROM_SUPPORT := n -+NANDX_UBOOT_SUPPORT := y -+NANDX_BBT_SUPPORT := y -+ -+NANDX_NAND_SPI := y -+NANDX_NAND_SLC := n -+NANDX_NAND_MLC := n -+NANDX_NAND_TLC := n -+NANDX_NFI_BASE := y -+NANDX_NFI_ECC := y -+NANDX_NFI_SPI := y ---- /dev/null -+++ b/drivers/mtd/nandx/Nandx.mk -@@ -0,0 +1,91 @@ -+# -+# Copyright (C) 2017 MediaTek Inc. -+# Licensed under either -+# BSD Licence, (see NOTICE for more details) -+# GNU General Public License, version 2.0, (see NOTICE for more details) -+# -+ -+nandx_dir := $(shell dirname $(lastword $(MAKEFILE_LIST))) -+include $(nandx_dir)/Nandx.config -+ -+ifeq ($(NANDX_SIMULATOR_SUPPORT), y) -+sim-obj := -+sim-inc := -+nandx-obj := sim-obj -+nandx-prefix := . -+nandx-postfix := %.o -+sim-inc += -I$(nandx-prefix)/include/internal -+sim-inc += -I$(nandx-prefix)/include/simulator -+endif -+ -+ifeq ($(NANDX_CTP_SUPPORT), y) -+nandx-obj := C_SRC_FILES -+nandx-prefix := $(nandx_dir) -+nandx-postfix := %.c -+INC_DIRS += $(nandx_dir)/include/internal -+INC_DIRS += $(nandx_dir)/include/ctp -+endif -+ -+ifeq ($(NANDX_DA_SUPPORT), y) -+nandx-obj := obj-y -+nandx-prefix := $(nandx_dir) -+nandx-postfix := %.o -+INCLUDE_PATH += $(TOPDIR)/platform/$(CODE_BASE)/dev/nand/nandx/include/internal -+INCLUDE_PATH += $(TOPDIR)/platform/$(CODE_BASE)/dev/nand/nandx/include/da -+endif -+ -+ifeq ($(NANDX_PRELOADER_SUPPORT), y) -+nandx-obj := MOD_SRC -+nandx-prefix := $(nandx_dir) -+nandx-postfix := %.c -+C_OPTION += -I$(MTK_PATH_PLATFORM)/src/drivers/nandx/include/internal -+C_OPTION += -I$(MTK_PATH_PLATFORM)/src/drivers/nandx/include/preloader -+endif -+ -+ifeq ($(NANDX_LK_SUPPORT), y) -+nandx-obj := MODULE_SRCS -+nandx-prefix := $(nandx_dir) -+nandx-postfix := %.c -+GLOBAL_INCLUDES += $(nandx_dir)/include/internal -+GLOBAL_INCLUDES += $(nandx_dir)/include/lk -+endif -+ -+ifeq ($(NANDX_KERNEL_SUPPORT), y) -+nandx-obj := obj-y -+nandx-prefix := nandx -+nandx-postfix := %.o -+ccflags-y += -I$(nandx_dir)/include/internal -+ccflags-y += -I$(nandx_dir)/include/kernel -+endif -+ -+ifeq ($(NANDX_UBOOT_SUPPORT), y) -+nandx-obj := obj-y -+nandx-prefix := nandx -+nandx-postfix := %.o -+ccflags-y += -I$(nandx_dir)/include/internal -+ccflags-y += -I$(nandx_dir)/include/uboot -+endif -+ -+nandx-y := -+include $(nandx_dir)/core/Nandx.mk -+nandx-target := $(nandx-prefix)/core/$(nandx-postfix) -+$(nandx-obj) += $(patsubst %.c, $(nandx-target), $(nandx-y)) -+ -+ -+nandx-y := -+include $(nandx_dir)/driver/Nandx.mk -+nandx-target := $(nandx-prefix)/driver/$(nandx-postfix) -+$(nandx-obj) += $(patsubst %.c, $(nandx-target), $(nandx-y)) -+ -+ifeq ($(NANDX_SIMULATOR_SUPPORT), y) -+cc := gcc -+CFLAGS += $(sim-inc) -+ -+.PHONY:nandx -+nandx: $(sim-obj) -+ $(cc) $(sim-obj) -o nandx -+ -+.PHONY:clean -+clean: -+ rm -rf $(sim-obj) nandx -+endif ---- /dev/null -+++ b/drivers/mtd/nandx/README -@@ -0,0 +1,31 @@ -+ -+ NAND2.0 -+ =============================== -+ -+ NAND2.0 is a common nand driver which designed for accessing -+different type of NANDs(SLC, SPI-NAND, MLC, TLC) on various OS. This -+driver can work on mostly SoCs of Mediatek. -+ -+ Although there already has a common nand driver, it doesn't cover -+SPI-NAND, and not match our IC-Verification's reqirement. We need -+a driver that can be exten or cut easily. -+ -+ This driver is base on NANDX & SLC. We try to refactor structures, -+and make them inheritable. We also refactor some operations' flow -+principally for adding SPI-NAND support. -+ -+ This driver's architecture is like: -+ -+ Driver @LK/Uboot/DA... |IC verify/other purposes -+ ---------------------------------------------------------------- -+ partition | BBM | -+ -------------------------------------- | extend_core -+ nandx_core/core_io | -+ ---------------------------------------------------------------- -+ nand_chip/nand_base | -+ -------------------------------------- | extend_nfi -+ nand_device | nfi/nfi_base | -+ -+ Any block of above graph can be extended at your will, if you -+want add new feature into this code, please make sure that your code -+would follow the framework, and we will be appreciated about it. ---- /dev/null -+++ b/drivers/mtd/nandx/core/Nandx.mk -@@ -0,0 +1,38 @@ -+# -+# Copyright (C) 2017 MediaTek Inc. -+# Licensed under either -+# BSD Licence, (see NOTICE for more details) -+# GNU General Public License, version 2.0, (see NOTICE for more details) -+# -+ -+nandx-y += nand_device.c -+nandx-y += nand_base.c -+nandx-y += nand_chip.c -+nandx-y += core_io.c -+ -+nandx-header-y += nand_device.h -+nandx-header-y += nand_base.h -+nandx-header-y += nand_chip.h -+nandx-header-y += core_io.h -+nandx-header-y += nfi.h -+ -+nandx-$(NANDX_NAND_SPI) += nand/device_spi.c -+nandx-$(NANDX_NAND_SPI) += nand/nand_spi.c -+nandx-$(NANDX_NAND_SLC) += nand/device_slc.c -+nandx-$(NANDX_NAND_SLC) += nand/nand_slc.c -+ -+nandx-header-$(NANDX_NAND_SPI) += nand/device_spi.h -+nandx-header-$(NANDX_NAND_SPI) += nand/nand_spi.h -+nandx-header-$(NANDX_NAND_SLC) += nand/device_slc.h -+nandx-header-$(NANDX_NAND_SLC) += nand/nand_slc.h -+ -+nandx-$(NANDX_NFI_BASE) += nfi/nfi_base.c -+nandx-$(NANDX_NFI_ECC) += nfi/nfiecc.c -+nandx-$(NANDX_NFI_SPI) += nfi/nfi_spi.c -+ -+nandx-header-$(NANDX_NFI_BASE) += nfi/nfi_base.h -+nandx-header-$(NANDX_NFI_BASE) += nfi/nfi_regs.h -+nandx-header-$(NANDX_NFI_ECC) += nfi/nfiecc.h -+nandx-header-$(NANDX_NFI_ECC) += nfi/nfiecc_regs.h -+nandx-header-$(NANDX_NFI_SPI) += nfi/nfi_spi.h -+nandx-header-$(NANDX_NFI_SPI) += nfi/nfi_spi_regs.h ---- /dev/null -+++ b/drivers/mtd/nandx/core/core_io.c -@@ -0,0 +1,735 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+/*NOTE: switch cache/multi*/ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nand_chip.h" -+#include "core_io.h" -+ -+static struct nandx_desc *g_nandx; -+ -+static inline bool is_sector_align(u64 val) -+{ -+ return reminder(val, g_nandx->chip->sector_size) ? false : true; -+} -+ -+static inline bool is_page_align(u64 val) -+{ -+ return reminder(val, g_nandx->chip->page_size) ? false : true; -+} -+ -+static inline bool is_block_align(u64 val) -+{ -+ return reminder(val, g_nandx->chip->block_size) ? false : true; -+} -+ -+static inline u32 page_sectors(void) -+{ -+ return div_down(g_nandx->chip->page_size, g_nandx->chip->sector_size); -+} -+ -+static inline u32 sector_oob(void) -+{ -+ return div_down(g_nandx->chip->oob_size, page_sectors()); -+} -+ -+static inline u32 sector_padded_size(void) -+{ -+ return g_nandx->chip->sector_size + g_nandx->chip->sector_spare_size; -+} -+ -+static inline u32 page_padded_size(void) -+{ -+ return page_sectors() * sector_padded_size(); -+} -+ -+static inline u32 offset_to_padded_col(u64 offset) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ u32 col, sectors; -+ -+ col = reminder(offset, nandx->chip->page_size); -+ sectors = div_down(col, nandx->chip->sector_size); -+ -+ return col + sectors * nandx->chip->sector_spare_size; -+} -+ -+static inline u32 offset_to_row(u64 offset) -+{ -+ return div_down(offset, g_nandx->chip->page_size); -+} -+ -+static inline u32 offset_to_col(u64 offset) -+{ -+ return reminder(offset, g_nandx->chip->page_size); -+} -+ -+static inline u32 oob_upper_size(void) -+{ -+ return g_nandx->ecc_en ? g_nandx->chip->oob_size : -+ g_nandx->chip->sector_spare_size * page_sectors(); -+} -+ -+static inline bool is_upper_oob_align(u64 val) -+{ -+ return reminder(val, oob_upper_size()) ? false : true; -+} -+ -+#define prepare_op(_op, _row, _col, _len, _data, _oob) \ -+ do { \ -+ (_op).row = (_row); \ -+ (_op).col = (_col); \ -+ (_op).len = (_len); \ -+ (_op).data = (_data); \ -+ (_op).oob = (_oob); \ -+ } while (0) -+ -+static int operation_multi(enum nandx_op_mode mode, u8 *data, u8 *oob, -+ u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ u32 row = offset_to_row(offset); -+ u32 col = offset_to_padded_col(offset); -+ -+ if (nandx->mode == NANDX_IDLE) { -+ nandx->mode = mode; -+ nandx->ops_current = 0; -+ } else if (nandx->mode != mode) { -+ pr_info("forbid mixed operations.\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ prepare_op(nandx->ops[nandx->ops_current], row, col, len, data, oob); -+ nandx->ops_current++; -+ -+ if (nandx->ops_current == nandx->ops_multi_len) -+ return nandx_sync(); -+ -+ return nandx->ops_multi_len - nandx->ops_current; -+} -+ -+static int operation_sequent(enum nandx_op_mode mode, u8 *data, u8 *oob, -+ u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ u32 row = offset_to_row(offset); -+ func_chip_ops chip_ops; -+ u8 *ref_data = data, *ref_oob = oob; -+ int align, ops, row_step; -+ int i, rem; -+ -+ align = data ? chip->page_size : oob_upper_size(); -+ ops = data ? div_down(len, align) : div_down(len, oob_upper_size()); -+ row_step = 1; -+ -+ switch (mode) { -+ case NANDX_ERASE: -+ chip_ops = chip->erase_block; -+ align = chip->block_size; -+ ops = div_down(len, align); -+ row_step = chip->block_pages; -+ break; -+ -+ case NANDX_READ: -+ chip_ops = chip->read_page; -+ break; -+ -+ case NANDX_WRITE: -+ chip_ops = chip->write_page; -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ if (!data) { -+ ref_data = nandx->head_buf; -+ memset(ref_data, 0xff, chip->page_size); -+ } -+ -+ if (!oob) { -+ ref_oob = nandx->head_buf + chip->page_size; -+ memset(ref_oob, 0xff, oob_upper_size()); -+ } -+ -+ for (i = 0; i < ops; i++) { -+ prepare_op(nandx->ops[nandx->ops_current], -+ row + i * row_step, 0, align, ref_data, ref_oob); -+ nandx->ops_current++; -+ /* if data or oob is null, nandx->head_buf or -+ * nandx->head_buf + chip->page_size should not been used -+ * so, here it is safe to use the buf. -+ */ -+ ref_data = data ? ref_data + chip->page_size : nandx->head_buf; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ -+ if (nandx->mode == NANDX_WRITE) { -+ rem = reminder(nandx->ops_current, nandx->min_write_pages); -+ if (rem) -+ return nandx->min_write_pages - rem; -+ } -+ -+ nandx->ops_current = 0; -+ return chip_ops(chip, nandx->ops, ops); -+} -+ -+static int read_pages(u8 *data, u8 *oob, u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ struct nandx_split64 split = {0}; -+ u8 *ref_data = data, *ref_oob; -+ u32 row, col; -+ int ret = 0, i, ops; -+ u32 head_offset = 0; -+ u64 val; -+ -+ if (!data) -+ return operation_sequent(NANDX_READ, NULL, oob, offset, len); -+ -+ ref_oob = oob ? oob : nandx->head_buf + chip->page_size; -+ -+ nandx_split(&split, offset, len, val, chip->page_size); -+ -+ if (split.head_len) { -+ row = offset_to_row(split.head); -+ col = offset_to_col(split.head); -+ prepare_op(nandx->ops[nandx->ops_current], row, 0, -+ chip->page_size, -+ nandx->head_buf, ref_oob); -+ nandx->ops_current++; -+ -+ head_offset = col; -+ -+ ref_data += split.head_len; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ -+ if (split.body_len) { -+ ops = div_down(split.body_len, chip->page_size); -+ row = offset_to_row(split.body); -+ for (i = 0; i < ops; i++) { -+ prepare_op(nandx->ops[nandx->ops_current], -+ row + i, 0, chip->page_size, -+ ref_data, ref_oob); -+ nandx->ops_current++; -+ ref_data += chip->page_size; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ } -+ -+ if (split.tail_len) { -+ row = offset_to_row(split.tail); -+ prepare_op(nandx->ops[nandx->ops_current], row, 0, -+ chip->page_size, nandx->tail_buf, ref_oob); -+ nandx->ops_current++; -+ } -+ -+ ret = chip->read_page(chip, nandx->ops, nandx->ops_current); -+ -+ if (split.head_len) -+ memcpy(data, nandx->head_buf + head_offset, split.head_len); -+ if (split.tail_len) -+ memcpy(ref_data, nandx->tail_buf, split.tail_len); -+ -+ nandx->ops_current = 0; -+ return ret; -+} -+ -+int nandx_read(u8 *data, u8 *oob, u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ -+ if (!len || len > nandx->info.total_size) -+ return -EINVAL; -+ if (div_up(len, nandx->chip->page_size) > nandx->ops_len) -+ return -EINVAL; -+ if (!data && !oob) -+ return -EINVAL; -+ /** -+ * as design, oob not support partial read -+ * and, the length of oob buf should be oob size aligned -+ */ -+ if (!data && !is_upper_oob_align(len)) -+ return -EINVAL; -+ -+ if (g_nandx->multi_en) { -+ /* as design, there only 2 buf for partial read, -+ * if partial read allowed for multi read, -+ * there are not enough buf -+ */ -+ if (!is_sector_align(offset)) -+ return -EINVAL; -+ if (data && !is_sector_align(len)) -+ return -EINVAL; -+ return operation_multi(NANDX_READ, data, oob, offset, len); -+ } -+ -+ nandx->ops_current = 0; -+ nandx->mode = NANDX_IDLE; -+ return read_pages(data, oob, offset, len); -+} -+ -+static int write_pages(u8 *data, u8 *oob, u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ struct nandx_split64 split = {0}; -+ int ret, rem, i, ops; -+ u32 row, col; -+ u8 *ref_oob = oob; -+ u64 val; -+ -+ nandx->mode = NANDX_WRITE; -+ -+ if (!data) -+ return operation_sequent(NANDX_WRITE, NULL, oob, offset, len); -+ -+ if (!oob) { -+ ref_oob = nandx->head_buf + chip->page_size; -+ memset(ref_oob, 0xff, oob_upper_size()); -+ } -+ -+ nandx_split(&split, offset, len, val, chip->page_size); -+ -+ /*NOTE: slc can support sector write, here copy too many data.*/ -+ if (split.head_len) { -+ row = offset_to_row(split.head); -+ col = offset_to_col(split.head); -+ memset(nandx->head_buf, 0xff, page_padded_size()); -+ memcpy(nandx->head_buf + col, data, split.head_len); -+ prepare_op(nandx->ops[nandx->ops_current], row, 0, -+ chip->page_size, nandx->head_buf, ref_oob); -+ nandx->ops_current++; -+ -+ data += split.head_len; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ -+ if (split.body_len) { -+ row = offset_to_row(split.body); -+ ops = div_down(split.body_len, chip->page_size); -+ for (i = 0; i < ops; i++) { -+ prepare_op(nandx->ops[nandx->ops_current], -+ row + i, 0, chip->page_size, data, ref_oob); -+ nandx->ops_current++; -+ data += chip->page_size; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ } -+ -+ if (split.tail_len) { -+ row = offset_to_row(split.tail); -+ memset(nandx->tail_buf, 0xff, page_padded_size()); -+ memcpy(nandx->tail_buf, data, split.tail_len); -+ prepare_op(nandx->ops[nandx->ops_current], row, 0, -+ chip->page_size, nandx->tail_buf, ref_oob); -+ nandx->ops_current++; -+ } -+ -+ rem = reminder(nandx->ops_current, nandx->min_write_pages); -+ if (rem) -+ return nandx->min_write_pages - rem; -+ -+ ret = chip->write_page(chip, nandx->ops, nandx->ops_current); -+ -+ nandx->ops_current = 0; -+ nandx->mode = NANDX_IDLE; -+ return ret; -+} -+ -+int nandx_write(u8 *data, u8 *oob, u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ -+ if (!len || len > nandx->info.total_size) -+ return -EINVAL; -+ if (div_up(len, nandx->chip->page_size) > nandx->ops_len) -+ return -EINVAL; -+ if (!data && !oob) -+ return -EINVAL; -+ if (!data && !is_upper_oob_align(len)) -+ return -EINVAL; -+ -+ if (nandx->multi_en) { -+ if (!is_page_align(offset)) -+ return -EINVAL; -+ if (data && !is_page_align(len)) -+ return -EINVAL; -+ -+ return operation_multi(NANDX_WRITE, data, oob, offset, len); -+ } -+ -+ return write_pages(data, oob, offset, len); -+} -+ -+int nandx_erase(u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ -+ if (!len || len > nandx->info.total_size) -+ return -EINVAL; -+ if (div_down(len, nandx->chip->block_size) > nandx->ops_len) -+ return -EINVAL; -+ if (!is_block_align(offset) || !is_block_align(len)) -+ return -EINVAL; -+ -+ if (g_nandx->multi_en) -+ return operation_multi(NANDX_ERASE, NULL, NULL, offset, len); -+ -+ nandx->ops_current = 0; -+ nandx->mode = NANDX_IDLE; -+ return operation_sequent(NANDX_ERASE, NULL, NULL, offset, len); -+} -+ -+int nandx_sync(void) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ func_chip_ops chip_ops; -+ int ret, i, rem; -+ -+ if (!nandx->ops_current) -+ return 0; -+ -+ rem = reminder(nandx->ops_current, nandx->ops_multi_len); -+ if (nandx->multi_en && rem) { -+ ret = -EIO; -+ goto error; -+ } -+ -+ switch (nandx->mode) { -+ case NANDX_IDLE: -+ return 0; -+ case NANDX_ERASE: -+ chip_ops = chip->erase_block; -+ break; -+ case NANDX_READ: -+ chip_ops = chip->read_page; -+ break; -+ case NANDX_WRITE: -+ chip_ops = chip->write_page; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ rem = reminder(nandx->ops_current, nandx->min_write_pages); -+ if (!nandx->multi_en && nandx->mode == NANDX_WRITE && rem) { -+ /* in one process of program, only allow 2 pages to do partial -+ * write, here we supposed 1st buf would be used, and 2nd -+ * buf should be not used. -+ */ -+ memset(nandx->tail_buf, 0xff, -+ chip->page_size + oob_upper_size()); -+ for (i = 0; i < rem; i++) { -+ prepare_op(nandx->ops[nandx->ops_current], -+ nandx->ops[nandx->ops_current - 1].row + 1, -+ 0, chip->page_size, nandx->tail_buf, -+ nandx->tail_buf + chip->page_size); -+ nandx->ops_current++; -+ } -+ } -+ -+ ret = chip_ops(nandx->chip, nandx->ops, nandx->ops_current); -+ -+error: -+ nandx->mode = NANDX_IDLE; -+ nandx->ops_current = 0; -+ -+ return ret; -+} -+ -+int nandx_ioctl(int cmd, void *arg) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ int ret = 0; -+ -+ switch (cmd) { -+ case CORE_CTRL_NAND_INFO: -+ *(struct nandx_info *)arg = nandx->info; -+ break; -+ -+ case CHIP_CTRL_OPS_MULTI: -+ ret = chip->chip_ctrl(chip, cmd, arg); -+ if (!ret) -+ nandx->multi_en = *(bool *)arg; -+ break; -+ -+ case NFI_CTRL_ECC: -+ ret = chip->chip_ctrl(chip, cmd, arg); -+ if (!ret) -+ nandx->ecc_en = *(bool *)arg; -+ break; -+ -+ default: -+ ret = chip->chip_ctrl(chip, cmd, arg); -+ break; -+ } -+ -+ return ret; -+} -+ -+bool nandx_is_bad_block(u64 offset) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ -+ prepare_op(nandx->ops[0], offset_to_row(offset), 0, -+ nandx->chip->page_size, nandx->head_buf, -+ nandx->head_buf + nandx->chip->page_size); -+ -+ return nandx->chip->is_bad_block(nandx->chip, nandx->ops, 1); -+} -+ -+int nandx_suspend(void) -+{ -+ return g_nandx->chip->suspend(g_nandx->chip); -+} -+ -+int nandx_resume(void) -+{ -+ return g_nandx->chip->resume(g_nandx->chip); -+} -+ -+int nandx_init(struct nfi_resource *res) -+{ -+ struct nand_chip *chip; -+ struct nandx_desc *nandx; -+ int ret = 0; -+ -+ if (!res) -+ return -EINVAL; -+ -+ chip = nand_chip_init(res); -+ if (!chip) { -+ pr_info("nand chip init fail.\n"); -+ return -EFAULT; -+ } -+ -+ nandx = (struct nandx_desc *)mem_alloc(1, sizeof(struct nandx_desc)); -+ if (!nandx) -+ return -ENOMEM; -+ -+ g_nandx = nandx; -+ -+ nandx->chip = chip; -+ nandx->min_write_pages = chip->min_program_pages; -+ nandx->ops_multi_len = nandx->min_write_pages * chip->plane_num; -+ nandx->ops_len = chip->block_pages * chip->plane_num; -+ nandx->ops = mem_alloc(1, sizeof(struct nand_ops) * nandx->ops_len); -+ if (!nandx->ops) { -+ ret = -ENOMEM; -+ goto ops_error; -+ } -+ -+#if NANDX_BULK_IO_USE_DRAM -+ nandx->head_buf = NANDX_CORE_BUF_ADDR; -+#else -+ nandx->head_buf = mem_alloc(2, page_padded_size()); -+#endif -+ if (!nandx->head_buf) { -+ ret = -ENOMEM; -+ goto buf_error; -+ } -+ nandx->tail_buf = nandx->head_buf + page_padded_size(); -+ memset(nandx->head_buf, 0xff, 2 * page_padded_size()); -+ nandx->multi_en = false; -+ nandx->ecc_en = false; -+ nandx->ops_current = 0; -+ nandx->mode = NANDX_IDLE; -+ -+ nandx->info.max_io_count = nandx->ops_len; -+ nandx->info.min_write_pages = nandx->min_write_pages; -+ nandx->info.plane_num = chip->plane_num; -+ nandx->info.oob_size = chip->oob_size; -+ nandx->info.page_parity_size = chip->sector_spare_size * page_sectors(); -+ nandx->info.page_size = chip->page_size; -+ nandx->info.block_size = chip->block_size; -+ nandx->info.total_size = chip->block_size * chip->block_num; -+ nandx->info.fdm_ecc_size = chip->fdm_ecc_size; -+ nandx->info.fdm_reg_size = chip->fdm_reg_size; -+ nandx->info.ecc_strength = chip->ecc_strength; -+ nandx->info.sector_size = chip->sector_size; -+ -+ return 0; -+ -+buf_error: -+#if !NANDX_BULK_IO_USE_DRAM -+ mem_free(nandx->head_buf); -+#endif -+ops_error: -+ mem_free(nandx); -+ -+ return ret; -+} -+ -+void nandx_exit(void) -+{ -+ nand_chip_exit(g_nandx->chip); -+#if !NANDX_BULK_IO_USE_DRAM -+ mem_free(g_nandx->head_buf); -+#endif -+ mem_free(g_nandx->ops); -+ mem_free(g_nandx); -+} -+ -+#ifdef NANDX_UNIT_TEST -+static void dump_buf(u8 *buf, u32 len) -+{ -+ u32 i; -+ -+ pr_info("dump buf@0x%X start", (u32)buf); -+ for (i = 0; i < len; i++) { -+ if (!reminder(i, 16)) -+ pr_info("\n0x"); -+ pr_info("%x ", buf[i]); -+ } -+ pr_info("\ndump buf done.\n"); -+} -+ -+int nandx_unit_test(u64 offset, size_t len) -+{ -+ u8 *src_buf, *dst_buf; -+ u32 i, j; -+ int ret; -+ -+ if (!len || len > g_nandx->chip->block_size) -+ return -EINVAL; -+ -+#if NANDX_BULK_IO_USE_DRAM -+ src_buf = NANDX_UT_SRC_ADDR; -+ dst_buf = NANDX_UT_DST_ADDR; -+ -+#else -+ src_buf = mem_alloc(1, g_nandx->chip->page_size); -+ if (!src_buf) -+ return -ENOMEM; -+ dst_buf = mem_alloc(1, g_nandx->chip->page_size); -+ if (!dst_buf) { -+ mem_free(src_buf); -+ return -ENOMEM; -+ } -+#endif -+ -+ pr_info("%s: src_buf address 0x%x, dst_buf address 0x%x\n", -+ __func__, (int)((unsigned long)src_buf), -+ (int)((unsigned long)dst_buf)); -+ -+ memset(dst_buf, 0, g_nandx->chip->page_size); -+ pr_info("read page 0 data...!\n"); -+ ret = nandx_read(dst_buf, NULL, 0, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("read fail with ret %d\n", ret); -+ } else { -+ pr_info("read page success!\n"); -+ } -+ -+ for (i = 0; i < g_nandx->chip->page_size; i++) { -+ src_buf[i] = 0x5a; -+ } -+ -+ ret = nandx_erase(offset, g_nandx->chip->block_size); -+ if (ret < 0) { -+ pr_info("erase fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ for (j = 0; j < g_nandx->chip->block_pages; j++) { -+ memset(dst_buf, 0, g_nandx->chip->page_size); -+ pr_info("check data after erase...!\n"); -+ ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("read fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ for (i = 0; i < g_nandx->chip->page_size; i++) { -+ if (dst_buf[i] != 0xff) { -+ pr_info("read after erase, check fail @%d\n", i); -+ pr_info("all data should be 0xff\n"); -+ ret = -ENANDERASE; -+ dump_buf(dst_buf, 128); -+ //goto error; -+ break; -+ } -+ } -+ -+ pr_info("write data...!\n"); -+ ret = nandx_write(src_buf, NULL, offset, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("write fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ memset(dst_buf, 0, g_nandx->chip->page_size); -+ pr_info("read data...!\n"); -+ ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("read fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ for (i = 0; i < g_nandx->chip->page_size; i++) { -+ if (dst_buf[i] != src_buf[i]) { -+ pr_info("read after write, check fail @%d\n", i); -+ pr_info("dst_buf should be same as src_buf\n"); -+ ret = -EIO; -+ dump_buf(src_buf + i, 128); -+ dump_buf(dst_buf + i, 128); -+ break; -+ } -+ } -+ -+ pr_err("%s %d %s@%d\n", __func__, __LINE__, ret?"Failed":"OK", j); -+ if (ret) -+ break; -+ -+ offset += g_nandx->chip->page_size; -+ } -+ -+ ret = nandx_erase(offset, g_nandx->chip->block_size); -+ if (ret < 0) { -+ pr_info("erase fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ memset(dst_buf, 0, g_nandx->chip->page_size); -+ ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("read fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ for (i = 0; i < g_nandx->chip->page_size; i++) { -+ if (dst_buf[i] != 0xff) { -+ pr_info("read after erase, check fail\n"); -+ pr_info("all data should be 0xff\n"); -+ ret = -ENANDERASE; -+ dump_buf(dst_buf, 128); -+ goto error; -+ } -+ } -+ -+ return 0; -+ -+error: -+#if !NANDX_BULK_IO_USE_DRAM -+ mem_free(src_buf); -+ mem_free(dst_buf); -+#endif -+ return ret; -+} -+#endif ---- /dev/null -+++ b/drivers/mtd/nandx/core/core_io.h -@@ -0,0 +1,39 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __CORE_IO_H__ -+#define __CORE_IO_H__ -+ -+typedef int (*func_chip_ops)(struct nand_chip *, struct nand_ops *, -+ int); -+ -+enum nandx_op_mode { -+ NANDX_IDLE, -+ NANDX_WRITE, -+ NANDX_READ, -+ NANDX_ERASE -+}; -+ -+struct nandx_desc { -+ struct nand_chip *chip; -+ struct nandx_info info; -+ enum nandx_op_mode mode; -+ -+ bool multi_en; -+ bool ecc_en; -+ -+ struct nand_ops *ops; -+ int ops_len; -+ int ops_multi_len; -+ int ops_current; -+ int min_write_pages; -+ -+ u8 *head_buf; -+ u8 *tail_buf; -+}; -+ -+#endif /* __CORE_IO_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand/device_spi.c -@@ -0,0 +1,200 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "../nand_device.h" -+#include "device_spi.h" -+ -+/* spi nand basic commands */ -+static struct nand_cmds spi_cmds = { -+ .reset = 0xff, -+ .read_id = 0x9f, -+ .read_status = 0x0f, -+ .read_param_page = 0x03, -+ .set_feature = 0x1f, -+ .get_feature = 0x0f, -+ .read_1st = 0x13, -+ .read_2nd = -1, -+ .random_out_1st = 0x03, -+ .random_out_2nd = -1, -+ .program_1st = 0x02, -+ .program_2nd = 0x10, -+ .erase_1st = 0xd8, -+ .erase_2nd = -1, -+ .read_cache = 0x30, -+ .read_cache_last = 0x3f, -+ .program_cache = 0x02 -+}; -+ -+/* spi nand extend commands */ -+static struct spi_extend_cmds spi_extend_cmds = { -+ .die_select = 0xc2, -+ .write_enable = 0x06 -+}; -+ -+/* means the start bit of addressing type */ -+static struct nand_addressing spi_addressing = { -+ .row_bit_start = 0, -+ .block_bit_start = 0, -+ .plane_bit_start = 12, -+ .lun_bit_start = 0, -+}; -+ -+/* spi nand endurance */ -+static struct nand_endurance spi_endurance = { -+ .pe_cycle = 100000, -+ .ecc_req = 1, -+ .max_bitflips = 1 -+}; -+ -+/* array_busy, write_protect, erase_fail, program_fail */ -+static struct nand_status spi_status[] = { -+ {.array_busy = BIT(0), -+ .write_protect = BIT(1), -+ .erase_fail = BIT(2), -+ .program_fail = BIT(3)} -+}; -+ -+/* measure time by the us */ -+static struct nand_array_timing spi_array_timing = { -+ .tRST = 500, -+ .tWHR = 1, -+ .tR = 25, -+ .tRCBSY = 25, -+ .tFEAT = 1, -+ .tPROG = 600, -+ .tPCBSY = 600, -+ .tBERS = 10000, -+ .tDBSY = 1 -+}; -+ -+/* spi nand device table */ -+static struct device_spi spi_nand[] = { -+ { -+ NAND_DEVICE("W25N01GV", -+ NAND_PACK_ID(0xef, 0xaa, 0x21, 0, 0, 0, 0, 0), -+ 3, 0, 3, 3, -+ 1, 1, 1, 1024, KB(128), KB(2), 64, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 0), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("MX35LF1G", -+ NAND_PACK_ID(0xc2, 0x12, 0x21, 0, 0, 0, 0, 0), -+ 2, 0, 3, 3, -+ 1, 1, 1, 1024, KB(128), KB(2), 64, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("MT29F4G01ABAFDWB", -+ NAND_PACK_ID(0x2c, 0x34, 0, 0, 0, 0, 0, 0), -+ 2, 0, 3, 3, -+ 1, 1, 1, 2048, KB(256), KB(4), 256, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("GD5F4GQ4UB", -+ NAND_PACK_ID(0xc8, 0xd4, 0, 0, 0, 0, 0, 0), -+ 2, 0, 3, 3, -+ 1, 1, 1, 2048, KB(256), KB(4), 256, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("TC58CVG2S0HRAIJ", -+ NAND_PACK_ID(0x98, 0xED, 0x51, 0, 0, 0, 0, 0), -+ 3, 0, 3, 3, -+ 1, 1, 1, 2048, KB(256), KB(4), 256, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("NO-DEVICE", -+ NAND_PACK_ID(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 0), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ } -+}; -+ -+u8 spi_replace_rx_cmds(u8 mode) -+{ -+ u8 rx_replace_cmds[] = {0x03, 0x3b, 0x6b, 0xbb, 0xeb}; -+ -+ return rx_replace_cmds[mode]; -+} -+ -+u8 spi_replace_tx_cmds(u8 mode) -+{ -+ u8 tx_replace_cmds[] = {0x02, 0x32}; -+ -+ return tx_replace_cmds[mode]; -+} -+ -+u8 spi_replace_rx_col_cycle(u8 mode) -+{ -+ u8 rx_replace_col_cycle[] = {3, 3, 3, 3, 4}; -+ -+ return rx_replace_col_cycle[mode]; -+} -+ -+u8 spi_replace_tx_col_cycle(u8 mode) -+{ -+ u8 tx_replace_col_cycle[] = {2, 2}; -+ -+ return tx_replace_col_cycle[mode]; -+} -+ -+struct nand_device *nand_get_device(int index) -+{ -+ return &spi_nand[index].dev; -+} -+ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand/device_spi.h -@@ -0,0 +1,132 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __DEVICE_SPI_H__ -+#define __DEVICE_SPI_H__ -+ -+/* -+ * extend commands -+ * @die_select: select nand device die command -+ * @write_enable: enable write command before write data to spi nand -+ * spi nand device will auto to be disable after write done -+ */ -+struct spi_extend_cmds { -+ short die_select; -+ short write_enable; -+}; -+ -+/* -+ * protection feature register -+ * @addr: register address -+ * @wp_en_bit: write protection enable bit -+ * @bp_start_bit: block protection mask start bit -+ * @bp_end_bit: block protection mask end bit -+ */ -+struct feature_protect { -+ u8 addr; -+ u8 wp_en_bit; -+ u8 bp_start_bit; -+ u8 bp_end_bit; -+}; -+ -+/* -+ * configuration feature register -+ * @addr: register address -+ * @ecc_en_bit: in-die ecc enable bit -+ * @otp_en_bit: enter otp access mode bit -+ * @need_qe: quad io enable bit -+ */ -+struct feature_config { -+ u8 addr; -+ u8 ecc_en_bit; -+ u8 otp_en_bit; -+ u8 need_qe; -+}; -+ -+/* -+ * status feature register -+ * @addr: register address -+ * @ecc_start_bit: ecc status mask start bit for error bits number -+ * @ecc_end_bit: ecc status mask end bit for error bits number -+ * note that: -+ * operations status (ex. array busy status) could see on struct nand_status -+ */ -+struct feature_status { -+ u8 addr; -+ u8 ecc_start_bit; -+ u8 ecc_end_bit; -+}; -+ -+/* -+ * character feature register -+ * @addr: register address -+ * @die_sel_bit: die select bit -+ * @drive_start_bit: drive strength mask start bit -+ * @drive_end_bit: drive strength mask end bit -+ */ -+struct feature_character { -+ u8 addr; -+ u8 die_sel_bit; -+ u8 drive_start_bit; -+ u8 drive_end_bit; -+}; -+ -+/* -+ * spi features -+ * @protect: protection feature register -+ * @config: configuration feature register -+ * @status: status feature register -+ * @character: character feature register -+ */ -+struct spi_features { -+ struct feature_protect protect; -+ struct feature_config config; -+ struct feature_status status; -+ struct feature_character character; -+}; -+ -+/* -+ * device_spi -+ * configurations of spi nand device table -+ * @dev: base information of nand device -+ * @feature: feature information for spi nand -+ * @extend_cmds: extended the nand base commands -+ * @tx_mode_mask: tx mode mask for chip read -+ * @rx_mode_mask: rx mode mask for chip write -+ */ -+struct device_spi { -+ struct nand_device dev; -+ struct spi_features feature; -+ struct spi_extend_cmds *extend_cmds; -+ -+ u8 tx_mode_mask; -+ u8 rx_mode_mask; -+}; -+ -+#define NAND_SPI_PROTECT(addr, wp_en_bit, bp_start_bit, bp_end_bit) \ -+ {addr, wp_en_bit, bp_start_bit, bp_end_bit} -+ -+#define NAND_SPI_CONFIG(addr, ecc_en_bit, otp_en_bit, need_qe) \ -+ {addr, ecc_en_bit, otp_en_bit, need_qe} -+ -+#define NAND_SPI_STATUS(addr, ecc_start_bit, ecc_end_bit) \ -+ {addr, ecc_start_bit, ecc_end_bit} -+ -+#define NAND_SPI_CHARACTER(addr, die_sel_bit, drive_start_bit, drive_end_bit) \ -+ {addr, die_sel_bit, drive_start_bit, drive_end_bit} -+ -+static inline struct device_spi *device_to_spi(struct nand_device *dev) -+{ -+ return container_of(dev, struct device_spi, dev); -+} -+ -+u8 spi_replace_rx_cmds(u8 mode); -+u8 spi_replace_tx_cmds(u8 mode); -+u8 spi_replace_rx_col_cycle(u8 mode); -+u8 spi_replace_tx_col_cycle(u8 mode); -+ -+#endif /* __DEVICE_SPI_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand/nand_spi.c -@@ -0,0 +1,526 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "../nand_chip.h" -+#include "../nand_device.h" -+#include "../nfi.h" -+#include "../nand_base.h" -+#include "device_spi.h" -+#include "nand_spi.h" -+ -+#define READY_TIMEOUT 500000 /* us */ -+ -+static int nand_spi_read_status(struct nand_base *nand) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ u8 status; -+ -+ nand->get_feature(nand, dev->feature.status.addr, &status, 1); -+ -+ return status; -+} -+ -+static int nand_spi_wait_ready(struct nand_base *nand, u32 timeout) -+{ -+ u64 now, end; -+ int status; -+ -+ end = get_current_time_us() + timeout; -+ -+ do { -+ status = nand_spi_read_status(nand); -+ status &= nand->dev->status->array_busy; -+ now = get_current_time_us(); -+ -+ if (now > end) -+ break; -+ } while (status); -+ -+ return status ? -EBUSY : 0; -+} -+ -+static int nand_spi_set_op_mode(struct nand_base *nand, u8 mode) -+{ -+ struct nand_spi *spi_nand = base_to_spi(nand); -+ struct nfi *nfi = nand->nfi; -+ int ret = 0; -+ -+ if (spi_nand->op_mode != mode) { -+ ret = nfi->nfi_ctrl(nfi, SNFI_CTRL_OP_MODE, (void *)&mode); -+ spi_nand->op_mode = mode; -+ } -+ -+ return ret; -+} -+ -+static int nand_spi_set_config(struct nand_base *nand, u8 addr, u8 mask, -+ bool en) -+{ -+ u8 configs = 0; -+ -+ nand->get_feature(nand, addr, &configs, 1); -+ -+ if (en) -+ configs |= mask; -+ else -+ configs &= ~mask; -+ -+ nand->set_feature(nand, addr, &configs, 1); -+ -+ configs = 0; -+ nand->get_feature(nand, addr, &configs, 1); -+ -+ return (configs & mask) == en ? 0 : -EFAULT; -+} -+ -+static int nand_spi_die_select(struct nand_base *nand, int *row) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nfi *nfi = nand->nfi; -+ int lun_blocks, block_pages, lun, blocks; -+ int page = *row, ret = 0; -+ u8 param = 0, die_sel; -+ -+ if (nand->dev->lun_num < 2) -+ return 0; -+ -+ block_pages = nand_block_pages(nand->dev); -+ lun_blocks = nand_lun_blocks(nand->dev); -+ blocks = div_down(page, block_pages); -+ lun = div_down(blocks, lun_blocks); -+ -+ if (dev->extend_cmds->die_select == -1) { -+ die_sel = (u8)(lun << dev->feature.character.die_sel_bit); -+ nand->get_feature(nand, dev->feature.character.addr, ¶m, 1); -+ param |= die_sel; -+ nand->set_feature(nand, dev->feature.character.addr, ¶m, 1); -+ param = 0; -+ nand->get_feature(nand, dev->feature.character.addr, ¶m, 1); -+ ret = (param & die_sel) ? 0 : -EFAULT; -+ } else { -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->extend_cmds->die_select); -+ nfi->send_addr(nfi, lun, 0, 1, 0); -+ nfi->trigger(nfi); -+ } -+ -+ *row = page - (lun_blocks * block_pages) * lun; -+ -+ return ret; -+} -+ -+static int nand_spi_select_device(struct nand_base *nand, int cs) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ return parent->select_device(nand, cs); -+} -+ -+static int nand_spi_reset(struct nand_base *nand) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ parent->reset(nand); -+ -+ return nand_spi_wait_ready(nand, READY_TIMEOUT); -+} -+ -+static int nand_spi_read_id(struct nand_base *nand, u8 *id, int count) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ return parent->read_id(nand, id, count); -+} -+ -+static int nand_spi_read_param_page(struct nand_base *nand, u8 *data, -+ int count) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nfi *nfi = nand->nfi; -+ int sectors, value; -+ u8 param = 0; -+ -+ sectors = div_round_up(count, nfi->sector_size); -+ -+ nand->get_feature(nand, dev->feature.config.addr, ¶m, 1); -+ param |= BIT(dev->feature.config.otp_en_bit); -+ nand->set_feature(nand, dev->feature.config.addr, ¶m, 1); -+ -+ param = 0; -+ nand->get_feature(nand, dev->feature.config.addr, ¶m, 1); -+ if (param & BIT(dev->feature.config.otp_en_bit)) { -+ value = 0; -+ nfi->nfi_ctrl(nfi, NFI_CTRL_ECC, &value); -+ nand->dev->col_cycle = spi_replace_rx_col_cycle(spi->rx_mode); -+ nand->read_page(nand, 0x01); -+ nand->read_data(nand, 0x01, 0, sectors, data, NULL); -+ } -+ -+ param &= ~BIT(dev->feature.config.otp_en_bit); -+ nand->set_feature(nand, dev->feature.config.addr, ¶m, 1); -+ -+ return 0; -+} -+ -+static int nand_spi_set_feature(struct nand_base *nand, u8 addr, -+ u8 *param, -+ int count) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand->write_enable(nand); -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ return parent->set_feature(nand, addr, param, count); -+} -+ -+static int nand_spi_get_feature(struct nand_base *nand, u8 addr, -+ u8 *param, -+ int count) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ return parent->get_feature(nand, addr, param, count); -+} -+ -+static int nand_spi_addressing(struct nand_base *nand, int *row, -+ int *col) -+{ -+ struct nand_device *dev = nand->dev; -+ int plane, block, block_pages; -+ int ret; -+ -+ ret = nand_spi_die_select(nand, row); -+ if (ret) -+ return ret; -+ -+ block_pages = nand_block_pages(dev); -+ block = div_down(*row, block_pages); -+ -+ plane = block % dev->plane_num; -+ *col |= (plane << dev->addressing->plane_bit_start); -+ -+ return 0; -+} -+ -+static int nand_spi_read_page(struct nand_base *nand, int row) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ if (spi->op_mode == SNFI_AUTO_MODE) -+ nand_spi_set_op_mode(nand, SNFI_AUTO_MODE); -+ else -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ parent->read_page(nand, row); -+ -+ return nand_spi_wait_ready(nand, READY_TIMEOUT); -+} -+ -+static int nand_spi_read_data(struct nand_base *nand, int row, int col, -+ int sectors, u8 *data, u8 *oob) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ int ret; -+ -+ if ((spi->rx_mode == SNFI_RX_114 || spi->rx_mode == SNFI_RX_144) && -+ dev->feature.config.need_qe) -+ nand_spi_set_config(nand, dev->feature.config.addr, -+ BIT(0), true); -+ -+ nand->dev->col_cycle = spi_replace_rx_col_cycle(spi->rx_mode); -+ -+ nand_spi_set_op_mode(nand, SNFI_CUSTOM_MODE); -+ -+ ret = parent->read_data(nand, row, col, sectors, data, oob); -+ if (ret) -+ return -ENANDREAD; -+ -+ if (spi->ondie_ecc) { -+ ret = nand_spi_read_status(nand); -+ ret &= GENMASK(dev->feature.status.ecc_end_bit, -+ dev->feature.status.ecc_start_bit); -+ ret >>= dev->feature.status.ecc_start_bit; -+ if (ret > nand->dev->endurance->ecc_req) -+ return -ENANDREAD; -+ else if (ret > nand->dev->endurance->max_bitflips) -+ return -ENANDFLIPS; -+ } -+ -+ return 0; -+} -+ -+static int nand_spi_write_enable(struct nand_base *nand) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nfi *nfi = nand->nfi; -+ int status; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->extend_cmds->write_enable); -+ -+ nfi->trigger(nfi); -+ -+ status = nand_spi_read_status(nand); -+ status &= nand->dev->status->write_protect; -+ -+ return !status; -+} -+ -+static int nand_spi_program_data(struct nand_base *nand, int row, -+ int col, -+ u8 *data, u8 *oob) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nand_spi *spi = base_to_spi(nand); -+ -+ if (spi->tx_mode == SNFI_TX_114 && dev->feature.config.need_qe) -+ nand_spi_set_config(nand, dev->feature.config.addr, -+ BIT(0), true); -+ -+ nand_spi_set_op_mode(nand, SNFI_CUSTOM_MODE); -+ -+ nand->dev->col_cycle = spi_replace_tx_col_cycle(spi->tx_mode); -+ -+ return spi->parent->program_data(nand, row, col, data, oob); -+} -+ -+static int nand_spi_program_page(struct nand_base *nand, int row) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_device *dev = nand->dev; -+ struct nfi *nfi = nand->nfi; -+ -+ if (spi->op_mode == SNFI_AUTO_MODE) -+ nand_spi_set_op_mode(nand, SNFI_AUTO_MODE); -+ else -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->program_2nd); -+ nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle); -+ nfi->trigger(nfi); -+ -+ return nand_spi_wait_ready(nand, READY_TIMEOUT); -+} -+ -+static int nand_spi_erase_block(struct nand_base *nand, int row) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ parent->erase_block(nand, row); -+ -+ return nand_spi_wait_ready(nand, READY_TIMEOUT); -+} -+ -+static int nand_chip_spi_ctrl(struct nand_chip *chip, int cmd, -+ void *args) -+{ -+ struct nand_base *nand = chip->nand; -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nfi *nfi = nand->nfi; -+ int ret = 0, value = *(int *)args; -+ -+ switch (cmd) { -+ case CHIP_CTRL_ONDIE_ECC: -+ spi->ondie_ecc = (bool)value; -+ ret = nand_spi_set_config(nand, dev->feature.config.addr, -+ BIT(dev->feature.config.ecc_en_bit), -+ spi->ondie_ecc); -+ break; -+ -+ case SNFI_CTRL_TX_MODE: -+ if (value < 0 || value > SNFI_TX_114) -+ return -EOPNOTSUPP; -+ -+ if (dev->tx_mode_mask & BIT(value)) { -+ spi->tx_mode = value; -+ nand->dev->cmds->random_out_1st = spi_replace_tx_cmds( -+ spi->tx_mode); -+ ret = nfi->nfi_ctrl(nfi, cmd, args); -+ } -+ -+ break; -+ -+ case SNFI_CTRL_RX_MODE: -+ if (value < 0 || value > SNFI_RX_144) -+ return -EOPNOTSUPP; -+ -+ if (dev->rx_mode_mask & BIT(value)) { -+ spi->rx_mode = value; -+ nand->dev->cmds->program_1st = spi_replace_rx_cmds( -+ spi->rx_mode); -+ ret = nfi->nfi_ctrl(nfi, cmd, args); -+ } -+ -+ break; -+ -+ case CHIP_CTRL_OPS_CACHE: -+ case CHIP_CTRL_OPS_MULTI: -+ case CHIP_CTRL_PSLC_MODE: -+ case CHIP_CTRL_DDR_MODE: -+ case CHIP_CTRL_DRIVE_STRENGTH: -+ case CHIP_CTRL_TIMING_MODE: -+ ret = -EOPNOTSUPP; -+ break; -+ -+ default: -+ ret = nfi->nfi_ctrl(nfi, cmd, args); -+ break; -+ } -+ -+ return ret; -+} -+ -+int nand_chip_spi_resume(struct nand_chip *chip) -+{ -+ struct nand_base *nand = chip->nand; -+ struct nand_spi *spi = base_to_spi(nand); -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nfi *nfi = nand->nfi; -+ struct nfi_format format; -+ u8 mask; -+ -+ nand->reset(nand); -+ -+ mask = GENMASK(dev->feature.protect.bp_end_bit, -+ dev->feature.protect.bp_start_bit); -+ nand_spi_set_config(nand, dev->feature.config.addr, mask, false); -+ mask = BIT(dev->feature.config.ecc_en_bit); -+ nand_spi_set_config(nand, dev->feature.config.addr, mask, -+ spi->ondie_ecc); -+ -+ format.page_size = nand->dev->page_size; -+ format.spare_size = nand->dev->spare_size; -+ format.ecc_req = nand->dev->endurance->ecc_req; -+ -+ return nfi->set_format(nfi, &format); -+} -+ -+static int nand_spi_set_format(struct nand_base *nand) -+{ -+ struct nfi_format format = { -+ nand->dev->page_size, -+ nand->dev->spare_size, -+ nand->dev->endurance->ecc_req -+ }; -+ -+ return nand->nfi->set_format(nand->nfi, &format); -+} -+ -+struct nand_base *nand_device_init(struct nand_chip *chip) -+{ -+ struct nand_base *nand; -+ struct nand_spi *spi; -+ struct device_spi *dev; -+ int ret; -+ u8 mask; -+ -+ spi = mem_alloc(1, sizeof(struct nand_spi)); -+ if (!spi) { -+ pr_info("alloc nand_spi fail\n"); -+ return NULL; -+ } -+ -+ spi->ondie_ecc = false; -+ spi->op_mode = SNFI_CUSTOM_MODE; -+ spi->rx_mode = SNFI_RX_114; -+ spi->tx_mode = SNFI_TX_114; -+ -+ spi->parent = chip->nand; -+ nand = &spi->base; -+ nand->dev = spi->parent->dev; -+ nand->nfi = spi->parent->nfi; -+ -+ nand->select_device = nand_spi_select_device; -+ nand->reset = nand_spi_reset; -+ nand->read_id = nand_spi_read_id; -+ nand->read_param_page = nand_spi_read_param_page; -+ nand->set_feature = nand_spi_set_feature; -+ nand->get_feature = nand_spi_get_feature; -+ nand->read_status = nand_spi_read_status; -+ nand->addressing = nand_spi_addressing; -+ nand->read_page = nand_spi_read_page; -+ nand->read_data = nand_spi_read_data; -+ nand->write_enable = nand_spi_write_enable; -+ nand->program_data = nand_spi_program_data; -+ nand->program_page = nand_spi_program_page; -+ nand->erase_block = nand_spi_erase_block; -+ -+ chip->chip_ctrl = nand_chip_spi_ctrl; -+ chip->nand_type = NAND_SPI; -+ chip->resume = nand_chip_spi_resume; -+ -+ ret = nand_detect_device(nand); -+ if (ret) -+ goto err; -+ -+ nand->select_device(nand, 0); -+ -+ ret = nand_spi_set_format(nand); -+ if (ret) -+ goto err; -+ -+ dev = (struct device_spi *)nand->dev; -+ -+ nand->dev->cmds->random_out_1st = -+ spi_replace_rx_cmds(spi->rx_mode); -+ nand->dev->cmds->program_1st = -+ spi_replace_tx_cmds(spi->tx_mode); -+ -+ mask = GENMASK(dev->feature.protect.bp_end_bit, -+ dev->feature.protect.bp_start_bit); -+ ret = nand_spi_set_config(nand, dev->feature.protect.addr, mask, false); -+ if (ret) -+ goto err; -+ -+ mask = BIT(dev->feature.config.ecc_en_bit); -+ ret = nand_spi_set_config(nand, dev->feature.config.addr, mask, -+ spi->ondie_ecc); -+ if (ret) -+ goto err; -+ -+ return nand; -+ -+err: -+ mem_free(spi); -+ return NULL; -+} -+ -+void nand_exit(struct nand_base *nand) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ -+ nand_base_exit(spi->parent); -+ mem_free(spi); -+} ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand/nand_spi.h -@@ -0,0 +1,35 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NAND_SPI_H__ -+#define __NAND_SPI_H__ -+ -+/* -+ * spi nand handler -+ * @base: spi nand base functions -+ * @parent: common parent nand base functions -+ * @tx_mode: spi bus width of transfer to device -+ * @rx_mode: spi bus width of transfer from device -+ * @op_mode: spi nand controller (NFI) operation mode -+ * @ondie_ecc: spi nand on-die ecc flag -+ */ -+ -+struct nand_spi { -+ struct nand_base base; -+ struct nand_base *parent; -+ u8 tx_mode; -+ u8 rx_mode; -+ u8 op_mode; -+ bool ondie_ecc; -+}; -+ -+static inline struct nand_spi *base_to_spi(struct nand_base *base) -+{ -+ return container_of(base, struct nand_spi, base); -+} -+ -+#endif /* __NAND_SPI_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_base.c -@@ -0,0 +1,304 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nand_chip.h" -+#include "nand_device.h" -+#include "nfi.h" -+#include "nand_base.h" -+ -+static int nand_base_select_device(struct nand_base *nand, int cs) -+{ -+ struct nfi *nfi = nand->nfi; -+ -+ nfi->reset(nfi); -+ -+ return nfi->select_chip(nfi, cs); -+} -+ -+static int nand_base_reset(struct nand_base *nand) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->reset); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tRST); -+} -+ -+static int nand_base_read_id(struct nand_base *nand, u8 *id, int count) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_id); -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tWHR); -+ nfi->send_addr(nfi, 0, 0, 1, 0); -+ -+ return nfi->read_bytes(nfi, id, count); -+} -+ -+static int nand_base_read_param_page(struct nand_base *nand, u8 *data, -+ int count) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_param_page); -+ nfi->send_addr(nfi, 0, 0, 1, 0); -+ -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tR); -+ -+ return nfi->read_bytes(nfi, data, count); -+} -+ -+static int nand_base_set_feature(struct nand_base *nand, u8 addr, -+ u8 *param, -+ int count) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->set_feature); -+ nfi->send_addr(nfi, addr, 0, 1, 0); -+ -+ nfi->write_bytes(nfi, param, count); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tFEAT); -+} -+ -+static int nand_base_get_feature(struct nand_base *nand, u8 addr, -+ u8 *param, -+ int count) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->get_feature); -+ nfi->send_addr(nfi, addr, 0, 1, 0); -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tFEAT); -+ -+ return nfi->read_bytes(nfi, param, count); -+} -+ -+static int nand_base_read_status(struct nand_base *nand) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ u8 status = 0; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_status); -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tWHR); -+ nfi->read_bytes(nfi, &status, 1); -+ -+ return status; -+} -+ -+static int nand_base_addressing(struct nand_base *nand, int *row, -+ int *col) -+{ -+ struct nand_device *dev = nand->dev; -+ int lun, plane, block, page, cs = 0; -+ int block_pages, target_blocks, wl = 0; -+ int icol = *col; -+ -+ if (dev->target_num > 1) { -+ block_pages = nand_block_pages(dev); -+ target_blocks = nand_target_blocks(dev); -+ cs = div_down(*row, block_pages * target_blocks); -+ *row -= cs * block_pages * target_blocks; -+ } -+ -+ nand->select_device(nand, cs); -+ -+ block_pages = nand_block_pages(dev); -+ block = div_down(*row, block_pages); -+ page = *row - block * block_pages; -+ plane = reminder(block, dev->plane_num); -+ lun = div_down(block, nand_lun_blocks(dev)); -+ -+ wl |= (page << dev->addressing->row_bit_start); -+ wl |= (block << dev->addressing->block_bit_start); -+ wl |= (plane << dev->addressing->plane_bit_start); -+ wl |= (lun << dev->addressing->lun_bit_start); -+ -+ *row = wl; -+ *col = icol; -+ -+ return 0; -+} -+ -+static int nand_base_read_page(struct nand_base *nand, int row) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_1st); -+ nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle); -+ nfi->send_cmd(nfi, dev->cmds->read_2nd); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tR); -+} -+ -+static int nand_base_read_data(struct nand_base *nand, int row, int col, -+ int sectors, u8 *data, u8 *oob) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->random_out_1st); -+ nfi->send_addr(nfi, col, row, dev->col_cycle, dev->row_cycle); -+ nfi->send_cmd(nfi, dev->cmds->random_out_2nd); -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tRCBSY); -+ -+ return nfi->read_sectors(nfi, data, oob, sectors); -+} -+ -+static int nand_base_write_enable(struct nand_base *nand) -+{ -+ struct nand_device *dev = nand->dev; -+ int status; -+ -+ status = nand_base_read_status(nand); -+ if (status & dev->status->write_protect) -+ return 0; -+ -+ return -ENANDWP; -+} -+ -+static int nand_base_program_data(struct nand_base *nand, int row, -+ int col, -+ u8 *data, u8 *oob) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->program_1st); -+ nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle); -+ -+ return nfi->write_page(nfi, data, oob); -+} -+ -+static int nand_base_program_page(struct nand_base *nand, int row) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->program_2nd); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tPROG); -+} -+ -+static int nand_base_erase_block(struct nand_base *nand, int row) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->erase_1st); -+ nfi->send_addr(nfi, 0, row, 0, dev->row_cycle); -+ nfi->send_cmd(nfi, dev->cmds->erase_2nd); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tBERS); -+} -+ -+static int nand_base_read_cache(struct nand_base *nand, int row) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_1st); -+ nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle); -+ nfi->send_cmd(nfi, dev->cmds->read_cache); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tRCBSY); -+} -+ -+static int nand_base_read_last(struct nand_base *nand) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_cache_last); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tRCBSY); -+} -+ -+static int nand_base_program_cache(struct nand_base *nand) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->program_cache); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tPCBSY); -+} -+ -+struct nand_base *nand_base_init(struct nand_device *dev, -+ struct nfi *nfi) -+{ -+ struct nand_base *nand; -+ -+ nand = mem_alloc(1, sizeof(struct nand_base)); -+ if (!nand) -+ return NULL; -+ -+ nand->dev = dev; -+ nand->nfi = nfi; -+ nand->select_device = nand_base_select_device; -+ nand->reset = nand_base_reset; -+ nand->read_id = nand_base_read_id; -+ nand->read_param_page = nand_base_read_param_page; -+ nand->set_feature = nand_base_set_feature; -+ nand->get_feature = nand_base_get_feature; -+ nand->read_status = nand_base_read_status; -+ nand->addressing = nand_base_addressing; -+ nand->read_page = nand_base_read_page; -+ nand->read_data = nand_base_read_data; -+ nand->read_cache = nand_base_read_cache; -+ nand->read_last = nand_base_read_last; -+ nand->write_enable = nand_base_write_enable; -+ nand->program_data = nand_base_program_data; -+ nand->program_page = nand_base_program_page; -+ nand->program_cache = nand_base_program_cache; -+ nand->erase_block = nand_base_erase_block; -+ -+ return nand; -+} -+ -+void nand_base_exit(struct nand_base *base) -+{ -+ nfi_exit(base->nfi); -+ mem_free(base); -+} ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_base.h -@@ -0,0 +1,71 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NAND_BASE_H__ -+#define __NAND_BASE_H__ -+ -+/* -+ * nand base functions -+ * @dev: nand device infomations -+ * @nfi: nand host controller -+ * @select_device: select one nand device of multi nand on chip -+ * @reset: reset current nand device -+ * @read_id: read current nand id -+ * @read_param_page: read current nand parameters page -+ * @set_feature: configurate the nand device feature -+ * @get_feature: get the nand device feature -+ * @read_status: read nand device status -+ * @addressing: addressing the address to nand device physical address -+ * @read_page: read page data to device cache register -+ * @read_data: read data from device cache register by bus protocol -+ * @read_cache: nand cache read operation for data output -+ * @read_last: nand cache read operation for last page output -+ * @write_enable: enable program/erase for nand, especially spi nand -+ * @program_data: program data to nand device cache register -+ * @program_page: program page data from nand device cache register to array -+ * @program_cache: nand cache program operation for data input -+ * @erase_block: erase nand block operation -+ */ -+struct nand_base { -+ struct nand_device *dev; -+ struct nfi *nfi; -+ int (*select_device)(struct nand_base *nand, int cs); -+ int (*reset)(struct nand_base *nand); -+ int (*read_id)(struct nand_base *nand, u8 *id, int count); -+ int (*read_param_page)(struct nand_base *nand, u8 *data, int count); -+ int (*set_feature)(struct nand_base *nand, u8 addr, u8 *param, -+ int count); -+ int (*get_feature)(struct nand_base *nand, u8 addr, u8 *param, -+ int count); -+ int (*read_status)(struct nand_base *nand); -+ int (*addressing)(struct nand_base *nand, int *row, int *col); -+ -+ int (*read_page)(struct nand_base *nand, int row); -+ int (*read_data)(struct nand_base *nand, int row, int col, int sectors, -+ u8 *data, u8 *oob); -+ int (*read_cache)(struct nand_base *nand, int row); -+ int (*read_last)(struct nand_base *nand); -+ -+ int (*write_enable)(struct nand_base *nand); -+ int (*program_data)(struct nand_base *nand, int row, int col, u8 *data, -+ u8 *oob); -+ int (*program_page)(struct nand_base *nand, int row); -+ int (*program_cache)(struct nand_base *nand); -+ -+ int (*erase_block)(struct nand_base *nand, int row); -+}; -+ -+struct nand_base *nand_base_init(struct nand_device *device, -+ struct nfi *nfi); -+void nand_base_exit(struct nand_base *base); -+ -+struct nand_base *nand_device_init(struct nand_chip *nand); -+void nand_exit(struct nand_base *nand); -+ -+int nand_detect_device(struct nand_base *nand); -+ -+#endif /* __NAND_BASE_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_chip.c -@@ -0,0 +1,272 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nand_chip.h" -+#include "nand_device.h" -+#include "nfi.h" -+#include "nand_base.h" -+ -+static int nand_chip_read_page(struct nand_chip *chip, -+ struct nand_ops *ops, -+ int count) -+{ -+ struct nand_base *nand = chip->nand; -+ struct nand_device *dev = nand->dev; -+ int i, ret = 0; -+ int row, col, sectors; -+ u8 *data, *oob; -+ -+ for (i = 0; i < count; i++) { -+ row = ops[i].row; -+ col = ops[i].col; -+ -+ nand->addressing(nand, &row, &col); -+ ops[i].status = nand->read_page(nand, row); -+ if (ops[i].status < 0) { -+ ret = ops[i].status; -+ continue; -+ } -+ -+ data = ops[i].data; -+ oob = ops[i].oob; -+ sectors = ops[i].len / chip->sector_size; -+ ops[i].status = nand->read_data(nand, row, col, -+ sectors, data, oob); -+ if (ops[i].status > 0) -+ ops[i].status = ops[i].status >= -+ dev->endurance->max_bitflips ? -+ -ENANDFLIPS : 0; -+ -+ ret = min_t(int, ret, ops[i].status); -+ } -+ -+ return ret; -+} -+ -+static int nand_chip_write_page(struct nand_chip *chip, -+ struct nand_ops *ops, -+ int count) -+{ -+ struct nand_base *nand = chip->nand; -+ struct nand_device *dev = nand->dev; -+ int i, ret = 0; -+ int row, col; -+ u8 *data, *oob; -+ -+ for (i = 0; i < count; i++) { -+ row = ops[i].row; -+ col = ops[i].col; -+ -+ nand->addressing(nand, &row, &col); -+ -+ ops[i].status = nand->write_enable(nand); -+ if (ops[i].status) { -+ pr_debug("Write Protect at %x!\n", row); -+ ops[i].status = -ENANDWP; -+ return -ENANDWP; -+ } -+ -+ data = ops[i].data; -+ oob = ops[i].oob; -+ ops[i].status = nand->program_data(nand, row, col, data, oob); -+ if (ops[i].status < 0) { -+ ret = ops[i].status; -+ continue; -+ } -+ -+ ops[i].status = nand->program_page(nand, row); -+ if (ops[i].status < 0) { -+ ret = ops[i].status; -+ continue; -+ } -+ -+ ops[i].status = nand->read_status(nand); -+ if (ops[i].status & dev->status->program_fail) -+ ops[i].status = -ENANDWRITE; -+ -+ ret = min_t(int, ret, ops[i].status); -+ } -+ -+ return ret; -+} -+ -+static int nand_chip_erase_block(struct nand_chip *chip, -+ struct nand_ops *ops, -+ int count) -+{ -+ struct nand_base *nand = chip->nand; -+ struct nand_device *dev = nand->dev; -+ int i, ret = 0; -+ int row, col; -+ -+ for (i = 0; i < count; i++) { -+ row = ops[i].row; -+ col = ops[i].col; -+ -+ nand->addressing(nand, &row, &col); -+ -+ ops[i].status = nand->write_enable(nand); -+ if (ops[i].status) { -+ pr_debug("Write Protect at %x!\n", row); -+ ops[i].status = -ENANDWP; -+ return -ENANDWP; -+ } -+ -+ ops[i].status = nand->erase_block(nand, row); -+ if (ops[i].status < 0) { -+ ret = ops[i].status; -+ continue; -+ } -+ -+ ops[i].status = nand->read_status(nand); -+ if (ops[i].status & dev->status->erase_fail) -+ ops[i].status = -ENANDERASE; -+ -+ ret = min_t(int, ret, ops[i].status); -+ } -+ -+ return ret; -+} -+ -+/* read first bad mark on spare */ -+static int nand_chip_is_bad_block(struct nand_chip *chip, -+ struct nand_ops *ops, -+ int count) -+{ -+ int i, ret, value; -+ int status = 0; -+ u8 *data, *tmp_buf; -+ -+ tmp_buf = mem_alloc(1, chip->page_size); -+ if (!tmp_buf) -+ return -ENOMEM; -+ -+ memset(tmp_buf, 0x00, chip->page_size); -+ -+ /* Disable ECC */ -+ value = 0; -+ ret = chip->chip_ctrl(chip, NFI_CTRL_ECC, &value); -+ if (ret) -+ goto out; -+ -+ ret = chip->read_page(chip, ops, count); -+ if (ret) -+ goto out; -+ -+ for (i = 0; i < count; i++) { -+ data = ops[i].data; -+ -+ /* temp solution for mt7622, because of no bad mark swap */ -+ if (!memcmp(data, tmp_buf, chip->page_size)) { -+ ops[i].status = -ENANDBAD; -+ status = -ENANDBAD; -+ -+ } else { -+ ops[i].status = 0; -+ } -+ } -+ -+ /* Enable ECC */ -+ value = 1; -+ ret = chip->chip_ctrl(chip, NFI_CTRL_ECC, &value); -+ if (ret) -+ goto out; -+ -+ mem_free(tmp_buf); -+ return status; -+ -+out: -+ mem_free(tmp_buf); -+ return ret; -+} -+ -+static int nand_chip_ctrl(struct nand_chip *chip, int cmd, void *args) -+{ -+ return -EOPNOTSUPP; -+} -+ -+static int nand_chip_suspend(struct nand_chip *chip) -+{ -+ return 0; -+} -+ -+static int nand_chip_resume(struct nand_chip *chip) -+{ -+ return 0; -+} -+ -+struct nand_chip *nand_chip_init(struct nfi_resource *res) -+{ -+ struct nand_chip *chip; -+ struct nand_base *nand; -+ struct nfi *nfi; -+ -+ chip = mem_alloc(1, sizeof(struct nand_chip)); -+ if (!chip) { -+ pr_info("nand chip alloc fail!\n"); -+ return NULL; -+ } -+ -+ nfi = nfi_init(res); -+ if (!nfi) { -+ pr_info("nfi init fail!\n"); -+ goto nfi_err; -+ } -+ -+ nand = nand_base_init(NULL, nfi); -+ if (!nand) { -+ pr_info("nand base init fail!\n"); -+ goto base_err; -+ } -+ -+ chip->nand = (void *)nand; -+ chip->read_page = nand_chip_read_page; -+ chip->write_page = nand_chip_write_page; -+ chip->erase_block = nand_chip_erase_block; -+ chip->is_bad_block = nand_chip_is_bad_block; -+ chip->chip_ctrl = nand_chip_ctrl; -+ chip->suspend = nand_chip_suspend; -+ chip->resume = nand_chip_resume; -+ -+ nand = nand_device_init(chip); -+ if (!nand) -+ goto nand_err; -+ -+ chip->nand = (void *)nand; -+ chip->plane_num = nand->dev->plane_num; -+ chip->block_num = nand_total_blocks(nand->dev); -+ chip->block_size = nand->dev->block_size; -+ chip->block_pages = nand_block_pages(nand->dev); -+ chip->page_size = nand->dev->page_size; -+ chip->oob_size = nfi->fdm_size * div_down(chip->page_size, -+ nfi->sector_size); -+ chip->sector_size = nfi->sector_size; -+ chip->sector_spare_size = nfi->sector_spare_size; -+ chip->min_program_pages = nand->dev->min_program_pages; -+ chip->ecc_strength = nfi->ecc_strength; -+ chip->ecc_parity_size = nfi->ecc_parity_size; -+ chip->fdm_ecc_size = nfi->fdm_ecc_size; -+ chip->fdm_reg_size = nfi->fdm_size; -+ -+ return chip; -+ -+nand_err: -+ mem_free(nand); -+base_err: -+ nfi_exit(nfi); -+nfi_err: -+ mem_free(chip); -+ return NULL; -+} -+ -+void nand_chip_exit(struct nand_chip *chip) -+{ -+ nand_exit(chip->nand); -+ mem_free(chip); -+} ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_chip.h -@@ -0,0 +1,103 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NAND_CHIP_H__ -+#define __NAND_CHIP_H__ -+ -+enum nand_type { -+ NAND_SPI, -+ NAND_SLC, -+ NAND_MLC, -+ NAND_TLC -+}; -+ -+/* -+ * nand chip operation unit -+ * one nand_ops indicates one row operation -+ * @row: nand chip row address, like as nand row -+ * @col: nand chip column address, like as nand column -+ * @len: operate data length, min is sector_size, -+ * max is page_size and sector_size aligned -+ * @status: one operation result status -+ * @data: data buffer for operation -+ * @oob: oob buffer for operation, like as nand spare area -+ */ -+struct nand_ops { -+ int row; -+ int col; -+ int len; -+ int status; -+ void *data; -+ void *oob; -+}; -+ -+/* -+ * nand chip descriptions -+ * nand chip includes nand controller and the several same nand devices -+ * @nand_type: the nand type on this chip, -+ * the chip maybe have several nand device and the type must be same -+ * @plane_num: the whole plane number on the chip -+ * @block_num: the whole block number on the chip -+ * @block_size: nand device block size -+ * @block_pages: nand device block has page number -+ * @page_size: nand device page size -+ * @oob_size: chip out of band size, like as nand spare szie, -+ * but restricts this: -+ * the size is provied by nand controller(NFI), -+ * because NFI would use some nand spare size -+ * @min_program_pages: chip needs min pages per program operations -+ * one page as one nand_ops -+ * @sector_size: chip min read size -+ * @sector_spare_size: spare size for sector, is spare_size/page_sectors -+ * @ecc_strength: ecc stregth per sector_size, it would be for calculated ecc -+ * @ecc_parity_size: ecc parity size for one sector_size data -+ * @nand: pointer to inherited struct nand_base -+ * @read_page: read %count pages on chip -+ * @write_page: write %count pages on chip -+ * @erase_block: erase %count blocks on chip, one block is one nand_ops -+ * it is better to set nand_ops.row to block start row -+ * @is_bad_block: judge the %count blocks on chip if they are bad -+ * by vendor specification -+ * @chip_ctrl: control the chip features by nandx_ctrl_cmd -+ * @suspend: suspend nand chip -+ * @resume: resume nand chip -+ */ -+struct nand_chip { -+ int nand_type; -+ int plane_num; -+ int block_num; -+ int block_size; -+ int block_pages; -+ int page_size; -+ int oob_size; -+ -+ int min_program_pages; -+ int sector_size; -+ int sector_spare_size; -+ int ecc_strength; -+ int ecc_parity_size; -+ u32 fdm_ecc_size; -+ u32 fdm_reg_size; -+ -+ void *nand; -+ -+ int (*read_page)(struct nand_chip *chip, struct nand_ops *ops, -+ int count); -+ int (*write_page)(struct nand_chip *chip, struct nand_ops *ops, -+ int count); -+ int (*erase_block)(struct nand_chip *chip, struct nand_ops *ops, -+ int count); -+ int (*is_bad_block)(struct nand_chip *chip, struct nand_ops *ops, -+ int count); -+ int (*chip_ctrl)(struct nand_chip *chip, int cmd, void *args); -+ int (*suspend)(struct nand_chip *chip); -+ int (*resume)(struct nand_chip *chip); -+}; -+ -+struct nand_chip *nand_chip_init(struct nfi_resource *res); -+void nand_chip_exit(struct nand_chip *chip); -+#endif /* __NAND_CHIP_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_device.c -@@ -0,0 +1,285 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nand_chip.h" -+#include "nand_device.h" -+#include "nand_base.h" -+ -+#define MAX_CHIP_DEVICE 4 -+#define PARAM_PAGE_LEN 2048 -+#define ONFI_CRC_BASE 0x4f4e -+ -+static u16 nand_onfi_crc16(u16 crc, u8 const *p, size_t len) -+{ -+ int i; -+ -+ while (len--) { -+ crc ^= *p++ << 8; -+ -+ for (i = 0; i < 8; i++) -+ crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); -+ } -+ -+ return crc; -+} -+ -+static inline void decode_addr_cycle(u8 addr_cycle, u8 *row_cycle, -+ u8 *col_cycle) -+{ -+ *row_cycle = addr_cycle & 0xf; -+ *col_cycle = (addr_cycle >> 4) & 0xf; -+} -+ -+static int detect_onfi(struct nand_device *dev, -+ struct nand_onfi_params *onfi) -+{ -+ struct nand_endurance *endurance = dev->endurance; -+ u16 size, i, crc16; -+ u8 *id; -+ -+ size = sizeof(struct nand_onfi_params) - sizeof(u16); -+ -+ for (i = 0; i < 3; i++) { -+ crc16 = nand_onfi_crc16(ONFI_CRC_BASE, (u8 *)&onfi[i], size); -+ -+ if (onfi[i].signature[0] == 'O' && -+ onfi[i].signature[1] == 'N' && -+ onfi[i].signature[2] == 'F' && -+ onfi[i].signature[3] == 'I' && -+ onfi[i].crc16 == crc16) -+ break; -+ -+ /* in some spi nand, onfi signature maybe "NAND" */ -+ if (onfi[i].signature[0] == 'N' && -+ onfi[i].signature[1] == 'A' && -+ onfi[i].signature[2] == 'N' && -+ onfi[i].signature[3] == 'D' && -+ onfi[i].crc16 == crc16) -+ break; -+ } -+ -+ if (i == 3) -+ return -ENODEV; -+ -+ memcpy(dev->name, onfi[i].model, 20); -+ id = onfi[i].manufacturer; -+ dev->id = NAND_PACK_ID(id[0], id[1], id[2], id[3], id[4], id[5], id[6], -+ id[7]); -+ dev->id_len = MAX_ID_NUM; -+ dev->io_width = (onfi[i].features & 1) ? NAND_IO16 : NAND_IO8; -+ decode_addr_cycle(onfi[i].addr_cycle, &dev->row_cycle, -+ &dev->col_cycle); -+ dev->target_num = 1; -+ dev->lun_num = onfi[i].lun_num; -+ dev->plane_num = BIT(onfi[i].plane_address_bits); -+ dev->block_num = onfi[i].lun_blocks / dev->plane_num; -+ dev->block_size = onfi[i].block_pages * onfi[i].page_size; -+ dev->page_size = onfi[i].page_size; -+ dev->spare_size = onfi[i].spare_size; -+ -+ endurance->ecc_req = onfi[i].ecc_req; -+ endurance->pe_cycle = onfi[i].valid_block_endurance; -+ endurance->max_bitflips = endurance->ecc_req >> 1; -+ -+ return 0; -+} -+ -+static int detect_jedec(struct nand_device *dev, -+ struct nand_jedec_params *jedec) -+{ -+ struct nand_endurance *endurance = dev->endurance; -+ u16 size, i, crc16; -+ u8 *id; -+ -+ size = sizeof(struct nand_jedec_params) - sizeof(u16); -+ -+ for (i = 0; i < 3; i++) { -+ crc16 = nand_onfi_crc16(ONFI_CRC_BASE, (u8 *)&jedec[i], size); -+ -+ if (jedec[i].signature[0] == 'J' && -+ jedec[i].signature[1] == 'E' && -+ jedec[i].signature[2] == 'S' && -+ jedec[i].signature[3] == 'D' && -+ jedec[i].crc16 == crc16) -+ break; -+ } -+ -+ if (i == 3) -+ return -ENODEV; -+ -+ memcpy(dev->name, jedec[i].model, 20); -+ id = jedec[i].manufacturer; -+ dev->id = NAND_PACK_ID(id[0], id[1], id[2], id[3], id[4], id[5], id[6], -+ id[7]); -+ dev->id_len = MAX_ID_NUM; -+ dev->io_width = (jedec[i].features & 1) ? NAND_IO16 : NAND_IO8; -+ decode_addr_cycle(jedec[i].addr_cycle, &dev->row_cycle, -+ &dev->col_cycle); -+ dev->target_num = 1; -+ dev->lun_num = jedec[i].lun_num; -+ dev->plane_num = BIT(jedec[i].plane_address_bits); -+ dev->block_num = jedec[i].lun_blocks / dev->plane_num; -+ dev->block_size = jedec[i].block_pages * jedec[i].page_size; -+ dev->page_size = jedec[i].page_size; -+ dev->spare_size = jedec[i].spare_size; -+ -+ endurance->ecc_req = jedec[i].endurance_block0[0]; -+ endurance->pe_cycle = jedec[i].valid_block_endurance; -+ endurance->max_bitflips = endurance->ecc_req >> 1; -+ -+ return 0; -+} -+ -+static struct nand_device *detect_parameters_page(struct nand_base -+ *nand) -+{ -+ struct nand_device *dev = nand->dev; -+ void *params; -+ int ret; -+ -+ params = mem_alloc(1, PARAM_PAGE_LEN); -+ if (!params) -+ return NULL; -+ -+ memset(params, 0, PARAM_PAGE_LEN); -+ ret = nand->read_param_page(nand, params, PARAM_PAGE_LEN); -+ if (ret < 0) { -+ pr_info("read parameters page fail!\n"); -+ goto error; -+ } -+ -+ ret = detect_onfi(dev, params); -+ if (ret) { -+ pr_info("detect onfi device fail! try to detect jedec\n"); -+ ret = detect_jedec(dev, params); -+ if (ret) { -+ pr_info("detect jedec device fail!\n"); -+ goto error; -+ } -+ } -+ -+ mem_free(params); -+ return dev; -+ -+error: -+ mem_free(params); -+ return NULL; -+} -+ -+static int read_device_id(struct nand_base *nand, int cs, u8 *id) -+{ -+ int i; -+ -+ nand->select_device(nand, cs); -+ nand->reset(nand); -+ nand->read_id(nand, id, MAX_ID_NUM); -+ pr_info("device %d ID: ", cs); -+ -+ for (i = 0; i < MAX_ID_NUM; i++) -+ pr_info("%x ", id[i]); -+ -+ pr_info("\n"); -+ -+ return 0; -+} -+ -+static int detect_more_device(struct nand_base *nand, u8 *id) -+{ -+ u8 id_ext[MAX_ID_NUM]; -+ int i, j, target_num = 0; -+ -+ for (i = 1; i < MAX_CHIP_DEVICE; i++) { -+ memset(id_ext, 0xff, MAX_ID_NUM); -+ read_device_id(nand, i, id_ext); -+ -+ for (j = 0; j < MAX_ID_NUM; j++) { -+ if (id_ext[j] != id[j]) -+ goto out; -+ } -+ -+ target_num += 1; -+ } -+ -+out: -+ return target_num; -+} -+ -+static struct nand_device *scan_device_table(const u8 *id, int id_len) -+{ -+ struct nand_device *dev; -+ int i = 0, j; -+ u8 ids[MAX_ID_NUM] = {0}; -+ -+ while (1) { -+ dev = nand_get_device(i); -+ -+ if (!strcmp(dev->name, "NO-DEVICE")) -+ break; -+ -+ if (id_len < dev->id_len) { -+ i += 1; -+ continue; -+ } -+ -+ NAND_UNPACK_ID(dev->id, ids, MAX_ID_NUM); -+ for (j = 0; j < dev->id_len; j++) { -+ if (ids[j] != id[j]) -+ break; -+ } -+ -+ if (j == dev->id_len) -+ break; -+ -+ i += 1; -+ } -+ -+ return dev; -+} -+ -+int nand_detect_device(struct nand_base *nand) -+{ -+ struct nand_device *dev; -+ u8 id[MAX_ID_NUM] = { 0 }; -+ int target_num = 0; -+ -+ /* Get nand device default setting for reset/read_id */ -+ nand->dev = scan_device_table(NULL, -1); -+ -+ read_device_id(nand, 0, id); -+ dev = scan_device_table(id, MAX_ID_NUM); -+ -+ if (!strcmp(dev->name, "NO-DEVICE")) { -+ pr_info("device scan fail\n"); -+ return -ENODEV; -+ } -+ -+ /* TobeFix: has null pointer issue in this funciton */ -+ if (!strcmp(dev->name, "NO-DEVICE")) { -+ pr_info("device scan fail, detect parameters page\n"); -+ dev = detect_parameters_page(nand); -+ if (!dev) { -+ pr_info("detect parameters fail\n"); -+ return -ENODEV; -+ } -+ } -+ -+ if (dev->target_num > 1) -+ target_num = detect_more_device(nand, id); -+ -+ target_num += 1; -+ pr_debug("chip has target device num: %d\n", target_num); -+ -+ if (dev->target_num != target_num) -+ dev->target_num = target_num; -+ -+ nand->dev = dev; -+ -+ return 0; -+} -+ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_device.h -@@ -0,0 +1,608 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NAND_DEVICE_H__ -+#define __NAND_DEVICE_H__ -+ -+/* onfi 3.2 */ -+struct nand_onfi_params { -+ /* Revision information and features block. 0 */ -+ /* -+ * Byte 0: 4Fh, -+ * Byte 1: 4Eh, -+ * Byte 2: 46h, -+ * Byte 3: 49h, -+ */ -+ u8 signature[4]; -+ /* -+ * 9-15 Reserved (0) -+ * 8 1 = supports ONFI version 3.2 -+ * 7 1 = supports ONFI version 3.1 -+ * 6 1 = supports ONFI version 3.0 -+ * 5 1 = supports ONFI version 2.3 -+ * 4 1 = supports ONFI version 2.2 -+ * 3 1 = supports ONFI version 2.1 -+ * 2 1 = supports ONFI version 2.0 -+ * 1 1 = supports ONFI version 1.0 -+ * 0 Reserved (0) -+ */ -+ u16 revision; -+ /* -+ * 13-15 Reserved (0) -+ * 12 1 = supports external Vpp -+ * 11 1 = supports Volume addressing -+ * 10 1 = supports NV-DDR2 -+ * 9 1 = supports EZ NAND -+ * 8 1 = supports program page register clear enhancement -+ * 7 1 = supports extended parameter page -+ * 6 1 = supports multi-plane read operations -+ * 5 1 = supports NV-DDR -+ * 4 1 = supports odd to even page Copyback -+ * 3 1 = supports multi-plane program and erase operations -+ * 2 1 = supports non-sequential page programming -+ * 1 1 = supports multiple LUN operations -+ * 0 1 = supports 16-bit data bus width -+ */ -+ u16 features; -+ /* -+ * 13-15 Reserved (0) -+ * 12 1 = supports LUN Get and LUN Set Features -+ * 11 1 = supports ODT Configure -+ * 10 1 = supports Volume Select -+ * 9 1 = supports Reset LUN -+ * 8 1 = supports Small Data Move -+ * 7 1 = supports Change Row Address -+ * 6 1 = supports Change Read Column Enhanced -+ * 5 1 = supports Read Unique ID -+ * 4 1 = supports Copyback -+ * 3 1 = supports Read Status Enhanced -+ * 2 1 = supports Get Features and Set Features -+ * 1 1 = supports Read Cache commands -+ * 0 1 = supports Page Cache Program command -+ */ -+ u16 opt_cmds; -+ /* -+ * 4-7 Reserved (0) -+ * 3 1 = supports Multi-plane Block Erase -+ * 2 1 = supports Multi-plane Copyback Program -+ * 1 1 = supports Multi-plane Page Program -+ * 0 1 = supports Random Data Out -+ */ -+ u8 advance_cmds; -+ u8 reserved0[1]; -+ u16 extend_param_len; -+ u8 param_page_num; -+ u8 reserved1[17]; -+ -+ /* Manufacturer information block. 32 */ -+ u8 manufacturer[12]; -+ u8 model[20]; -+ u8 jedec_id; -+ u16 data_code; -+ u8 reserved2[13]; -+ -+ /* Memory organization block. 80 */ -+ u32 page_size; -+ u16 spare_size; -+ u32 partial_page_size; /* obsolete */ -+ u16 partial_spare_size; /* obsolete */ -+ u32 block_pages; -+ u32 lun_blocks; -+ u8 lun_num; -+ /* -+ * 4-7 Column address cycles -+ * 0-3 Row address cycles -+ */ -+ u8 addr_cycle; -+ u8 cell_bits; -+ u16 lun_max_bad_blocks; -+ u16 block_endurance; -+ u8 target_begin_valid_blocks; -+ u16 valid_block_endurance; -+ u8 page_program_num; -+ u8 partial_program_attr; /* obsolete */ -+ u8 ecc_req; -+ /* -+ * 4-7 Reserved (0) -+ * 0-3 Number of plane address bits -+ */ -+ u8 plane_address_bits; -+ /* -+ * 6-7 Reserved (0) -+ * 5 1 = lower bit XNOR block address restriction -+ * 4 1 = read cache supported -+ * 3 Address restrictions for cache operations -+ * 2 1 = program cache supported -+ * 1 1 = no block address restrictions -+ * 0 Overlapped / concurrent multi-plane support -+ */ -+ u8 multi_plane_attr; -+ u8 ez_nand_support; -+ u8 reserved3[12]; -+ -+ /* Electrical parameters block. 128 */ -+ u8 io_pin_max_capacitance; -+ /* -+ * 6-15 Reserved (0) -+ * 5 1 = supports timing mode 5 -+ * 4 1 = supports timing mode 4 -+ * 3 1 = supports timing mode 3 -+ * 2 1 = supports timing mode 2 -+ * 1 1 = supports timing mode 1 -+ * 0 1 = supports timing mode 0, shall be 1 -+ */ -+ u16 sdr_timing_mode; -+ u16 sdr_program_cache_timing_mode; /* obsolete */ -+ u16 tPROG; -+ u16 tBERS; -+ u16 tR; -+ u16 tCCS; -+ /* -+ * 7 Reserved (0) -+ * 6 1 = supports NV-DDR2 timing mode 8 -+ * 5 1 = supports NV-DDR timing mode 5 -+ * 4 1 = supports NV-DDR timing mode 4 -+ * 3 1 = supports NV-DDR timing mode 3 -+ * 2 1 = supports NV-DDR timing mode 2 -+ * 1 1 = supports NV-DDR timing mode 1 -+ * 0 1 = supports NV-DDR timing mode 0 -+ */ -+ u8 nvddr_timing_mode; -+ /* -+ * 7 1 = supports timing mode 7 -+ * 6 1 = supports timing mode 6 -+ * 5 1 = supports timing mode 5 -+ * 4 1 = supports timing mode 4 -+ * 3 1 = supports timing mode 3 -+ * 2 1 = supports timing mode 2 -+ * 1 1 = supports timing mode 1 -+ * 0 1 = supports timing mode 0 -+ */ -+ u8 nvddr2_timing_mode; -+ /* -+ * 4-7 Reserved (0) -+ * 3 1 = device requires Vpp enablement sequence -+ * 2 1 = device supports CLK stopped for data input -+ * 1 1 = typical capacitance -+ * 0 tCAD value to use -+ */ -+ u8 nvddr_fetures; -+ u16 clk_pin_capacitance; -+ u16 io_pin_capacitance; -+ u16 input_pin_capacitance; -+ u8 input_pin_max_capacitance; -+ /* -+ * 3-7 Reserved (0) -+ * 2 1 = supports 18 Ohm drive strength -+ * 1 1 = supports 25 Ohm drive strength -+ * 0 1 = supports driver strength settings -+ */ -+ u8 drive_strength; -+ u16 tR_multi_plane; -+ u16 tADL; -+ u16 tR_ez_nand; -+ /* -+ * 6-7 Reserved (0) -+ * 5 1 = external VREFQ required for >= 200 MT/s -+ * 4 1 = supports differential signaling for DQS -+ * 3 1 = supports differential signaling for RE_n -+ * 2 1 = supports ODT value of 30 Ohms -+ * 1 1 = supports matrix termination ODT -+ * 0 1 = supports self-termination ODT -+ */ -+ u8 nvddr2_features; -+ u8 nvddr2_warmup_cycles; -+ u8 reserved4[4]; -+ -+ /* vendor block. 164 */ -+ u16 vendor_revision; -+ u8 vendor_spec[88]; -+ -+ /* CRC for Parameter Page. 254 */ -+ u16 crc16; -+} __packed; -+ -+/* JESD230-B */ -+struct nand_jedec_params { -+ /* Revision information and features block. 0 */ -+ /* -+ * Byte 0:4Ah -+ * Byte 1:45h -+ * Byte 2:53h -+ * Byte 3:44h -+ */ -+ u8 signature[4]; -+ /* -+ * 3-15: Reserved (0) -+ * 2: 1 = supports parameter page revision 1.0 and standard revision 1.0 -+ * 1: 1 = supports vendor specific parameter page -+ * 0: Reserved (0) -+ */ -+ u16 revision; -+ /* -+ * 9-15 Reserved (0) -+ * 8: 1 = supports program page register clear enhancement -+ * 7: 1 = supports external Vpp -+ * 6: 1 = supports Toggle Mode DDR -+ * 5: 1 = supports Synchronous DDR -+ * 4: 1 = supports multi-plane read operations -+ * 3: 1 = supports multi-plane program and erase operations -+ * 2: 1 = supports non-sequential page programming -+ * 1: 1 = supports multiple LUN operations -+ * 0: 1 = supports 16-bit data bus width -+ */ -+ u16 features; -+ /* -+ * 11-23: Reserved (0) -+ * 10: 1 = supports Synchronous Reset -+ * 9: 1 = supports Reset LUN (Primary) -+ * 8: 1 = supports Small Data Move -+ * 7: 1 = supports Multi-plane Copyback Program (Primary) -+ * 6: 1 = supports Random Data Out (Primary) -+ * 5: 1 = supports Read Unique ID -+ * 4: 1 = supports Copyback -+ * 3: 1 = supports Read Status Enhanced (Primary) -+ * 2: 1 = supports Get Features and Set Features -+ * 1: 1 = supports Read Cache commands -+ * 0: 1 = supports Page Cache Program command -+ */ -+ u8 opt_cmds[3]; -+ /* -+ * 8-15: Reserved (0) -+ * 7: 1 = supports secondary Read Status Enhanced -+ * 6: 1 = supports secondary Multi-plane Block Erase -+ * 5: 1 = supports secondary Multi-plane Copyback Program -+ * 4: 1 = supports secondary Multi-plane Program -+ * 3: 1 = supports secondary Random Data Out -+ * 2: 1 = supports secondary Multi-plane Copyback Read -+ * 1: 1 = supports secondary Multi-plane Read Cache Random -+ * 0: 1 = supports secondary Multi-plane Read -+ */ -+ u16 secondary_cmds; -+ u8 param_page_num; -+ u8 reserved0[18]; -+ -+ /* Manufacturer information block. 32*/ -+ u8 manufacturer[12]; -+ u8 model[20]; -+ u8 jedec_id[6]; -+ u8 reserved1[10]; -+ -+ /* Memory organization block. 80 */ -+ u32 page_size; -+ u16 spare_size; -+ u8 reserved2[6]; -+ u32 block_pages; -+ u32 lun_blocks; -+ u8 lun_num; -+ /* -+ * 4-7 Column address cycles -+ * 0-3 Row address cycles -+ */ -+ u8 addr_cycle; -+ u8 cell_bits; -+ u8 page_program_num; -+ /* -+ * 4-7 Reserved (0) -+ * 0-3 Number of plane address bits -+ */ -+ u8 plane_address_bits; -+ /* -+ * 3-7: Reserved (0) -+ * 2: 1= read cache supported -+ * 1: 1 = program cache supported -+ * 0: 1= No multi-plane block address restrictions -+ */ -+ u8 multi_plane_attr; -+ u8 reserved3[38]; -+ -+ /* Electrical parameters block. 144 */ -+ /* -+ * 6-15: Reserved (0) -+ * 5: 1 = supports 20 ns speed grade (50 MHz) -+ * 4: 1 = supports 25 ns speed grade (40 MHz) -+ * 3: 1 = supports 30 ns speed grade (~33 MHz) -+ * 2: 1 = supports 35 ns speed grade (~28 MHz) -+ * 1: 1 = supports 50 ns speed grade (20 MHz) -+ * 0: 1 = supports 100 ns speed grade (10 MHz) -+ */ -+ u16 sdr_speed; -+ /* -+ * 8-15: Reserved (0) -+ * 7: 1 = supports 5 ns speed grade (200 MHz) -+ * 6: 1 = supports 6 ns speed grade (~166 MHz) -+ * 5: 1 = supports 7.5 ns speed grade (~133 MHz) -+ * 4: 1 = supports 10 ns speed grade (100 MHz) -+ * 3: 1 = supports 12 ns speed grade (~83 MHz) -+ * 2: 1 = supports 15 ns speed grade (~66 MHz) -+ * 1: 1 = supports 25 ns speed grade (40 MHz) -+ * 0: 1 = supports 30 ns speed grade (~33 MHz) -+ */ -+ u16 toggle_ddr_speed; -+ /* -+ * 6-15: Reserved (0) -+ * 5: 1 = supports 10 ns speed grade (100 MHz) -+ * 4: 1 = supports 12 ns speed grade (~83 MHz) -+ * 3: 1 = supports 15 ns speed grade (~66 MHz) -+ * 2: 1 = supports 20 ns speed grade (50 MHz) -+ * 1: 1 = supports 30 ns speed grade (~33 MHz) -+ * 0: 1 = supports 50 ns speed grade (20 MHz) -+ */ -+ u16 sync_ddr_speed; -+ u8 sdr_features; -+ u8 toggle_ddr_features; -+ /* -+ * 2-7: Reserved (0) -+ * 1: Device supports CK stopped for data input -+ * 0: tCAD value to use -+ */ -+ u8 sync_ddr_features; -+ u16 tPROG; -+ u16 tBERS; -+ u16 tR; -+ u16 tR_multi_plane; -+ u16 tCCS; -+ u16 io_pin_capacitance; -+ u16 input_pin_capacitance; -+ u16 ck_pin_capacitance; -+ /* -+ * 3-7: Reserved (0) -+ * 2: 1 = supports 18 ohm drive strength -+ * 1: 1 = supports 25 ohm drive strength -+ * 0: 1 = supports 35ohm/50ohm drive strength -+ */ -+ u8 drive_strength; -+ u16 tADL; -+ u8 reserved4[36]; -+ -+ /* ECC and endurance block. 208 */ -+ u8 target_begin_valid_blocks; -+ u16 valid_block_endurance; -+ /* -+ * Byte 0: Number of bits ECC correctability -+ * Byte 1: Codeword size -+ * Byte 2-3: Bad blocks maximum per LUN -+ * Byte 4-5: Block endurance -+ * Byte 6-7: Reserved (0) -+ */ -+ u8 endurance_block0[8]; -+ u8 endurance_block1[8]; -+ u8 endurance_block2[8]; -+ u8 endurance_block3[8]; -+ u8 reserved5[29]; -+ -+ /* Reserved. 272 */ -+ u8 reserved6[148]; -+ -+ /* Vendor specific block. 420 */ -+ u16 vendor_revision; -+ u8 vendor_spec[88]; -+ -+ /* CRC for Parameter Page. 510 */ -+ u16 crc16; -+} __packed; -+ -+/* parallel nand io width */ -+enum nand_io_width { -+ NAND_IO8, -+ NAND_IO16 -+}; -+ -+/* all supported nand timming type */ -+enum nand_timing_type { -+ NAND_TIMING_SDR, -+ NAND_TIMING_SYNC_DDR, -+ NAND_TIMING_TOGGLE_DDR, -+ NAND_TIMING_NVDDR2 -+}; -+ -+/* nand basic commands */ -+struct nand_cmds { -+ short reset; -+ short read_id; -+ short read_status; -+ short read_param_page; -+ short set_feature; -+ short get_feature; -+ short read_1st; -+ short read_2nd; -+ short random_out_1st; -+ short random_out_2nd; -+ short program_1st; -+ short program_2nd; -+ short erase_1st; -+ short erase_2nd; -+ short read_cache; -+ short read_cache_last; -+ short program_cache; -+}; -+ -+/* -+ * addressing for nand physical address -+ * @row_bit_start: row address start bit -+ * @block_bit_start: block address start bit -+ * @plane_bit_start: plane address start bit -+ * @lun_bit_start: lun address start bit -+ */ -+struct nand_addressing { -+ u8 row_bit_start; -+ u8 block_bit_start; -+ u8 plane_bit_start; -+ u8 lun_bit_start; -+}; -+ -+/* -+ * nand operations status -+ * @array_busy: indicates device array operation busy -+ * @write_protect: indicates the device cannot be wrote or erased -+ * @erase_fail: indicates erase operation fail -+ * @program_fail: indicates program operation fail -+ */ -+struct nand_status { -+ u8 array_busy; -+ u8 write_protect; -+ u8 erase_fail; -+ u8 program_fail; -+}; -+ -+/* -+ * nand endurance information -+ * @pe_cycle: max program/erase cycle for nand stored data stability -+ * @ecc_req: ecc strength required for the nand, measured per 1KB -+ * @max_bitflips: bitflips is ecc corrected bits, -+ * max_bitflips is the threshold for nand stored data stability -+ * if corrected bits is over max_bitflips, stored data must be moved -+ * to another good block -+ */ -+struct nand_endurance { -+ int pe_cycle; -+ int ecc_req; -+ int max_bitflips; -+}; -+ -+/* wait for nand busy type */ -+enum nand_wait_type { -+ NAND_WAIT_IRQ, -+ NAND_WAIT_POLLING, -+ NAND_WAIT_TWHR2, -+}; -+ -+/* each nand array operations time */ -+struct nand_array_timing { -+ u16 tRST; -+ u16 tWHR; -+ u16 tR; -+ u16 tRCBSY; -+ u16 tFEAT; -+ u16 tPROG; -+ u16 tPCBSY; -+ u16 tBERS; -+ u16 tDBSY; -+}; -+ -+/* nand sdr interface timing required */ -+struct nand_sdr_timing { -+ u16 tREA; -+ u16 tREH; -+ u16 tCR; -+ u16 tRP; -+ u16 tWP; -+ u16 tWH; -+ u16 tWHR; -+ u16 tCLS; -+ u16 tALS; -+ u16 tCLH; -+ u16 tALH; -+ u16 tWC; -+ u16 tRC; -+}; -+ -+/* nand onfi ddr (nvddr) interface timing required */ -+struct nand_onfi_timing { -+ u16 tCAD; -+ u16 tWPRE; -+ u16 tWPST; -+ u16 tWRCK; -+ u16 tDQSCK; -+ u16 tWHR; -+}; -+ -+/* nand toggle ddr (toggle 1.0) interface timing required */ -+struct nand_toggle_timing { -+ u16 tCS; -+ u16 tCH; -+ u16 tCAS; -+ u16 tCAH; -+ u16 tCALS; -+ u16 tCALH; -+ u16 tWP; -+ u16 tWPRE; -+ u16 tWPST; -+ u16 tWPSTH; -+ u16 tCR; -+ u16 tRPRE; -+ u16 tRPST; -+ u16 tRPSTH; -+ u16 tCDQSS; -+ u16 tWHR; -+}; -+ -+/* nand basic device information */ -+struct nand_device { -+ u8 *name; -+ u64 id; -+ u8 id_len; -+ u8 io_width; -+ u8 row_cycle; -+ u8 col_cycle; -+ u8 target_num; -+ u8 lun_num; -+ u8 plane_num; -+ int block_num; -+ int block_size; -+ int page_size; -+ int spare_size; -+ int min_program_pages; -+ struct nand_cmds *cmds; -+ struct nand_addressing *addressing; -+ struct nand_status *status; -+ struct nand_endurance *endurance; -+ struct nand_array_timing *array_timing; -+}; -+ -+#define NAND_DEVICE(_name, _id, _id_len, _io_width, _row_cycle, \ -+ _col_cycle, _target_num, _lun_num, _plane_num, \ -+ _block_num, _block_size, _page_size, _spare_size, \ -+ _min_program_pages, _cmds, _addressing, _status, \ -+ _endurance, _array_timing) \ -+{ \ -+ _name, _id, _id_len, _io_width, _row_cycle, \ -+ _col_cycle, _target_num, _lun_num, _plane_num, \ -+ _block_num, _block_size, _page_size, _spare_size, \ -+ _min_program_pages, _cmds, _addressing, _status, \ -+ _endurance, _array_timing \ -+} -+ -+#define MAX_ID_NUM sizeof(u64) -+ -+#define NAND_PACK_ID(id0, id1, id2, id3, id4, id5, id6, id7) \ -+ ( \ -+ id0 | id1 << 8 | id2 << 16 | id3 << 24 | \ -+ (u64)id4 << 32 | (u64)id5 << 40 | \ -+ (u64)id6 << 48 | (u64)id7 << 56 \ -+ ) -+ -+#define NAND_UNPACK_ID(id, ids, len) \ -+ do { \ -+ int _i; \ -+ for (_i = 0; _i < len; _i++) \ -+ ids[_i] = id >> (_i << 3) & 0xff; \ -+ } while (0) -+ -+static inline int nand_block_pages(struct nand_device *device) -+{ -+ return div_down(device->block_size, device->page_size); -+} -+ -+static inline int nand_lun_blocks(struct nand_device *device) -+{ -+ return device->plane_num * device->block_num; -+} -+ -+static inline int nand_target_blocks(struct nand_device *device) -+{ -+ return device->lun_num * device->plane_num * device->block_num; -+} -+ -+static inline int nand_total_blocks(struct nand_device *device) -+{ -+ return device->target_num * device->lun_num * device->plane_num * -+ device->block_num; -+} -+ -+struct nand_device *nand_get_device(int index); -+#endif /* __NAND_DEVICE_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi.h -@@ -0,0 +1,51 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_H__ -+#define __NFI_H__ -+ -+struct nfi_format { -+ int page_size; -+ int spare_size; -+ int ecc_req; -+}; -+ -+struct nfi { -+ int sector_size; -+ int sector_spare_size; -+ int fdm_size; /*for sector*/ -+ int fdm_ecc_size; -+ int ecc_strength; -+ int ecc_parity_size; /*for sector*/ -+ -+ int (*select_chip)(struct nfi *nfi, int cs); -+ int (*set_format)(struct nfi *nfi, struct nfi_format *format); -+ int (*set_timing)(struct nfi *nfi, void *timing, int type); -+ int (*nfi_ctrl)(struct nfi *nfi, int cmd, void *args); -+ -+ int (*reset)(struct nfi *nfi); -+ int (*send_cmd)(struct nfi *nfi, short cmd); -+ int (*send_addr)(struct nfi *nfi, int col, int row, -+ int col_cycle, int row_cycle); -+ int (*trigger)(struct nfi *nfi); -+ -+ int (*write_page)(struct nfi *nfi, u8 *data, u8 *fdm); -+ int (*write_bytes)(struct nfi *nfi, u8 *data, int count); -+ int (*read_sectors)(struct nfi *nfi, u8 *data, u8 *fdm, -+ int sectors); -+ int (*read_bytes)(struct nfi *nfi, u8 *data, int count); -+ -+ int (*wait_ready)(struct nfi *nfi, int type, u32 timeout); -+ -+ int (*enable_randomizer)(struct nfi *nfi, u32 row, bool encode); -+ int (*disable_randomizer)(struct nfi *nfi); -+}; -+ -+struct nfi *nfi_init(struct nfi_resource *res); -+void nfi_exit(struct nfi *nfi); -+ -+#endif /* __NFI_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_base.c -@@ -0,0 +1,1357 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+/** -+ * nfi_base.c - the base logic for nfi to access nand flash -+ * -+ * slc/mlc/tlc could use same code to access nand -+ * of cause, there still some work need to do -+ * even for spi nand, there should be a chance to integrate code together -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "../nfi.h" -+#include "../nand_device.h" -+#include "nfi_regs.h" -+#include "nfiecc.h" -+#include "nfi_base.h" -+ -+static const int spare_size_mt7622[] = { -+ 16, 26, 27, 28 -+}; -+ -+#define RAND_SEED_SHIFT(op) \ -+ ((op) == RAND_ENCODE ? ENCODE_SEED_SHIFT : DECODE_SEED_SHIFT) -+#define RAND_EN(op) \ -+ ((op) == RAND_ENCODE ? RAN_ENCODE_EN : RAN_DECODE_EN) -+ -+#define SS_SEED_NUM 128 -+static u16 ss_randomizer_seed[SS_SEED_NUM] = { -+ 0x576A, 0x05E8, 0x629D, 0x45A3, 0x649C, 0x4BF0, 0x2342, 0x272E, -+ 0x7358, 0x4FF3, 0x73EC, 0x5F70, 0x7A60, 0x1AD8, 0x3472, 0x3612, -+ 0x224F, 0x0454, 0x030E, 0x70A5, 0x7809, 0x2521, 0x484F, 0x5A2D, -+ 0x492A, 0x043D, 0x7F61, 0x3969, 0x517A, 0x3B42, 0x769D, 0x0647, -+ 0x7E2A, 0x1383, 0x49D9, 0x07B8, 0x2578, 0x4EEC, 0x4423, 0x352F, -+ 0x5B22, 0x72B9, 0x367B, 0x24B6, 0x7E8E, 0x2318, 0x6BD0, 0x5519, -+ 0x1783, 0x18A7, 0x7B6E, 0x7602, 0x4B7F, 0x3648, 0x2C53, 0x6B99, -+ 0x0C23, 0x67CF, 0x7E0E, 0x4D8C, 0x5079, 0x209D, 0x244A, 0x747B, -+ 0x350B, 0x0E4D, 0x7004, 0x6AC3, 0x7F3E, 0x21F5, 0x7A15, 0x2379, -+ 0x1517, 0x1ABA, 0x4E77, 0x15A1, 0x04FA, 0x2D61, 0x253A, 0x1302, -+ 0x1F63, 0x5AB3, 0x049A, 0x5AE8, 0x1CD7, 0x4A00, 0x30C8, 0x3247, -+ 0x729C, 0x5034, 0x2B0E, 0x57F2, 0x00E4, 0x575B, 0x6192, 0x38F8, -+ 0x2F6A, 0x0C14, 0x45FC, 0x41DF, 0x38DA, 0x7AE1, 0x7322, 0x62DF, -+ 0x5E39, 0x0E64, 0x6D85, 0x5951, 0x5937, 0x6281, 0x33A1, 0x6A32, -+ 0x3A5A, 0x2BAC, 0x743A, 0x5E74, 0x3B2E, 0x7EC7, 0x4FD2, 0x5D28, -+ 0x751F, 0x3EF8, 0x39B1, 0x4E49, 0x746B, 0x6EF6, 0x44BE, 0x6DB7 -+}; -+ -+#if 0 -+static void dump_register(void *regs) -+{ -+ int i; -+ -+ pr_info("registers:\n"); -+ for (i = 0; i < 0x600; i += 0x10) { -+ pr_info(" address 0x%X : %X %X %X %X\n", -+ (u32)((unsigned long)regs + i), -+ (u32)readl(regs + i), -+ (u32)readl(regs + i + 0x4), -+ (u32)readl(regs + i + 0x8), -+ (u32)readl(regs + i + 0xC)); -+ } -+} -+#endif -+ -+static int nfi_enable_randomizer(struct nfi *nfi, u32 row, bool encode) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ enum randomizer_op op = RAND_ENCODE; -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ if (!encode) -+ op = RAND_DECODE; -+ -+ /* randomizer type and reseed type setup */ -+ val = readl(regs + NFI_CNFG); -+ val |= CNFG_RAND_SEL | CNFG_RESEED_SEC_EN; -+ writel(val, regs + NFI_CNFG); -+ -+ /* randomizer seed and type setup */ -+ val = ss_randomizer_seed[row % SS_SEED_NUM] & RAN_SEED_MASK; -+ val <<= RAND_SEED_SHIFT(op); -+ val |= RAND_EN(op); -+ writel(val, regs + NFI_RANDOM_CNFG); -+ -+ return 0; -+} -+ -+static int nfi_disable_randomizer(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ -+ writel(0, nb->res.nfi_regs + NFI_RANDOM_CNFG); -+ -+ return 0; -+} -+ -+static int nfi_irq_handler(int irq, void *data) -+{ -+ struct nfi_base *nb = (struct nfi_base *) data; -+ void *regs = nb->res.nfi_regs; -+ u16 status, en; -+ -+ status = readw(regs + NFI_INTR_STA); -+ en = readw(regs + NFI_INTR_EN); -+ -+ if (!(status & en)) -+ return NAND_IRQ_NONE; -+ -+ writew(~status & en, regs + NFI_INTR_EN); -+ -+ nandx_event_complete(nb->done); -+ -+ return NAND_IRQ_HANDLED; -+} -+ -+static int nfi_select_chip(struct nfi *nfi, int cs) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ -+ writel(cs, nb->res.nfi_regs + NFI_CSEL); -+ -+ return 0; -+} -+ -+static inline void set_op_mode(void *regs, u32 mode) -+{ -+ u32 val = readl(regs + NFI_CNFG); -+ -+ val &= ~CNFG_OP_MODE_MASK; -+ val |= mode; -+ -+ writel(val, regs + NFI_CNFG); -+} -+ -+static int nfi_reset(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ int ret, val; -+ -+ /* The NFI reset to reset all registers and force the NFI -+ * master be early terminated -+ */ -+ writel(CON_FIFO_FLUSH | CON_NFI_RST, regs + NFI_CON); -+ -+ /* check state of NFI internal FSM and NAND interface FSM */ -+ ret = readl_poll_timeout_atomic(regs + NFI_MASTER_STA, val, -+ !(val & MASTER_BUS_BUSY), -+ 10, NFI_TIMEOUT); -+ if (ret) -+ pr_info("nfi reset timeout...\n"); -+ -+ writel(CON_FIFO_FLUSH | CON_NFI_RST, regs + NFI_CON); -+ writew(STAR_DE, regs + NFI_STRDATA); -+ -+ return ret; -+} -+ -+static void bad_mark_swap(struct nfi *nfi, u8 *buf, u8 *fdm) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ u32 start_sector = div_down(nb->col, nfi->sector_size); -+ u32 data_mark_pos; -+ u8 temp; -+ -+ /* raw access, no need to do swap. */ -+ if (!nb->ecc_en) -+ return; -+ -+ if (!buf || !fdm) -+ return; -+ -+ if (nb->bad_mark_ctrl.sector < start_sector || -+ nb->bad_mark_ctrl.sector > start_sector + nb->rw_sectors) -+ return; -+ -+ data_mark_pos = nb->bad_mark_ctrl.position + -+ (nb->bad_mark_ctrl.sector - start_sector) * -+ nfi->sector_size; -+ -+ temp = *fdm; -+ *fdm = *(buf + data_mark_pos); -+ *(buf + data_mark_pos) = temp; -+} -+ -+static u8 *fdm_shift(struct nfi *nfi, u8 *fdm, int sector) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ u8 *pos; -+ -+ if (!fdm) -+ return NULL; -+ -+ /* map the sector's FDM data to free oob: -+ * the beginning of the oob area stores the FDM data of bad mark sectors -+ */ -+ if (sector < nb->bad_mark_ctrl.sector) -+ pos = fdm + (sector + 1) * nfi->fdm_size; -+ else if (sector == nb->bad_mark_ctrl.sector) -+ pos = fdm; -+ else -+ pos = fdm + sector * nfi->fdm_size; -+ -+ return pos; -+ -+} -+ -+static void set_bad_mark_ctrl(struct nfi_base *nb) -+{ -+ int temp, page_size = nb->format.page_size; -+ -+ nb->bad_mark_ctrl.bad_mark_swap = bad_mark_swap; -+ nb->bad_mark_ctrl.fdm_shift = fdm_shift; -+ -+ temp = nb->nfi.sector_size + nb->nfi.sector_spare_size; -+ nb->bad_mark_ctrl.sector = div_down(page_size, temp); -+ nb->bad_mark_ctrl.position = reminder(page_size, temp); -+} -+ -+/* NOTE: check if page_size valid future */ -+static int setup_format(struct nfi_base *nb, int spare_idx) -+{ -+ struct nfi *nfi = &nb->nfi; -+ u32 page_size = nb->format.page_size; -+ u32 val; -+ -+ switch (page_size) { -+ case 512: -+ val = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512; -+ break; -+ -+ case KB(2): -+ if (nfi->sector_size == 512) -+ val = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512; -+ else -+ val = PAGEFMT_512_2K; -+ -+ break; -+ -+ case KB(4): -+ if (nfi->sector_size == 512) -+ val = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512; -+ else -+ val = PAGEFMT_2K_4K; -+ -+ break; -+ -+ case KB(8): -+ if (nfi->sector_size == 512) -+ val = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512; -+ else -+ val = PAGEFMT_4K_8K; -+ -+ break; -+ -+ case KB(16): -+ val = PAGEFMT_8K_16K; -+ break; -+ -+ default: -+ pr_info("invalid page len: %d\n", page_size); -+ return -EINVAL; -+ } -+ -+ val |= spare_idx << PAGEFMT_SPARE_SHIFT; -+ val |= nfi->fdm_size << PAGEFMT_FDM_SHIFT; -+ val |= nfi->fdm_ecc_size << PAGEFMT_FDM_ECC_SHIFT; -+ writel(val, nb->res.nfi_regs + NFI_PAGEFMT); -+ -+ if (nb->custom_sector_en) { -+ val = nfi->sector_spare_size + nfi->sector_size; -+ val |= SECCUS_SIZE_EN; -+ writel(val, nb->res.nfi_regs + NFI_SECCUS_SIZE); -+ } -+ -+ return 0; -+} -+ -+static int adjust_spare(struct nfi_base *nb, int *spare) -+{ -+ int multi = nb->nfi.sector_size == 512 ? 1 : 2; -+ int i, count = nb->caps->spare_size_num; -+ -+ if (*spare >= nb->caps->spare_size[count - 1] * multi) { -+ *spare = nb->caps->spare_size[count - 1] * multi; -+ return count - 1; -+ } -+ -+ if (*spare < nb->caps->spare_size[0] * multi) -+ return -EINVAL; -+ -+ for (i = 1; i < count; i++) { -+ if (*spare < nb->caps->spare_size[i] * multi) { -+ *spare = nb->caps->spare_size[i - 1] * multi; -+ return i - 1; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+static int nfi_set_format(struct nfi *nfi, struct nfi_format *format) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfiecc *ecc = nb->ecc; -+ int ecc_strength = format->ecc_req; -+ int min_fdm, min_ecc, max_ecc; -+ u32 temp, page_sectors; -+ int spare_idx = 0; -+ -+ if (!nb->buf) { -+#if NANDX_BULK_IO_USE_DRAM -+ nb->buf = NANDX_NFI_BUF_ADDR; -+#else -+ nb->buf = mem_alloc(1, format->page_size + format->spare_size); -+#endif -+ if (!nb->buf) -+ return -ENOMEM; -+ } -+ -+ nb->format = *format; -+ -+ /* ToBeFixed: for spi nand, now sector size is 512, -+ * it should be same with slc. -+ */ -+ nfi->sector_size = 512; -+ /* format->ecc_req is the requirement per 1KB */ -+ ecc_strength >>= 1; -+ -+ page_sectors = div_down(format->page_size, nfi->sector_size); -+ nfi->sector_spare_size = div_down(format->spare_size, page_sectors); -+ -+ if (!nb->custom_sector_en) { -+ spare_idx = adjust_spare(nb, &nfi->sector_spare_size); -+ if (spare_idx < 0) -+ return -EINVAL; -+ } -+ -+ /* calculate ecc strength and fdm size */ -+ temp = (nfi->sector_spare_size - nb->caps->max_fdm_size) * 8; -+ min_ecc = div_down(temp, nb->caps->ecc_parity_bits); -+ min_ecc = ecc->adjust_strength(ecc, min_ecc); -+ if (min_ecc < 0) -+ return -EINVAL; -+ -+ temp = div_up(nb->res.min_oob_req, page_sectors); -+ temp = (nfi->sector_spare_size - temp) * 8; -+ max_ecc = div_down(temp, nb->caps->ecc_parity_bits); -+ max_ecc = ecc->adjust_strength(ecc, max_ecc); -+ if (max_ecc < 0) -+ return -EINVAL; -+ -+ temp = div_up(temp * nb->caps->ecc_parity_bits, 8); -+ temp = nfi->sector_spare_size - temp; -+ min_fdm = min_t(u32, temp, (u32)nb->caps->max_fdm_size); -+ -+ if (ecc_strength > max_ecc) { -+ pr_info("required ecc strength %d, max supported %d\n", -+ ecc_strength, max_ecc); -+ nfi->ecc_strength = max_ecc; -+ nfi->fdm_size = min_fdm; -+ } else if (format->ecc_req < min_ecc) { -+ nfi->ecc_strength = min_ecc; -+ nfi->fdm_size = nb->caps->max_fdm_size; -+ } else { -+ ecc_strength = ecc->adjust_strength(ecc, ecc_strength); -+ if (ecc_strength < 0) -+ return -EINVAL; -+ -+ nfi->ecc_strength = ecc_strength; -+ temp = div_up(ecc_strength * nb->caps->ecc_parity_bits, 8); -+ nfi->fdm_size = nfi->sector_spare_size - temp; -+ } -+ -+ nb->page_sectors = div_down(format->page_size, nfi->sector_size); -+ -+ /* some IC has fixed fdm_ecc_size, if not assigend, set to fdm_size */ -+ nfi->fdm_ecc_size = nb->caps->fdm_ecc_size ? : nfi->fdm_size; -+ -+ nfi->ecc_parity_size = div_up(nfi->ecc_strength * -+ nb->caps->ecc_parity_bits, -+ 8); -+ set_bad_mark_ctrl(nb); -+ -+ pr_debug("sector_size: %d\n", nfi->sector_size); -+ pr_debug("sector_spare_size: %d\n", nfi->sector_spare_size); -+ pr_debug("fdm_size: %d\n", nfi->fdm_size); -+ pr_debug("fdm_ecc_size: %d\n", nfi->fdm_ecc_size); -+ pr_debug("ecc_strength: %d\n", nfi->ecc_strength); -+ pr_debug("ecc_parity_size: %d\n", nfi->ecc_parity_size); -+ -+ return setup_format(nb, spare_idx); -+} -+ -+static int nfi_ctrl(struct nfi *nfi, int cmd, void *args) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ int ret = 0; -+ -+ switch (cmd) { -+ case NFI_CTRL_DMA: -+ nb->dma_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_AUTOFORMAT: -+ nb->auto_format = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_NFI_IRQ: -+ nb->nfi_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_PAGE_IRQ: -+ nb->page_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_BAD_MARK_SWAP: -+ nb->bad_mark_swap_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC: -+ nb->ecc_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_MODE: -+ nb->ecc_mode = *(enum nfiecc_mode *)args; -+ break; -+ -+ case NFI_CTRL_ECC_CLOCK: -+ /* NOTE: it seems that there's nothing need to do -+ * if new IC need, just add tht logic -+ */ -+ nb->ecc_clk_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_IRQ: -+ nb->ecc_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_DECODE_MODE: -+ nb->ecc_deccon = *(enum nfiecc_deccon *)args; -+ break; -+ -+ default: -+ pr_info("invalid arguments.\n"); -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ pr_debug("%s: set cmd(%d) to %d\n", __func__, cmd, *(int *)args); -+ return ret; -+} -+ -+static int nfi_send_cmd(struct nfi *nfi, short cmd) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ pr_debug("%s: cmd 0x%x\n", __func__, cmd); -+ -+ if (cmd < 0) -+ return -EINVAL; -+ -+ set_op_mode(regs, nb->op_mode); -+ -+ writel(cmd, regs + NFI_CMD); -+ -+ ret = readl_poll_timeout_atomic(regs + NFI_STA, -+ val, !(val & STA_CMD), -+ 5, NFI_TIMEOUT); -+ if (ret) -+ pr_info("send cmd 0x%x timeout\n", cmd); -+ -+ return ret; -+} -+ -+static int nfi_send_addr(struct nfi *nfi, int col, int row, -+ int col_cycle, int row_cycle) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ pr_debug("%s: col 0x%x, row 0x%x, col_cycle 0x%x, row_cycle 0x%x\n", -+ __func__, col, row, col_cycle, row_cycle); -+ -+ nb->col = col; -+ nb->row = row; -+ -+ writel(col, regs + NFI_COLADDR); -+ writel(row, regs + NFI_ROWADDR); -+ writel(col_cycle | (row_cycle << ROW_SHIFT), regs + NFI_ADDRNOB); -+ -+ ret = readl_poll_timeout_atomic(regs + NFI_STA, -+ val, !(val & STA_ADDR), -+ 5, NFI_TIMEOUT); -+ if (ret) -+ pr_info("send address timeout\n"); -+ -+ return ret; -+} -+ -+static int nfi_trigger(struct nfi *nfi) -+{ -+ /* Nothing need to do. */ -+ return 0; -+} -+ -+static inline int wait_io_ready(void *regs) -+{ -+ u32 val; -+ int ret; -+ -+ ret = readl_poll_timeout_atomic(regs + NFI_PIO_DIRDY, -+ val, val & PIO_DI_RDY, -+ 2, NFI_TIMEOUT); -+ if (ret) -+ pr_info("wait io ready timeout\n"); -+ -+ return ret; -+} -+ -+static int wait_ready_irq(struct nfi_base *nb, u32 timeout) -+{ -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ writel(0xf1, regs + NFI_CNRNB); -+ nandx_event_init(nb->done); -+ -+ writel(INTR_BUSY_RETURN_EN, (void *)(regs + NFI_INTR_EN)); -+ -+ /** -+ * check if nand already bean ready, -+ * avoid issue that casued by missing irq-event. -+ */ -+ val = readl(regs + NFI_STA); -+ if (val & STA_BUSY2READY) { -+ readl(regs + NFI_INTR_STA); -+ writel(0, (void *)(regs + NFI_INTR_EN)); -+ return 0; -+ } -+ -+ ret = nandx_event_wait_complete(nb->done, timeout); -+ -+ writew(0, regs + NFI_CNRNB); -+ return ret; -+} -+ -+static void wait_ready_twhr2(struct nfi_base *nb, u32 timeout) -+{ -+ /* NOTE: this for tlc */ -+} -+ -+static int wait_ready_poll(struct nfi_base *nb, u32 timeout) -+{ -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ writel(0x21, regs + NFI_CNRNB); -+ ret = readl_poll_timeout_atomic(regs + NFI_STA, val, -+ val & STA_BUSY2READY, -+ 2, timeout); -+ writew(0, regs + NFI_CNRNB); -+ -+ return ret; -+} -+ -+static int nfi_wait_ready(struct nfi *nfi, int type, u32 timeout) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ int ret; -+ -+ switch (type) { -+ case NAND_WAIT_IRQ: -+ if (nb->nfi_irq_en) -+ ret = wait_ready_irq(nb, timeout); -+ else -+ ret = -EINVAL; -+ -+ break; -+ -+ case NAND_WAIT_POLLING: -+ ret = wait_ready_poll(nb, timeout); -+ break; -+ -+ case NAND_WAIT_TWHR2: -+ wait_ready_twhr2(nb, timeout); -+ ret = 0; -+ break; -+ -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (ret) -+ pr_info("%s: type 0x%x, timeout 0x%x\n", -+ __func__, type, timeout); -+ -+ return ret; -+} -+ -+static int enable_ecc_decode(struct nfi_base *nb, int sectors) -+{ -+ struct nfi *nfi = &nb->nfi; -+ struct nfiecc *ecc = nb->ecc; -+ -+ ecc->config.op = ECC_DECODE; -+ ecc->config.mode = nb->ecc_mode; -+ ecc->config.deccon = nb->ecc_deccon; -+ ecc->config.sectors = sectors; -+ ecc->config.len = nfi->sector_size + nfi->fdm_ecc_size; -+ ecc->config.strength = nfi->ecc_strength; -+ -+ return ecc->enable(ecc); -+} -+ -+static int enable_ecc_encode(struct nfi_base *nb) -+{ -+ struct nfiecc *ecc = nb->ecc; -+ struct nfi *nfi = &nb->nfi; -+ -+ ecc->config.op = ECC_ENCODE; -+ ecc->config.mode = nb->ecc_mode; -+ ecc->config.len = nfi->sector_size + nfi->fdm_ecc_size; -+ ecc->config.strength = nfi->ecc_strength; -+ -+ return ecc->enable(ecc); -+} -+ -+static void read_fdm(struct nfi_base *nb, u8 *fdm, int start_sector, -+ int sectors) -+{ -+ void *regs = nb->res.nfi_regs; -+ int j, i = start_sector; -+ u32 vall, valm; -+ u8 *buf = fdm; -+ -+ for (; i < start_sector + sectors; i++) { -+ if (nb->bad_mark_swap_en) -+ buf = nb->bad_mark_ctrl.fdm_shift(&nb->nfi, fdm, i); -+ -+ vall = readl(regs + NFI_FDML(i)); -+ valm = readl(regs + NFI_FDMM(i)); -+ -+ for (j = 0; j < nb->nfi.fdm_size; j++) -+ *buf++ = (j >= 4 ? valm : vall) >> ((j & 3) << 3); -+ } -+} -+ -+static void write_fdm(struct nfi_base *nb, u8 *fdm) -+{ -+ struct nfi *nfi = &nb->nfi; -+ void *regs = nb->res.nfi_regs; -+ u32 vall, valm; -+ int i, j; -+ u8 *buf = fdm; -+ -+ for (i = 0; i < nb->page_sectors; i++) { -+ if (nb->bad_mark_swap_en) -+ buf = nb->bad_mark_ctrl.fdm_shift(nfi, fdm, i); -+ -+ vall = 0; -+ for (j = 0; j < 4; j++) -+ vall |= (j < nfi->fdm_size ? *buf++ : 0xff) << (j * 8); -+ writel(vall, regs + NFI_FDML(i)); -+ -+ valm = 0; -+ for (j = 0; j < 4; j++) -+ valm |= (j < nfi->fdm_size ? *buf++ : 0xff) << (j * 8); -+ writel(valm, regs + NFI_FDMM(i)); -+ } -+} -+ -+/* NOTE: pio not use auto format */ -+static int pio_rx_data(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ struct nfiecc_status ecc_status; -+ struct nfi *nfi = &nb->nfi; -+ void *regs = nb->res.nfi_regs; -+ u32 val, bitflips = 0; -+ int len, ret, i; -+ u8 *buf; -+ -+ val = readl(regs + NFI_CNFG) | CNFG_BYTE_RW; -+ writel(val, regs + NFI_CNFG); -+ -+ len = nfi->sector_size + nfi->sector_spare_size; -+ len *= sectors; -+ -+ for (i = 0; i < len; i++) { -+ ret = wait_io_ready(regs); -+ if (ret) -+ return ret; -+ -+ nb->buf[i] = readb(regs + NFI_DATAR); -+ } -+ -+ /* TODO: do error handle for autoformat setting of pio */ -+ if (nb->ecc_en) { -+ for (i = 0; i < sectors; i++) { -+ buf = nb->buf + i * (nfi->sector_size + -+ nfi->sector_spare_size); -+ ret = nb->ecc->correct_data(nb->ecc, &ecc_status, -+ buf, i); -+ if (data) -+ memcpy(data + i * nfi->sector_size, -+ buf, nfi->sector_size); -+ if (fdm) -+ memcpy(fdm + i * nfi->fdm_size, -+ buf + nfi->sector_size, nfi->fdm_size); -+ if (ret) { -+ ret = nb->ecc->decode_status(nb->ecc, i, 1); -+ if (ret < 0) -+ return ret; -+ -+ bitflips = max_t(int, (int)bitflips, ret); -+ } -+ } -+ -+ return bitflips; -+ } -+ -+ /* raw read, only data not null, and its length should be $len */ -+ if (data) -+ memcpy(data, nb->buf, len); -+ -+ return 0; -+} -+ -+static int pio_tx_data(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ struct nfi *nfi = &nb->nfi; -+ void *regs = nb->res.nfi_regs; -+ u32 i, val; -+ int len, ret; -+ -+ val = readw(regs + NFI_CNFG) | CNFG_BYTE_RW; -+ writew(val, regs + NFI_CNFG); -+ -+ len = nb->ecc_en ? nfi->sector_size : -+ nfi->sector_size + nfi->sector_spare_size; -+ len *= sectors; -+ -+ /* data shouldn't null, -+ * and if ecc enable ,fdm been written in prepare process -+ */ -+ for (i = 0; i < len; i++) { -+ ret = wait_io_ready(regs); -+ if (ret) -+ return ret; -+ writeb(data[i], regs + NFI_DATAW); -+ } -+ -+ return 0; -+} -+ -+static bool is_page_empty(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ u32 empty = readl(nb->res.nfi_regs + NFI_STA) & STA_EMP_PAGE; -+ -+ if (empty) { -+ pr_info("empty page!\n"); -+ return true; -+ } -+ -+ return false; -+} -+ -+static int rw_prepare(struct nfi_base *nb, int sectors, u8 *data, -+ u8 *fdm, bool read) -+{ -+ void *regs = nb->res.nfi_regs; -+ u32 len = nb->nfi.sector_size * sectors; -+ bool irq_en = nb->dma_en && nb->nfi_irq_en; -+ void *dma_addr; -+ u32 val; -+ int ret; -+ -+ nb->rw_sectors = sectors; -+ -+ if (irq_en) { -+ nandx_event_init(nb->done); -+ writel(INTR_AHB_DONE_EN, regs + NFI_INTR_EN); -+ } -+ -+ val = readw(regs + NFI_CNFG); -+ if (read) -+ val |= CNFG_READ_EN; -+ else -+ val &= ~CNFG_READ_EN; -+ -+ /* as design, now, auto format enabled when ecc enabled */ -+ if (nb->ecc_en) { -+ val |= CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; -+ -+ if (read) -+ ret = enable_ecc_decode(nb, sectors); -+ else -+ ret = enable_ecc_encode(nb); -+ -+ if (ret) { -+ pr_info("%s: ecc enable %s fail!\n", __func__, -+ read ? "decode" : "encode"); -+ return ret; -+ } -+ } else { -+ val &= ~(CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN); -+ } -+ -+ if (!read && nb->bad_mark_swap_en) -+ nb->bad_mark_ctrl.bad_mark_swap(&nb->nfi, data, fdm); -+ -+ if (!nb->ecc_en && read) -+ len += sectors * nb->nfi.sector_spare_size; -+ -+ if (nb->dma_en) { -+ val |= CNFG_DMA_BURST_EN | CNFG_AHB; -+ -+ if (read) { -+ dma_addr = (void *)(unsigned long)nandx_dma_map( -+ nb->res.dev, nb->buf, -+ (u64)len, NDMA_FROM_DEV); -+ } else { -+ memcpy(nb->buf, data, len); -+ dma_addr = (void *)(unsigned long)nandx_dma_map( -+ nb->res.dev, nb->buf, -+ (u64)len, NDMA_TO_DEV); -+ } -+ -+ writel((unsigned long)dma_addr, (void *)regs + NFI_STRADDR); -+ -+ nb->access_len = len; -+ nb->dma_addr = dma_addr; -+ } -+ -+ if (nb->ecc_en && !read && fdm) -+ write_fdm(nb, fdm); -+ -+ writew(val, regs + NFI_CNFG); -+ /* setup R/W sector number */ -+ writel(sectors << CON_SEC_SHIFT, regs + NFI_CON); -+ -+ return 0; -+} -+ -+static void rw_trigger(struct nfi_base *nb, bool read) -+{ -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ val = read ? CON_BRD : CON_BWR; -+ val |= readl(regs + NFI_CON); -+ writel(val, regs + NFI_CON); -+ -+ writel(STAR_EN, regs + NFI_STRDATA); -+} -+ -+static int rw_wait_done(struct nfi_base *nb, int sectors, bool read) -+{ -+ void *regs = nb->res.nfi_regs; -+ bool irq_en = nb->dma_en && nb->nfi_irq_en; -+ int ret; -+ u32 val; -+ -+ if (irq_en) { -+ ret = nandx_event_wait_complete(nb->done, NFI_TIMEOUT); -+ if (!ret) { -+ writew(0, regs + NFI_INTR_EN); -+ return ret; -+ } -+ } -+ -+ if (read) { -+ ret = readl_poll_timeout_atomic(regs + NFI_BYTELEN, val, -+ ADDRCNTR_SEC(val) >= -+ (u32)sectors, -+ 2, NFI_TIMEOUT); -+ /* HW issue: if not wait ahb done, need polling bus busy */ -+ if (!ret && !irq_en) -+ ret = readl_poll_timeout_atomic(regs + NFI_MASTER_STA, -+ val, -+ !(val & -+ MASTER_BUS_BUSY), -+ 2, NFI_TIMEOUT); -+ } else { -+ ret = readl_poll_timeout_atomic(regs + NFI_ADDRCNTR, val, -+ ADDRCNTR_SEC(val) >= -+ (u32)sectors, -+ 2, NFI_TIMEOUT); -+ } -+ -+ if (ret) { -+ pr_info("do page %s timeout\n", read ? "read" : "write"); -+ return ret; -+ } -+ -+ if (read && nb->ecc_en) { -+ ret = nb->ecc->wait_done(nb->ecc); -+ if (ret) -+ return ret; -+ -+ return nb->ecc->decode_status(nb->ecc, 0, sectors); -+ } -+ -+ return 0; -+} -+ -+static int rw_data(struct nfi_base *nb, u8 *data, u8 *fdm, int sectors, -+ bool read) -+{ -+ if (read && nb->dma_en && nb->ecc_en && fdm) -+ read_fdm(nb, fdm, 0, sectors); -+ -+ if (!nb->dma_en) { -+ if (read) -+ return pio_rx_data(nb, data, fdm, sectors); -+ -+ return pio_tx_data(nb, data, fdm, sectors); -+ } -+ -+ return 0; -+} -+ -+static void rw_complete(struct nfi_base *nb, u8 *data, u8 *fdm, -+ bool read) -+{ -+ int data_len = 0; -+ bool is_empty; -+ -+ if (nb->dma_en) { -+ if (read) { -+ nandx_dma_unmap(nb->res.dev, nb->buf, nb->dma_addr, -+ (u64)nb->access_len, NDMA_FROM_DEV); -+ -+ if (data) { -+ data_len = nb->rw_sectors * nb->nfi.sector_size; -+ memcpy(data, nb->buf, data_len); -+ } -+ -+ if (fdm) -+ memcpy(fdm, nb->buf + data_len, -+ nb->access_len - data_len); -+ -+ if (nb->read_status == -ENANDREAD) { -+ is_empty = nb->is_page_empty(nb, data, fdm, -+ nb->rw_sectors); -+ if (is_empty) -+ nb->read_status = 0; -+ } -+ } else { -+ nandx_dma_unmap(nb->res.dev, nb->buf, nb->dma_addr, -+ (u64)nb->access_len, NDMA_TO_DEV); -+ } -+ } -+ -+ /* whether it's reading or writing, we all check if nee swap -+ * for write, we need to restore data -+ */ -+ if (nb->bad_mark_swap_en) -+ nb->bad_mark_ctrl.bad_mark_swap(&nb->nfi, data, fdm); -+ -+ if (nb->ecc_en) -+ nb->ecc->disable(nb->ecc); -+ -+ writel(0, nb->res.nfi_regs + NFI_CNFG); -+ writel(0, nb->res.nfi_regs + NFI_CON); -+} -+ -+static int nfi_read_sectors(struct nfi *nfi, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ int bitflips = 0, ret; -+ -+ pr_debug("%s: read page#%d\n", __func__, nb->row); -+ pr_debug("%s: data address 0x%x, fdm address 0x%x, sectors 0x%x\n", -+ __func__, (u32)((unsigned long)data), -+ (u32)((unsigned long)fdm), sectors); -+ -+ nb->read_status = 0; -+ -+ ret = nb->rw_prepare(nb, sectors, data, fdm, true); -+ if (ret) -+ return ret; -+ -+ nb->rw_trigger(nb, true); -+ -+ if (nb->dma_en) { -+ ret = nb->rw_wait_done(nb, sectors, true); -+ if (ret > 0) -+ bitflips = ret; -+ else if (ret == -ENANDREAD) -+ nb->read_status = -ENANDREAD; -+ else if (ret < 0) -+ goto complete; -+ -+ } -+ -+ ret = nb->rw_data(nb, data, fdm, sectors, true); -+ if (ret > 0) -+ ret = max_t(int, ret, bitflips); -+ -+complete: -+ nb->rw_complete(nb, data, fdm, true); -+ -+ if (nb->read_status == -ENANDREAD) -+ return -ENANDREAD; -+ -+ return ret; -+} -+ -+int nfi_write_page(struct nfi *nfi, u8 *data, u8 *fdm) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ u32 sectors = div_down(nb->format.page_size, nfi->sector_size); -+ int ret; -+ -+ pr_debug("%s: data address 0x%x, fdm address 0x%x\n", -+ __func__, (int)((unsigned long)data), -+ (int)((unsigned long)fdm)); -+ -+ ret = nb->rw_prepare(nb, sectors, data, fdm, false); -+ if (ret) -+ return ret; -+ -+ nb->rw_trigger(nb, false); -+ -+ ret = nb->rw_data(nb, data, fdm, sectors, false); -+ if (ret) -+ return ret; -+ -+ ret = nb->rw_wait_done(nb, sectors, false); -+ -+ nb->rw_complete(nb, data, fdm, false); -+ -+ return ret; -+} -+ -+static int nfi_rw_bytes(struct nfi *nfi, u8 *data, int count, bool read) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ int i, ret; -+ u32 val; -+ -+ for (i = 0; i < count; i++) { -+ val = readl(regs + NFI_STA) & NFI_FSM_MASK; -+ if (val != NFI_FSM_CUSTDATA) { -+ val = readw(regs + NFI_CNFG) | CNFG_BYTE_RW; -+ if (read) -+ val |= CNFG_READ_EN; -+ writew(val, regs + NFI_CNFG); -+ -+ val = div_up(count, nfi->sector_size); -+ val = (val << CON_SEC_SHIFT) | CON_BRD | CON_BWR; -+ writel(val, regs + NFI_CON); -+ -+ writew(STAR_EN, regs + NFI_STRDATA); -+ } -+ -+ ret = wait_io_ready(regs); -+ if (ret) -+ return ret; -+ -+ if (read) -+ data[i] = readb(regs + NFI_DATAR); -+ else -+ writeb(data[i], regs + NFI_DATAW); -+ } -+ -+ writel(0, nb->res.nfi_regs + NFI_CNFG); -+ -+ return 0; -+} -+ -+static int nfi_read_bytes(struct nfi *nfi, u8 *data, int count) -+{ -+ return nfi_rw_bytes(nfi, data, count, true); -+} -+ -+static int nfi_write_bytes(struct nfi *nfi, u8 *data, int count) -+{ -+ return nfi_rw_bytes(nfi, data, count, false); -+} -+ -+/* As register map says, only when flash macro is idle, -+ * sw reset or nand interface change can be issued -+ */ -+static inline int wait_flash_macro_idle(void *regs) -+{ -+ u32 val; -+ -+ return readl_poll_timeout_atomic(regs + NFI_STA, val, -+ val & FLASH_MACRO_IDLE, 2, -+ NFI_TIMEOUT); -+} -+ -+#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \ -+ ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \ -+ (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt)) -+ -+static int nfi_set_sdr_timing(struct nfi *nfi, void *timing, u8 type) -+{ -+ struct nand_sdr_timing *sdr = (struct nand_sdr_timing *) timing; -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ u32 tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt, tstrobe; -+ u32 rate, val; -+ int ret; -+ -+ ret = wait_flash_macro_idle(regs); -+ if (ret) -+ return ret; -+ -+ /* turn clock rate into KHZ */ -+ rate = nb->res.clock_1x / 1000; -+ -+ tpoecs = max_t(u16, sdr->tALH, sdr->tCLH); -+ tpoecs = div_up(tpoecs * rate, 1000000); -+ tpoecs &= 0xf; -+ -+ tprecs = max_t(u16, sdr->tCLS, sdr->tALS); -+ tprecs = div_up(tprecs * rate, 1000000); -+ tprecs &= 0x3f; -+ -+ /* tc2r is in unit of 2T */ -+ tc2r = div_up(sdr->tCR * rate, 1000000); -+ tc2r = div_down(tc2r, 2); -+ tc2r &= 0x3f; -+ -+ tw2r = div_up(sdr->tWHR * rate, 1000000); -+ tw2r = div_down(tw2r, 2); -+ tw2r &= 0xf; -+ -+ twh = max_t(u16, sdr->tREH, sdr->tWH); -+ twh = div_up(twh * rate, 1000000) - 1; -+ twh &= 0xf; -+ -+ twst = div_up(sdr->tWP * rate, 1000000) - 1; -+ twst &= 0xf; -+ -+ trlt = div_up(sdr->tRP * rate, 1000000) - 1; -+ trlt &= 0xf; -+ -+ /* If tREA is bigger than tRP, setup strobe sel here */ -+ if ((trlt + 1) * 1000000 / rate < sdr->tREA) { -+ tstrobe = sdr->tREA - (trlt + 1) * 1000000 / rate; -+ tstrobe = div_up(tstrobe * rate, 1000000); -+ val = readl(regs + NFI_DEBUG_CON1); -+ val &= ~STROBE_MASK; -+ val |= tstrobe << STROBE_SHIFT; -+ writel(val, regs + NFI_DEBUG_CON1); -+ } -+ -+ /* -+ * ACCON: access timing control register -+ * ------------------------------------- -+ * 31:28: tpoecs, minimum required time for CS post pulling down after -+ * accessing the device -+ * 27:22: tprecs, minimum required time for CS pre pulling down before -+ * accessing the device -+ * 21:16: tc2r, minimum required time from NCEB low to NREB low -+ * 15:12: tw2r, minimum required time from NWEB high to NREB low. -+ * 11:08: twh, write enable hold time -+ * 07:04: twst, write wait states -+ * 03:00: trlt, read wait states -+ */ -+ val = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt); -+ pr_info("acctiming: 0x%x\n", val); -+ writel(val, regs + NFI_ACCCON); -+ -+ /* set NAND type */ -+ writel(NAND_TYPE_ASYNC, regs + NFI_NAND_TYPE_CNFG); -+ -+ return ret; -+} -+ -+static int nfi_set_timing(struct nfi *nfi, void *timing, int type) -+{ -+ switch (type) { -+ case NAND_TIMING_SDR: -+ return nfi_set_sdr_timing(nfi, timing, type); -+ -+ /* NOTE: for mlc/tlc */ -+ case NAND_TIMING_SYNC_DDR: -+ case NAND_TIMING_TOGGLE_DDR: -+ case NAND_TIMING_NVDDR2: -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static void set_nfi_funcs(struct nfi *nfi) -+{ -+ nfi->select_chip = nfi_select_chip; -+ nfi->set_format = nfi_set_format; -+ nfi->nfi_ctrl = nfi_ctrl; -+ nfi->set_timing = nfi_set_timing; -+ -+ nfi->reset = nfi_reset; -+ nfi->send_cmd = nfi_send_cmd; -+ nfi->send_addr = nfi_send_addr; -+ nfi->trigger = nfi_trigger; -+ -+ nfi->write_page = nfi_write_page; -+ nfi->write_bytes = nfi_write_bytes; -+ nfi->read_sectors = nfi_read_sectors; -+ nfi->read_bytes = nfi_read_bytes; -+ -+ nfi->wait_ready = nfi_wait_ready; -+ -+ nfi->enable_randomizer = nfi_enable_randomizer; -+ nfi->disable_randomizer = nfi_disable_randomizer; -+} -+ -+static struct nfi_caps nfi_caps_mt7622 = { -+ .max_fdm_size = 8, -+ .fdm_ecc_size = 1, -+ .ecc_parity_bits = 13, -+ .spare_size = spare_size_mt7622, -+ .spare_size_num = 4, -+}; -+ -+static struct nfi_caps *nfi_get_match_data(enum mtk_ic_version ic) -+{ -+ /* NOTE: add other IC's data */ -+ return &nfi_caps_mt7622; -+} -+ -+static void set_nfi_base_params(struct nfi_base *nb) -+{ -+ nb->ecc_en = false; -+ nb->dma_en = false; -+ nb->nfi_irq_en = false; -+ nb->ecc_irq_en = false; -+ nb->page_irq_en = false; -+ nb->ecc_clk_en = false; -+ nb->randomize_en = false; -+ nb->custom_sector_en = false; -+ nb->bad_mark_swap_en = false; -+ -+ nb->op_mode = CNFG_CUSTOM_MODE; -+ nb->ecc_deccon = ECC_DEC_CORRECT; -+ nb->ecc_mode = ECC_NFI_MODE; -+ -+ nb->done = nandx_event_create(); -+ nb->caps = nfi_get_match_data(nb->res.ic_ver); -+ -+ nb->set_op_mode = set_op_mode; -+ nb->is_page_empty = is_page_empty; -+ -+ nb->rw_prepare = rw_prepare; -+ nb->rw_trigger = rw_trigger; -+ nb->rw_wait_done = rw_wait_done; -+ nb->rw_data = rw_data; -+ nb->rw_complete = rw_complete; -+} -+ -+struct nfi *__weak nfi_extend_init(struct nfi_base *nb) -+{ -+ return &nb->nfi; -+} -+ -+void __weak nfi_extend_exit(struct nfi_base *nb) -+{ -+ mem_free(nb); -+} -+ -+struct nfi *nfi_init(struct nfi_resource *res) -+{ -+ struct nfiecc_resource ecc_res; -+ struct nfi_base *nb; -+ struct nfiecc *ecc; -+ struct nfi *nfi; -+ int ret; -+ -+ nb = mem_alloc(1, sizeof(struct nfi_base)); -+ if (!nb) { -+ pr_info("nfi alloc memory fail @%s.\n", __func__); -+ return NULL; -+ } -+ -+ nb->res = *res; -+ -+ ret = nandx_irq_register(res->dev, res->nfi_irq_id, nfi_irq_handler, -+ "mtk_nand", nb); -+ if (ret) { -+ pr_info("nfi irq register failed!\n"); -+ goto error; -+ } -+ -+ /* fill ecc paras and init ecc */ -+ ecc_res.ic_ver = nb->res.ic_ver; -+ ecc_res.dev = nb->res.dev; -+ ecc_res.irq_id = nb->res.ecc_irq_id; -+ ecc_res.regs = nb->res.ecc_regs; -+ ecc = nfiecc_init(&ecc_res); -+ if (!ecc) { -+ pr_info("nfiecc init fail.\n"); -+ return NULL; -+ } -+ -+ nb->ecc = ecc; -+ -+ set_nfi_base_params(nb); -+ set_nfi_funcs(&nb->nfi); -+ -+ /* Assign a temp sector size for reading ID & para page. -+ * We may assign new value later. -+ */ -+ nb->nfi.sector_size = 512; -+ -+ /* give a default timing, and as discuss -+ * this is the only thing what we need do for nfi init -+ * if need do more, then we can add a function -+ */ -+ writel(0x30C77FFF, nb->res.nfi_regs + NFI_ACCCON); -+ -+ nfi = nfi_extend_init(nb); -+ if (nfi) -+ return nfi; -+ -+error: -+ mem_free(nb); -+ return NULL; -+} -+ -+void nfi_exit(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ -+ nandx_event_destroy(nb->done); -+ nfiecc_exit(nb->ecc); -+#if !NANDX_BULK_IO_USE_DRAM -+ mem_free(nb->buf); -+#endif -+ nfi_extend_exit(nb); -+} -+ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_base.h -@@ -0,0 +1,95 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_BASE_H__ -+#define __NFI_BASE_H__ -+ -+#define NFI_TIMEOUT 1000000 -+ -+enum randomizer_op { -+ RAND_ENCODE, -+ RAND_DECODE -+}; -+ -+struct bad_mark_ctrl { -+ void (*bad_mark_swap)(struct nfi *nfi, u8 *buf, u8 *fdm); -+ u8 *(*fdm_shift)(struct nfi *nfi, u8 *fdm, int sector); -+ u32 sector; -+ u32 position; -+}; -+ -+struct nfi_caps { -+ u8 max_fdm_size; -+ u8 fdm_ecc_size; -+ u8 ecc_parity_bits; -+ const int *spare_size; -+ u32 spare_size_num; -+}; -+ -+struct nfi_base { -+ struct nfi nfi; -+ struct nfi_resource res; -+ struct nfiecc *ecc; -+ struct nfi_format format; -+ struct nfi_caps *caps; -+ struct bad_mark_ctrl bad_mark_ctrl; -+ -+ /* page_size + spare_size */ -+ u8 *buf; -+ -+ /* used for spi nand */ -+ u8 cmd_mode; -+ u32 op_mode; -+ -+ int page_sectors; -+ -+ void *done; -+ -+ /* for read/write */ -+ int col; -+ int row; -+ int access_len; -+ int rw_sectors; -+ void *dma_addr; -+ int read_status; -+ -+ bool dma_en; -+ bool nfi_irq_en; -+ bool page_irq_en; -+ bool auto_format; -+ bool ecc_en; -+ bool ecc_irq_en; -+ bool ecc_clk_en; -+ bool randomize_en; -+ bool custom_sector_en; -+ bool bad_mark_swap_en; -+ -+ enum nfiecc_deccon ecc_deccon; -+ enum nfiecc_mode ecc_mode; -+ -+ void (*set_op_mode)(void *regs, u32 mode); -+ bool (*is_page_empty)(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors); -+ -+ int (*rw_prepare)(struct nfi_base *nb, int sectors, u8 *data, u8 *fdm, -+ bool read); -+ void (*rw_trigger)(struct nfi_base *nb, bool read); -+ int (*rw_wait_done)(struct nfi_base *nb, int sectors, bool read); -+ int (*rw_data)(struct nfi_base *nb, u8 *data, u8 *fdm, int sectors, -+ bool read); -+ void (*rw_complete)(struct nfi_base *nb, u8 *data, u8 *fdm, bool read); -+}; -+ -+static inline struct nfi_base *nfi_to_base(struct nfi *nfi) -+{ -+ return container_of(nfi, struct nfi_base, nfi); -+} -+ -+struct nfi *nfi_extend_init(struct nfi_base *nb); -+void nfi_extend_exit(struct nfi_base *nb); -+ -+#endif /* __NFI_BASE_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_regs.h -@@ -0,0 +1,114 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_REGS_H__ -+#define __NFI_REGS_H__ -+ -+#define NFI_CNFG 0x000 -+#define CNFG_AHB BIT(0) -+#define CNFG_READ_EN BIT(1) -+#define CNFG_DMA_BURST_EN BIT(2) -+#define CNFG_RESEED_SEC_EN BIT(4) -+#define CNFG_RAND_SEL BIT(5) -+#define CNFG_BYTE_RW BIT(6) -+#define CNFG_HW_ECC_EN BIT(8) -+#define CNFG_AUTO_FMT_EN BIT(9) -+#define CNFG_RAND_MASK GENMASK(5, 4) -+#define CNFG_OP_MODE_MASK GENMASK(14, 12) -+#define CNFG_IDLE_MOD 0 -+#define CNFG_READ_MODE (1 << 12) -+#define CNFG_SINGLE_READ_MODE (2 << 12) -+#define CNFG_PROGRAM_MODE (3 << 12) -+#define CNFG_ERASE_MODE (4 << 12) -+#define CNFG_RESET_MODE (5 << 12) -+#define CNFG_CUSTOM_MODE (6 << 12) -+#define NFI_PAGEFMT 0x004 -+#define PAGEFMT_SPARE_SHIFT 4 -+#define PAGEFMT_FDM_ECC_SHIFT 12 -+#define PAGEFMT_FDM_SHIFT 8 -+#define PAGEFMT_SEC_SEL_512 BIT(2) -+#define PAGEFMT_512_2K 0 -+#define PAGEFMT_2K_4K 1 -+#define PAGEFMT_4K_8K 2 -+#define PAGEFMT_8K_16K 3 -+#define NFI_CON 0x008 -+#define CON_FIFO_FLUSH BIT(0) -+#define CON_NFI_RST BIT(1) -+#define CON_BRD BIT(8) -+#define CON_BWR BIT(9) -+#define CON_SEC_SHIFT 12 -+#define NFI_ACCCON 0x00c -+#define NFI_INTR_EN 0x010 -+#define INTR_BUSY_RETURN_EN BIT(4) -+#define INTR_AHB_DONE_EN BIT(6) -+#define NFI_INTR_STA 0x014 -+#define NFI_CMD 0x020 -+#define NFI_ADDRNOB 0x030 -+#define ROW_SHIFT 4 -+#define NFI_COLADDR 0x034 -+#define NFI_ROWADDR 0x038 -+#define NFI_STRDATA 0x040 -+#define STAR_EN 1 -+#define STAR_DE 0 -+#define NFI_CNRNB 0x044 -+#define NFI_DATAW 0x050 -+#define NFI_DATAR 0x054 -+#define NFI_PIO_DIRDY 0x058 -+#define PIO_DI_RDY 1 -+#define NFI_STA 0x060 -+#define STA_CMD BIT(0) -+#define STA_ADDR BIT(1) -+#define FLASH_MACRO_IDLE BIT(5) -+#define STA_BUSY BIT(8) -+#define STA_BUSY2READY BIT(9) -+#define STA_EMP_PAGE BIT(12) -+#define NFI_FSM_CUSTDATA (0xe << 16) -+#define NFI_FSM_MASK GENMASK(19, 16) -+#define NAND_FSM_MASK GENMASK(29, 23) -+#define NFI_ADDRCNTR 0x070 -+#define CNTR_VALID_MASK GENMASK(16, 0) -+#define CNTR_MASK GENMASK(15, 12) -+#define ADDRCNTR_SEC_SHIFT 12 -+#define ADDRCNTR_SEC(val) \ -+ (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT) -+#define NFI_STRADDR 0x080 -+#define NFI_BYTELEN 0x084 -+#define NFI_CSEL 0x090 -+#define NFI_FDML(x) (0x0a0 + (x) * 8) -+#define NFI_FDMM(x) (0x0a4 + (x) * 8) -+#define NFI_DEBUG_CON1 0x220 -+#define STROBE_MASK GENMASK(4, 3) -+#define STROBE_SHIFT 3 -+#define ECC_CLK_EN BIT(11) -+#define AUTOC_SRAM_MODE BIT(12) -+#define BYPASS_MASTER_EN BIT(15) -+#define NFI_MASTER_STA 0x224 -+#define MASTER_BUS_BUSY 0x3 -+#define NFI_SECCUS_SIZE 0x22c -+#define SECCUS_SIZE_EN BIT(17) -+#define NFI_RANDOM_CNFG 0x238 -+#define RAN_ENCODE_EN BIT(0) -+#define ENCODE_SEED_SHIFT 1 -+#define RAN_DECODE_EN BIT(16) -+#define DECODE_SEED_SHIFT 17 -+#define RAN_SEED_MASK 0x7fff -+#define NFI_EMPTY_THRESH 0x23c -+#define NFI_NAND_TYPE_CNFG 0x240 -+#define NAND_TYPE_ASYNC 0 -+#define NAND_TYPE_TOGGLE 1 -+#define NAND_TYPE_SYNC 2 -+#define NFI_ACCCON1 0x244 -+#define NFI_DELAY_CTRL 0x248 -+#define NFI_TLC_RD_WHR2 0x300 -+#define TLC_RD_WHR2_EN BIT(12) -+#define TLC_RD_WHR2_MASK GENMASK(11, 0) -+#define SNF_SNF_CNFG 0x55c -+#define SPI_MODE_EN 1 -+#define SPI_MODE_DIS 0 -+ -+#endif /* __NFI_REGS_H__ */ -+ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_spi.c -@@ -0,0 +1,689 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "../nfi.h" -+#include "nfiecc.h" -+#include "nfi_regs.h" -+#include "nfi_base.h" -+#include "nfi_spi_regs.h" -+#include "nfi_spi.h" -+ -+#define NFI_CMD_DUMMY_RD 0x00 -+#define NFI_CMD_DUMMY_WR 0x80 -+ -+static struct nfi_spi_delay spi_delay[SPI_NAND_MAX_DELAY] = { -+ /* -+ * tCLK_SAM_DLY, tCLK_OUT_DLY, tCS_DLY, tWR_EN_DLY, -+ * tIO_IN_DLY[4], tIO_OUT_DLY[4], tREAD_LATCH_LATENCY -+ */ -+ {0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0}, -+ {21, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0}, -+ {63, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0}, -+ {0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1}, -+ {21, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1}, -+ {63, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1} -+}; -+ -+static inline struct nfi_spi *base_to_snfi(struct nfi_base *nb) -+{ -+ return container_of(nb, struct nfi_spi, base); -+} -+ -+static void snfi_mac_enable(struct nfi_base *nb) -+{ -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ val = readl(regs + SNF_MAC_CTL); -+ val &= ~MAC_XIO_SEL; -+ val |= SF_MAC_EN; -+ -+ writel(val, regs + SNF_MAC_CTL); -+} -+ -+static void snfi_mac_disable(struct nfi_base *nb) -+{ -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ val = readl(regs + SNF_MAC_CTL); -+ val &= ~(SF_TRIG | SF_MAC_EN); -+ writel(val, regs + SNF_MAC_CTL); -+} -+ -+static int snfi_mac_trigger(struct nfi_base *nb) -+{ -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ val = readl(regs + SNF_MAC_CTL); -+ val |= SF_TRIG; -+ writel(val, regs + SNF_MAC_CTL); -+ -+ ret = readl_poll_timeout_atomic(regs + SNF_MAC_CTL, val, -+ val & WIP_READY, 10, -+ NFI_TIMEOUT); -+ if (ret) { -+ pr_info("polling wip ready for read timeout\n"); -+ return ret; -+ } -+ -+ return readl_poll_timeout_atomic(regs + SNF_MAC_CTL, val, -+ !(val & WIP), 10, -+ NFI_TIMEOUT); -+} -+ -+static int snfi_mac_op(struct nfi_base *nb) -+{ -+ int ret; -+ -+ snfi_mac_enable(nb); -+ ret = snfi_mac_trigger(nb); -+ snfi_mac_disable(nb); -+ -+ return ret; -+} -+ -+static void snfi_write_mac(struct nfi_spi *nfi_spi, u8 *data, int count) -+{ -+ struct nandx_split32 split = {0}; -+ u32 reg_offset = round_down(nfi_spi->tx_count, 4); -+ void *regs = nfi_spi->base.res.nfi_regs; -+ u32 data_offset = 0, i, val; -+ u8 *p_val = (u8 *)(&val); -+ -+ nandx_split(&split, nfi_spi->tx_count, count, val, 4); -+ -+ if (split.head_len) { -+ val = readl(regs + SPI_GPRAM_ADDR + reg_offset); -+ -+ for (i = 0; i < split.head_len; i++) -+ p_val[split.head + i] = data[i]; -+ -+ writel(val, regs + SPI_GPRAM_ADDR + reg_offset); -+ } -+ -+ if (split.body_len) { -+ reg_offset = split.body; -+ data_offset = split.head_len; -+ -+ for (i = 0; i < split.body_len; i++) { -+ p_val[i & 3] = data[data_offset + i]; -+ -+ if ((i & 3) == 3) { -+ writel(val, regs + SPI_GPRAM_ADDR + reg_offset); -+ reg_offset += 4; -+ } -+ } -+ } -+ -+ if (split.tail_len) { -+ reg_offset = split.tail; -+ data_offset += split.body_len; -+ -+ for (i = 0; i < split.tail_len; i++) { -+ p_val[i] = data[data_offset + i]; -+ -+ if (i == split.tail_len - 1) -+ writel(val, regs + SPI_GPRAM_ADDR + reg_offset); -+ } -+ } -+} -+ -+static void snfi_read_mac(struct nfi_spi *nfi_spi, u8 *data, int count) -+{ -+ void *regs = nfi_spi->base.res.nfi_regs; -+ u32 reg_offset = round_down(nfi_spi->tx_count, 4); -+ struct nandx_split32 split = {0}; -+ u32 data_offset = 0, i, val; -+ u8 *p_val = (u8 *)&val; -+ -+ nandx_split(&split, nfi_spi->tx_count, count, val, 4); -+ -+ if (split.head_len) { -+ val = readl(regs + SPI_GPRAM_ADDR + reg_offset); -+ -+ for (i = 0; i < split.head_len; i++) -+ data[data_offset + i] = p_val[split.head + i]; -+ } -+ -+ if (split.body_len) { -+ reg_offset = split.body; -+ data_offset = split.head_len; -+ -+ for (i = 0; i < split.body_len; i++) { -+ if ((i & 3) == 0) { -+ val = readl(regs + SPI_GPRAM_ADDR + reg_offset); -+ reg_offset += 4; -+ } -+ -+ data[data_offset + i] = p_val[i % 4]; -+ } -+ } -+ -+ if (split.tail_len) { -+ reg_offset = split.tail; -+ data_offset += split.body_len; -+ val = readl(regs + SPI_GPRAM_ADDR + reg_offset); -+ -+ for (i = 0; i < split.tail_len; i++) -+ data[data_offset + i] = p_val[i]; -+ } -+} -+ -+static int snfi_send_command(struct nfi *nfi, short cmd) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ -+ if (cmd == -1) -+ return 0; -+ -+ if (nfi_spi->snfi_mode == SNFI_MAC_MODE) { -+ snfi_write_mac(nfi_spi, (u8 *)&cmd, 1); -+ nfi_spi->tx_count++; -+ return 0; -+ } -+ -+ nfi_spi->cmd[nfi_spi->cur_cmd_idx++] = cmd; -+ return 0; -+} -+ -+static int snfi_send_address(struct nfi *nfi, int col, int row, -+ int col_cycle, -+ int row_cycle) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ u32 addr, cycle, temp; -+ -+ nb->col = col; -+ nb->row = row; -+ -+ if (nfi_spi->snfi_mode == SNFI_MAC_MODE) { -+ addr = row; -+ cycle = row_cycle; -+ -+ if (!row_cycle) { -+ addr = col; -+ cycle = col_cycle; -+ } -+ -+ temp = nandx_cpu_to_be32(addr) >> ((4 - cycle) << 3); -+ snfi_write_mac(nfi_spi, (u8 *)&temp, cycle); -+ nfi_spi->tx_count += cycle; -+ } else { -+ nfi_spi->row_addr[nfi_spi->cur_addr_idx++] = row; -+ nfi_spi->col_addr[nfi_spi->cur_addr_idx++] = col; -+ } -+ -+ return 0; -+} -+ -+static int snfi_trigger(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ -+ writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL); -+ writel(0, regs + SNF_MAC_INL); -+ -+ nfi_spi->tx_count = 0; -+ nfi_spi->cur_cmd_idx = 0; -+ nfi_spi->cur_addr_idx = 0; -+ -+ return snfi_mac_op(nb); -+} -+ -+static int snfi_select_chip(struct nfi *nfi, int cs) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ val = readl(regs + SNF_MISC_CTL); -+ -+ if (cs == 0) { -+ val &= ~SF2CS_SEL; -+ val &= ~SF2CS_EN; -+ } else if (cs == 1) { -+ val |= SF2CS_SEL; -+ val |= SF2CS_EN; -+ } else { -+ return -EIO; -+ } -+ -+ writel(val, regs + SNF_MISC_CTL); -+ -+ return 0; -+} -+ -+static int snfi_set_delay(struct nfi_base *nb, u8 delay_mode) -+{ -+ void *regs = nb->res.nfi_regs; -+ struct nfi_spi_delay *delay; -+ u32 val; -+ -+ if (delay_mode < 0 || delay_mode > SPI_NAND_MAX_DELAY) -+ return -EINVAL; -+ -+ delay = &spi_delay[delay_mode]; -+ -+ val = delay->tIO_OUT_DLY[0] | delay->tIO_OUT_DLY[1] << 8 | -+ delay->tIO_OUT_DLY[2] << 16 | -+ delay->tIO_OUT_DLY[3] << 24; -+ writel(val, regs + SNF_DLY_CTL1); -+ -+ val = delay->tIO_IN_DLY[0] | (delay->tIO_IN_DLY[1] << 8) | -+ delay->tIO_IN_DLY[2] << 16 | -+ delay->tIO_IN_DLY[3] << 24; -+ writel(val, regs + SNF_DLY_CTL2); -+ -+ val = delay->tCLK_SAM_DLY | delay->tCLK_OUT_DLY << 8 | -+ delay->tCS_DLY << 16 | -+ delay->tWR_EN_DLY << 24; -+ writel(val, regs + SNF_DLY_CTL3); -+ -+ writel(delay->tCS_DLY, regs + SNF_DLY_CTL4); -+ -+ val = readl(regs + SNF_MISC_CTL); -+ val |= (delay->tREAD_LATCH_LATENCY) << -+ LATCH_LAT_SHIFT; -+ writel(val, regs + SNF_MISC_CTL); -+ -+ return 0; -+} -+ -+static int snfi_set_timing(struct nfi *nfi, void *timing, int type) -+{ -+ /* Nothing need to do. */ -+ return 0; -+} -+ -+static int snfi_wait_ready(struct nfi *nfi, int type, u32 timeout) -+{ -+ /* Nothing need to do. */ -+ return 0; -+} -+ -+static int snfi_ctrl(struct nfi *nfi, int cmd, void *args) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ int ret = 0; -+ -+ if (!args) -+ return -EINVAL; -+ -+ switch (cmd) { -+ case NFI_CTRL_DMA: -+ nb->dma_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_NFI_IRQ: -+ nb->nfi_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_IRQ: -+ nb->ecc_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_PAGE_IRQ: -+ nb->page_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC: -+ nb->ecc_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_BAD_MARK_SWAP: -+ nb->bad_mark_swap_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_CLOCK: -+ nb->ecc_clk_en = *(bool *)args; -+ break; -+ -+ case SNFI_CTRL_OP_MODE: -+ nfi_spi->snfi_mode = *(u8 *)args; -+ break; -+ -+ case SNFI_CTRL_RX_MODE: -+ nfi_spi->read_cache_mode = *(u8 *)args; -+ break; -+ -+ case SNFI_CTRL_TX_MODE: -+ nfi_spi->write_cache_mode = *(u8 *)args; -+ break; -+ -+ case SNFI_CTRL_DELAY_MODE: -+ ret = snfi_set_delay(nb, *(u8 *)args); -+ break; -+ -+ default: -+ pr_info("operation not support.\n"); -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ return ret; -+} -+ -+static int snfi_read_bytes(struct nfi *nfi, u8 *data, int count) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ -+ writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL); -+ writel(count, regs + SNF_MAC_INL); -+ -+ ret = snfi_mac_op(nb); -+ if (ret) -+ return ret; -+ -+ snfi_read_mac(nfi_spi, data, count); -+ -+ nfi_spi->tx_count = 0; -+ -+ return 0; -+} -+ -+static int snfi_write_bytes(struct nfi *nfi, u8 *data, int count) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ -+ snfi_write_mac(nfi_spi, data, count); -+ nfi_spi->tx_count += count; -+ -+ writel(0, regs + SNF_MAC_INL); -+ writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL); -+ -+ nfi_spi->tx_count = 0; -+ -+ return snfi_mac_op(nb); -+} -+ -+static int snfi_reset(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ int ret; -+ -+ ret = nfi_spi->parent->nfi.reset(nfi); -+ if (ret) -+ return ret; -+ -+ val = readl(regs + SNF_MISC_CTL); -+ val |= SW_RST; -+ writel(val, regs + SNF_MISC_CTL); -+ -+ ret = readx_poll_timeout_atomic(readw, regs + SNF_STA_CTL1, val, -+ !(val & SPI_STATE), 50, -+ NFI_TIMEOUT); -+ if (ret) { -+ pr_info("spi state active in reset [0x%x] = 0x%x\n", -+ SNF_STA_CTL1, val); -+ return ret; -+ } -+ -+ val = readl(regs + SNF_MISC_CTL); -+ val &= ~SW_RST; -+ writel(val, regs + SNF_MISC_CTL); -+ -+ return 0; -+} -+ -+static int snfi_config_for_write(struct nfi_base *nb, int count) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ nb->set_op_mode(regs, CNFG_CUSTOM_MODE); -+ -+ val = readl(regs + SNF_MISC_CTL); -+ -+ if (nfi_spi->write_cache_mode == SNFI_TX_114) -+ val |= PG_LOAD_X4_EN; -+ -+ if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) -+ val |= PG_LOAD_CUSTOM_EN; -+ -+ writel(val, regs + SNF_MISC_CTL); -+ -+ val = count * (nb->nfi.sector_size + nb->nfi.sector_spare_size); -+ writel(val << PG_LOAD_SHIFT, regs + SNF_MISC_CTL2); -+ -+ val = readl(regs + SNF_PG_CTL1); -+ -+ if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) -+ val |= nfi_spi->cmd[0] << PG_LOAD_CMD_SHIFT; -+ else { -+ val |= nfi_spi->cmd[0] | nfi_spi->cmd[1] << PG_LOAD_CMD_SHIFT | -+ nfi_spi->cmd[2] << PG_EXE_CMD_SHIFT; -+ -+ writel(nfi_spi->row_addr[1], regs + SNF_PG_CTL3); -+ writel(nfi_spi->cmd[3] << GF_CMD_SHIFT | nfi_spi->col_addr[2] << -+ GF_ADDR_SHIFT, regs + SNF_GF_CTL1); -+ } -+ -+ writel(val, regs + SNF_PG_CTL1); -+ writel(nfi_spi->col_addr[1], regs + SNF_PG_CTL2); -+ -+ writel(NFI_CMD_DUMMY_WR, regs + NFI_CMD); -+ -+ return 0; -+} -+ -+static int snfi_config_for_read(struct nfi_base *nb, int count) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ int ret = 0; -+ -+ nb->set_op_mode(regs, CNFG_CUSTOM_MODE); -+ -+ val = readl(regs + SNF_MISC_CTL); -+ val &= ~DARA_READ_MODE_MASK; -+ -+ switch (nfi_spi->read_cache_mode) { -+ -+ case SNFI_RX_111: -+ break; -+ -+ case SNFI_RX_112: -+ val |= X2_DATA_MODE << READ_MODE_SHIFT; -+ break; -+ -+ case SNFI_RX_114: -+ val |= X4_DATA_MODE << READ_MODE_SHIFT; -+ break; -+ -+ case SNFI_RX_122: -+ val |= DUAL_IO_MODE << READ_MODE_SHIFT; -+ break; -+ -+ case SNFI_RX_144: -+ val |= QUAD_IO_MODE << READ_MODE_SHIFT; -+ break; -+ -+ default: -+ pr_info("Not support this read operarion: %d!\n", -+ nfi_spi->read_cache_mode); -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) -+ val |= DATARD_CUSTOM_EN; -+ -+ writel(val, regs + SNF_MISC_CTL); -+ -+ val = count * (nb->nfi.sector_size + nb->nfi.sector_spare_size); -+ writel(val, regs + SNF_MISC_CTL2); -+ -+ val = readl(regs + SNF_RD_CTL2); -+ -+ if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) { -+ val |= nfi_spi->cmd[0]; -+ writel(nfi_spi->col_addr[1], regs + SNF_RD_CTL3); -+ } else { -+ val |= nfi_spi->cmd[2]; -+ writel(nfi_spi->cmd[0] << PAGE_READ_CMD_SHIFT | -+ nfi_spi->row_addr[0], regs + SNF_RD_CTL1); -+ writel(nfi_spi->cmd[1] << GF_CMD_SHIFT | -+ nfi_spi->col_addr[1] << GF_ADDR_SHIFT, -+ regs + SNF_GF_CTL1); -+ writel(nfi_spi->col_addr[2], regs + SNF_RD_CTL3); -+ } -+ -+ writel(val, regs + SNF_RD_CTL2); -+ -+ writel(NFI_CMD_DUMMY_RD, regs + NFI_CMD); -+ -+ return ret; -+} -+ -+static bool is_page_empty(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ u32 *data32 = (u32 *)data; -+ u32 *fdm32 = (u32 *)fdm; -+ u32 i, count = 0; -+ -+ for (i = 0; i < nb->format.page_size >> 2; i++) { -+ if (data32[i] != 0xffff) { -+ count += zero_popcount(data32[i]); -+ if (count > 10) { -+ pr_info("%s %d %d count:%d\n", -+ __func__, __LINE__, i, count); -+ return false; -+ } -+ } -+ } -+ -+ if (fdm) { -+ for (i = 0; i < (nb->nfi.fdm_size * sectors >> 2); i++) -+ if (fdm32[i] != 0xffff) { -+ count += zero_popcount(fdm32[i]); -+ if (count > 10) { -+ pr_info("%s %d %d count:%d\n", -+ __func__, __LINE__, i, count); -+ return false; -+ } -+ } -+ } -+ -+ return true; -+} -+ -+static int rw_prepare(struct nfi_base *nb, int sectors, u8 *data, -+ u8 *fdm, -+ bool read) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ int ret; -+ -+ ret = nfi_spi->parent->rw_prepare(nb, sectors, data, fdm, read); -+ if (ret) -+ return ret; -+ -+ if (read) -+ ret = snfi_config_for_read(nb, sectors); -+ else -+ ret = snfi_config_for_write(nb, sectors); -+ -+ return ret; -+} -+ -+static void rw_complete(struct nfi_base *nb, u8 *data, u8 *fdm, -+ bool read) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ nfi_spi->parent->rw_complete(nb, data, fdm, read); -+ -+ val = readl(regs + SNF_MISC_CTL); -+ -+ if (read) -+ val &= ~DATARD_CUSTOM_EN; -+ else -+ val &= ~PG_LOAD_CUSTOM_EN; -+ -+ writel(val, regs + SNF_MISC_CTL); -+ -+ nfi_spi->tx_count = 0; -+ nfi_spi->cur_cmd_idx = 0; -+ nfi_spi->cur_addr_idx = 0; -+} -+ -+static void set_nfi_base_funcs(struct nfi_base *nb) -+{ -+ nb->nfi.reset = snfi_reset; -+ nb->nfi.set_timing = snfi_set_timing; -+ nb->nfi.wait_ready = snfi_wait_ready; -+ -+ nb->nfi.send_cmd = snfi_send_command; -+ nb->nfi.send_addr = snfi_send_address; -+ nb->nfi.trigger = snfi_trigger; -+ nb->nfi.nfi_ctrl = snfi_ctrl; -+ nb->nfi.select_chip = snfi_select_chip; -+ -+ nb->nfi.read_bytes = snfi_read_bytes; -+ nb->nfi.write_bytes = snfi_write_bytes; -+ -+ nb->rw_prepare = rw_prepare; -+ nb->rw_complete = rw_complete; -+ nb->is_page_empty = is_page_empty; -+ -+} -+ -+struct nfi *nfi_extend_init(struct nfi_base *nb) -+{ -+ struct nfi_spi *nfi_spi; -+ -+ nfi_spi = mem_alloc(1, sizeof(struct nfi_spi)); -+ if (!nfi_spi) { -+ pr_info("snfi alloc memory fail @%s.\n", __func__); -+ return NULL; -+ } -+ -+ memcpy(&nfi_spi->base, nb, sizeof(struct nfi_base)); -+ nfi_spi->parent = nb; -+ -+ nfi_spi->read_cache_mode = SNFI_RX_114; -+ nfi_spi->write_cache_mode = SNFI_TX_114; -+ -+ set_nfi_base_funcs(&nfi_spi->base); -+ -+ /* Change nfi to spi mode */ -+ writel(SPI_MODE, nb->res.nfi_regs + SNF_SNF_CNFG); -+ -+ return &(nfi_spi->base.nfi); -+} -+ -+void nfi_extend_exit(struct nfi_base *nb) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ -+ mem_free(nfi_spi->parent); -+ mem_free(nfi_spi); -+} -+ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_spi.h -@@ -0,0 +1,44 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_SPI_H__ -+#define __NFI_SPI_H__ -+ -+#define SPI_NAND_MAX_DELAY 6 -+#define SPI_NAND_MAX_OP 4 -+ -+/*TODO - add comments */ -+struct nfi_spi_delay { -+ u8 tCLK_SAM_DLY; -+ u8 tCLK_OUT_DLY; -+ u8 tCS_DLY; -+ u8 tWR_EN_DLY; -+ u8 tIO_IN_DLY[4]; -+ u8 tIO_OUT_DLY[4]; -+ u8 tREAD_LATCH_LATENCY; -+}; -+ -+/* SPI Nand structure */ -+struct nfi_spi { -+ struct nfi_base base; -+ struct nfi_base *parent; -+ -+ u8 snfi_mode; -+ u8 tx_count; -+ -+ u8 cmd[SPI_NAND_MAX_OP]; -+ u8 cur_cmd_idx; -+ -+ u32 row_addr[SPI_NAND_MAX_OP]; -+ u32 col_addr[SPI_NAND_MAX_OP]; -+ u8 cur_addr_idx; -+ -+ u8 read_cache_mode; -+ u8 write_cache_mode; -+}; -+ -+#endif /* __NFI_SPI_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_spi_regs.h -@@ -0,0 +1,64 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_SPI_REGS_H__ -+#define __NFI_SPI_REGS_H__ -+ -+#define SNF_MAC_CTL 0x500 -+#define WIP BIT(0) -+#define WIP_READY BIT(1) -+#define SF_TRIG BIT(2) -+#define SF_MAC_EN BIT(3) -+#define MAC_XIO_SEL BIT(4) -+#define SNF_MAC_OUTL 0x504 -+#define SNF_MAC_INL 0x508 -+#define SNF_RD_CTL1 0x50c -+#define PAGE_READ_CMD_SHIFT 24 -+#define SNF_RD_CTL2 0x510 -+#define SNF_RD_CTL3 0x514 -+#define SNF_GF_CTL1 0x518 -+#define GF_ADDR_SHIFT 16 -+#define GF_CMD_SHIFT 24 -+#define SNF_GF_CTL3 0x520 -+#define SNF_PG_CTL1 0x524 -+#define PG_EXE_CMD_SHIFT 16 -+#define PG_LOAD_CMD_SHIFT 8 -+#define SNF_PG_CTL2 0x528 -+#define SNF_PG_CTL3 0x52c -+#define SNF_ER_CTL 0x530 -+#define SNF_ER_CTL2 0x534 -+#define SNF_MISC_CTL 0x538 -+#define SW_RST BIT(28) -+#define PG_LOAD_X4_EN BIT(20) -+#define X2_DATA_MODE 1 -+#define X4_DATA_MODE 2 -+#define DUAL_IO_MODE 5 -+#define QUAD_IO_MODE 6 -+#define READ_MODE_SHIFT 16 -+#define LATCH_LAT_SHIFT 8 -+#define LATCH_LAT_MASK GENMASK(9, 8) -+#define DARA_READ_MODE_MASK GENMASK(18, 16) -+#define SF2CS_SEL BIT(13) -+#define SF2CS_EN BIT(12) -+#define PG_LOAD_CUSTOM_EN BIT(7) -+#define DATARD_CUSTOM_EN BIT(6) -+#define SNF_MISC_CTL2 0x53c -+#define PG_LOAD_SHIFT 16 -+#define SNF_DLY_CTL1 0x540 -+#define SNF_DLY_CTL2 0x544 -+#define SNF_DLY_CTL3 0x548 -+#define SNF_DLY_CTL4 0x54c -+#define SNF_STA_CTL1 0x550 -+#define SPI_STATE GENMASK(3, 0) -+#define SNF_STA_CTL2 0x554 -+#define SNF_STA_CTL3 0x558 -+#define SNF_SNF_CNFG 0x55c -+#define SPI_MODE BIT(0) -+#define SNF_DEBUG_SEL 0x560 -+#define SPI_GPRAM_ADDR 0x800 -+ -+#endif /* __NFI_SPI_REGS_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfiecc.c -@@ -0,0 +1,510 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nfiecc_regs.h" -+#include "nfiecc.h" -+ -+#define NFIECC_IDLE_REG(op) \ -+ ((op) == ECC_ENCODE ? NFIECC_ENCIDLE : NFIECC_DECIDLE) -+#define IDLE_MASK 1 -+#define NFIECC_CTL_REG(op) \ -+ ((op) == ECC_ENCODE ? NFIECC_ENCCON : NFIECC_DECCON) -+#define NFIECC_IRQ_REG(op) \ -+ ((op) == ECC_ENCODE ? NFIECC_ENCIRQEN : NFIECC_DECIRQEN) -+#define NFIECC_ADDR(op) \ -+ ((op) == ECC_ENCODE ? NFIECC_ENCDIADDR : NFIECC_DECDIADDR) -+ -+#define ECC_TIMEOUT 500000 -+ -+/* ecc strength that each IP supports */ -+static const int ecc_strength_mt7622[] = { -+ 4, 6, 8, 10, 12, 14, 16 -+}; -+ -+static int nfiecc_irq_handler(void *data) -+{ -+ struct nfiecc *ecc = data; -+ void *regs = ecc->res.regs; -+ u32 status; -+ -+ status = readl(regs + NFIECC_DECIRQSTA) & DEC_IRQSTA_GEN; -+ if (status) { -+ status = readl(regs + NFIECC_DECDONE); -+ if (!(status & ecc->config.sectors)) -+ return NAND_IRQ_NONE; -+ -+ /* -+ * Clear decode IRQ status once again to ensure that -+ * there will be no extra IRQ. -+ */ -+ readl(regs + NFIECC_DECIRQSTA); -+ ecc->config.sectors = 0; -+ nandx_event_complete(ecc->done); -+ } else { -+ status = readl(regs + NFIECC_ENCIRQSTA) & ENC_IRQSTA_GEN; -+ if (!status) -+ return NAND_IRQ_NONE; -+ -+ nandx_event_complete(ecc->done); -+ } -+ -+ return NAND_IRQ_HANDLED; -+} -+ -+static inline int nfiecc_wait_idle(struct nfiecc *ecc) -+{ -+ int op = ecc->config.op; -+ int ret, val; -+ -+ ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_IDLE_REG(op), -+ val, val & IDLE_MASK, -+ 10, ECC_TIMEOUT); -+ if (ret) -+ pr_info("%s not idle\n", -+ op == ECC_ENCODE ? "encoder" : "decoder"); -+ -+ return ret; -+} -+ -+static int nfiecc_wait_encode_done(struct nfiecc *ecc) -+{ -+ int ret, val; -+ -+ if (ecc->ecc_irq_en) { -+ /* poll one time to avoid missing irq event */ -+ ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_ENCSTA, -+ val, val & ENC_FSM_IDLE, 1, 1); -+ if (!ret) -+ return 0; -+ -+ /* irq done, if not, we can go on to poll status for a while */ -+ ret = nandx_event_wait_complete(ecc->done, ECC_TIMEOUT); -+ if (ret) -+ return 0; -+ } -+ -+ ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_ENCSTA, -+ val, val & ENC_FSM_IDLE, -+ 10, ECC_TIMEOUT); -+ if (ret) -+ pr_info("encode timeout\n"); -+ -+ return ret; -+ -+} -+ -+static int nfiecc_wait_decode_done(struct nfiecc *ecc) -+{ -+ u32 secbit = BIT(ecc->config.sectors - 1); -+ void *regs = ecc->res.regs; -+ int ret, val; -+ -+ if (ecc->ecc_irq_en) { -+ ret = readl_poll_timeout_atomic(regs + NFIECC_DECDONE, -+ val, val & secbit, 1, 1); -+ if (!ret) -+ return 0; -+ -+ ret = nandx_event_wait_complete(ecc->done, ECC_TIMEOUT); -+ if (ret) -+ return 0; -+ } -+ -+ ret = readl_poll_timeout_atomic(regs + NFIECC_DECDONE, -+ val, val & secbit, -+ 10, ECC_TIMEOUT); -+ if (ret) { -+ pr_info("decode timeout\n"); -+ return ret; -+ } -+ -+ /* decode done does not stands for ecc all work done. -+ * we need check syn, bma, chien, autoc all idle. -+ * just check it when ECC_DECCNFG[13:12] is 3, -+ * which means auto correct. -+ */ -+ ret = readl_poll_timeout_atomic(regs + NFIECC_DECFSM, -+ val, (val & FSM_MASK) == FSM_IDLE, -+ 10, ECC_TIMEOUT); -+ if (ret) -+ pr_info("decode fsm(0x%x) is not idle\n", -+ readl(regs + NFIECC_DECFSM)); -+ -+ return ret; -+} -+ -+static int nfiecc_wait_done(struct nfiecc *ecc) -+{ -+ if (ecc->config.op == ECC_ENCODE) -+ return nfiecc_wait_encode_done(ecc); -+ -+ return nfiecc_wait_decode_done(ecc); -+} -+ -+static void nfiecc_encode_config(struct nfiecc *ecc, u32 ecc_idx) -+{ -+ struct nfiecc_config *config = &ecc->config; -+ u32 val; -+ -+ val = ecc_idx | (config->mode << ecc->caps->ecc_mode_shift); -+ -+ if (config->mode == ECC_DMA_MODE) -+ val |= ENC_BURST_EN; -+ -+ val |= (config->len << 3) << ENCCNFG_MS_SHIFT; -+ writel(val, ecc->res.regs + NFIECC_ENCCNFG); -+} -+ -+static void nfiecc_decode_config(struct nfiecc *ecc, u32 ecc_idx) -+{ -+ struct nfiecc_config *config = &ecc->config; -+ u32 dec_sz = (config->len << 3) + -+ config->strength * ecc->caps->parity_bits; -+ u32 val; -+ -+ val = ecc_idx | (config->mode << ecc->caps->ecc_mode_shift); -+ -+ if (config->mode == ECC_DMA_MODE) -+ val |= DEC_BURST_EN; -+ -+ val |= (dec_sz << DECCNFG_MS_SHIFT) | -+ (config->deccon << DEC_CON_SHIFT); -+ val |= DEC_EMPTY_EN; -+ writel(val, ecc->res.regs + NFIECC_DECCNFG); -+} -+ -+static void nfiecc_config(struct nfiecc *ecc) -+{ -+ u32 idx; -+ -+ for (idx = 0; idx < ecc->caps->ecc_strength_num; idx++) { -+ if (ecc->config.strength == ecc->caps->ecc_strength[idx]) -+ break; -+ } -+ -+ if (ecc->config.op == ECC_ENCODE) -+ nfiecc_encode_config(ecc, idx); -+ else -+ nfiecc_decode_config(ecc, idx); -+} -+ -+static int nfiecc_enable(struct nfiecc *ecc) -+{ -+ enum nfiecc_operation op = ecc->config.op; -+ void *regs = ecc->res.regs; -+ -+ nfiecc_config(ecc); -+ -+ writel(ECC_OP_EN, regs + NFIECC_CTL_REG(op)); -+ -+ if (ecc->ecc_irq_en) { -+ writel(ECC_IRQEN, regs + NFIECC_IRQ_REG(op)); -+ -+ if (ecc->page_irq_en) -+ writel(ECC_IRQEN | ECC_PG_IRQ_SEL, -+ regs + NFIECC_IRQ_REG(op)); -+ -+ nandx_event_init(ecc->done); -+ } -+ -+ return 0; -+} -+ -+static int nfiecc_disable(struct nfiecc *ecc) -+{ -+ enum nfiecc_operation op = ecc->config.op; -+ void *regs = ecc->res.regs; -+ -+ nfiecc_wait_idle(ecc); -+ -+ writel(0, regs + NFIECC_IRQ_REG(op)); -+ writel(~ECC_OP_EN, regs + NFIECC_CTL_REG(op)); -+ -+ return 0; -+} -+ -+static int nfiecc_correct_data(struct nfiecc *ecc, -+ struct nfiecc_status *status, -+ u8 *data, u32 sector) -+{ -+ u32 err, offset, i; -+ u32 loc, byteloc, bitloc; -+ -+ status->corrected = 0; -+ status->failed = 0; -+ -+ offset = (sector >> 2); -+ err = readl(ecc->res.regs + NFIECC_DECENUM(offset)); -+ err >>= (sector % 4) * 8; -+ err &= ecc->caps->err_mask; -+ -+ if (err == ecc->caps->err_mask) { -+ status->failed++; -+ return -ENANDREAD; -+ } -+ -+ status->corrected += err; -+ status->bitflips = max_t(u32, status->bitflips, err); -+ -+ for (i = 0; i < err; i++) { -+ loc = readl(ecc->res.regs + NFIECC_DECEL(i >> 1)); -+ loc >>= ((i & 0x1) << 4); -+ byteloc = loc >> 3; -+ bitloc = loc & 0x7; -+ data[byteloc] ^= (1 << bitloc); -+ } -+ -+ return 0; -+} -+ -+static int nfiecc_fill_data(struct nfiecc *ecc, u8 *data) -+{ -+ struct nfiecc_config *config = &ecc->config; -+ void *regs = ecc->res.regs; -+ int size, ret, i; -+ u32 val; -+ -+ if (config->mode == ECC_DMA_MODE) { -+ if ((unsigned long)config->dma_addr & 0x3) -+ pr_info("encode address is not 4B aligned: 0x%x\n", -+ (u32)(unsigned long)config->dma_addr); -+ -+ writel((unsigned long)config->dma_addr, -+ regs + NFIECC_ADDR(config->op)); -+ } else if (config->mode == ECC_PIO_MODE) { -+ if (config->op == ECC_ENCODE) { -+ size = (config->len + 3) >> 2; -+ } else { -+ size = config->strength * ecc->caps->parity_bits; -+ size = (size + 7) >> 3; -+ size += config->len; -+ size >>= 2; -+ } -+ -+ for (i = 0; i < size; i++) { -+ ret = readl_poll_timeout_atomic(regs + NFIECC_PIO_DIRDY, -+ val, val & PIO_DI_RDY, -+ 10, ECC_TIMEOUT); -+ if (ret) -+ return ret; -+ -+ writel(*((u32 *)data + i), regs + NFIECC_PIO_DI); -+ } -+ } -+ -+ return 0; -+} -+ -+static int nfiecc_encode(struct nfiecc *ecc, u8 *data) -+{ -+ struct nfiecc_config *config = &ecc->config; -+ u32 len, i, val = 0; -+ u8 *p; -+ int ret; -+ -+ /* Under NFI mode, nothing need to do */ -+ if (config->mode == ECC_NFI_MODE) -+ return 0; -+ -+ ret = nfiecc_fill_data(ecc, data); -+ if (ret) -+ return ret; -+ -+ ret = nfiecc_wait_encode_done(ecc); -+ if (ret) -+ return ret; -+ -+ ret = nfiecc_wait_idle(ecc); -+ if (ret) -+ return ret; -+ -+ /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ -+ len = (config->strength * ecc->caps->parity_bits + 7) >> 3; -+ p = data + config->len; -+ -+ /* Write the parity bytes generated by the ECC back to the OOB region */ -+ for (i = 0; i < len; i++) { -+ if ((i % 4) == 0) -+ val = readl(ecc->res.regs + NFIECC_ENCPAR(i / 4)); -+ -+ p[i] = (val >> ((i % 4) * 8)) & 0xff; -+ } -+ -+ return 0; -+} -+ -+static int nfiecc_decode(struct nfiecc *ecc, u8 *data) -+{ -+ int ret; -+ -+ /* Under NFI mode, nothing need to do */ -+ if (ecc->config.mode == ECC_NFI_MODE) -+ return 0; -+ -+ ret = nfiecc_fill_data(ecc, data); -+ if (ret) -+ return ret; -+ -+ return nfiecc_wait_decode_done(ecc); -+} -+ -+static int nfiecc_decode_status(struct nfiecc *ecc, u32 start_sector, -+ u32 sectors) -+{ -+ void *regs = ecc->res.regs; -+ u32 i, val = 0, err; -+ u32 bitflips = 0; -+ -+ for (i = start_sector; i < start_sector + sectors; i++) { -+ if ((i % 4) == 0) -+ val = readl(regs + NFIECC_DECENUM(i / 4)); -+ -+ err = val >> ((i % 4) * 5); -+ err &= ecc->caps->err_mask; -+ -+ if (err == ecc->caps->err_mask) -+ pr_err("sector %d is uncorrect\n", i); -+ -+ bitflips = max_t(u32, bitflips, err); -+ } -+ -+ if (bitflips == ecc->caps->err_mask) -+ return -ENANDREAD; -+ -+ if (bitflips) -+ pr_info("bitflips %d is corrected\n", bitflips); -+ -+ return bitflips; -+} -+ -+static int nfiecc_adjust_strength(struct nfiecc *ecc, int strength) -+{ -+ struct nfiecc_caps *caps = ecc->caps; -+ int i, count = caps->ecc_strength_num; -+ -+ if (strength >= caps->ecc_strength[count - 1]) -+ return caps->ecc_strength[count - 1]; -+ -+ if (strength < caps->ecc_strength[0]) -+ return -EINVAL; -+ -+ for (i = 1; i < count; i++) { -+ if (strength < caps->ecc_strength[i]) -+ return caps->ecc_strength[i - 1]; -+ } -+ -+ return -EINVAL; -+} -+ -+static int nfiecc_ctrl(struct nfiecc *ecc, int cmd, void *args) -+{ -+ int ret = 0; -+ -+ switch (cmd) { -+ case NFI_CTRL_ECC_IRQ: -+ ecc->ecc_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_PAGE_IRQ: -+ ecc->page_irq_en = *(bool *)args; -+ break; -+ -+ default: -+ pr_info("invalid arguments.\n"); -+ ret = -EINVAL; -+ break; -+ } -+ -+ return ret; -+} -+ -+static int nfiecc_hw_init(struct nfiecc *ecc) -+{ -+ int ret; -+ -+ ret = nfiecc_wait_idle(ecc); -+ if (ret) -+ return ret; -+ -+ writel(~ECC_OP_EN, ecc->res.regs + NFIECC_ENCCON); -+ -+ ret = nfiecc_wait_idle(ecc); -+ if (ret) -+ return ret; -+ -+ writel(~ECC_OP_EN, ecc->res.regs + NFIECC_DECCON); -+ -+ return 0; -+} -+ -+static struct nfiecc_caps nfiecc_caps_mt7622 = { -+ .err_mask = 0x1f, -+ .ecc_mode_shift = 4, -+ .parity_bits = 13, -+ .ecc_strength = ecc_strength_mt7622, -+ .ecc_strength_num = 7, -+}; -+ -+static struct nfiecc_caps *nfiecc_get_match_data(enum mtk_ic_version ic) -+{ -+ /* NOTE: add other IC's data */ -+ return &nfiecc_caps_mt7622; -+} -+ -+struct nfiecc *nfiecc_init(struct nfiecc_resource *res) -+{ -+ struct nfiecc *ecc; -+ int ret; -+ -+ ecc = mem_alloc(1, sizeof(struct nfiecc)); -+ if (!ecc) -+ return NULL; -+ -+ ecc->res = *res; -+ -+ ret = nandx_irq_register(res->dev, res->irq_id, nfiecc_irq_handler, -+ "mtk-ecc", ecc); -+ if (ret) { -+ pr_info("ecc irq register failed!\n"); -+ goto error; -+ } -+ -+ ecc->ecc_irq_en = false; -+ ecc->page_irq_en = false; -+ ecc->done = nandx_event_create(); -+ ecc->caps = nfiecc_get_match_data(res->ic_ver); -+ -+ ecc->adjust_strength = nfiecc_adjust_strength; -+ ecc->enable = nfiecc_enable; -+ ecc->disable = nfiecc_disable; -+ ecc->decode = nfiecc_decode; -+ ecc->encode = nfiecc_encode; -+ ecc->wait_done = nfiecc_wait_done; -+ ecc->decode_status = nfiecc_decode_status; -+ ecc->correct_data = nfiecc_correct_data; -+ ecc->nfiecc_ctrl = nfiecc_ctrl; -+ -+ ret = nfiecc_hw_init(ecc); -+ if (ret) -+ return NULL; -+ -+ return ecc; -+ -+error: -+ mem_free(ecc); -+ -+ return NULL; -+} -+ -+void nfiecc_exit(struct nfiecc *ecc) -+{ -+ nandx_event_destroy(ecc->done); -+ mem_free(ecc); -+} -+ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfiecc.h -@@ -0,0 +1,90 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFIECC_H__ -+#define __NFIECC_H__ -+ -+enum nfiecc_mode { -+ ECC_DMA_MODE, -+ ECC_NFI_MODE, -+ ECC_PIO_MODE -+}; -+ -+enum nfiecc_operation { -+ ECC_ENCODE, -+ ECC_DECODE -+}; -+ -+enum nfiecc_deccon { -+ ECC_DEC_FER = 1, -+ ECC_DEC_LOCATE = 2, -+ ECC_DEC_CORRECT = 3 -+}; -+ -+struct nfiecc_resource { -+ int ic_ver; -+ void *dev; -+ void *regs; -+ int irq_id; -+ -+}; -+ -+struct nfiecc_status { -+ u32 corrected; -+ u32 failed; -+ u32 bitflips; -+}; -+ -+struct nfiecc_caps { -+ u32 err_mask; -+ u32 ecc_mode_shift; -+ u32 parity_bits; -+ const int *ecc_strength; -+ u32 ecc_strength_num; -+}; -+ -+struct nfiecc_config { -+ enum nfiecc_operation op; -+ enum nfiecc_mode mode; -+ enum nfiecc_deccon deccon; -+ -+ void *dma_addr; /* DMA use only */ -+ u32 strength; -+ u32 sectors; -+ u32 len; -+}; -+ -+struct nfiecc { -+ struct nfiecc_resource res; -+ struct nfiecc_config config; -+ struct nfiecc_caps *caps; -+ -+ bool ecc_irq_en; -+ bool page_irq_en; -+ -+ void *done; -+ -+ int (*adjust_strength)(struct nfiecc *ecc, int strength); -+ int (*enable)(struct nfiecc *ecc); -+ int (*disable)(struct nfiecc *ecc); -+ -+ int (*decode)(struct nfiecc *ecc, u8 *data); -+ int (*encode)(struct nfiecc *ecc, u8 *data); -+ -+ int (*decode_status)(struct nfiecc *ecc, u32 start_sector, u32 sectors); -+ int (*correct_data)(struct nfiecc *ecc, -+ struct nfiecc_status *status, -+ u8 *data, u32 sector); -+ int (*wait_done)(struct nfiecc *ecc); -+ -+ int (*nfiecc_ctrl)(struct nfiecc *ecc, int cmd, void *args); -+}; -+ -+struct nfiecc *nfiecc_init(struct nfiecc_resource *res); -+void nfiecc_exit(struct nfiecc *ecc); -+ -+#endif /* __NFIECC_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfiecc_regs.h -@@ -0,0 +1,51 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFIECC_REGS_H__ -+#define __NFIECC_REGS_H__ -+ -+#define NFIECC_ENCCON 0x000 -+/* NFIECC_DECCON has same bit define */ -+#define ECC_OP_EN BIT(0) -+#define NFIECC_ENCCNFG 0x004 -+#define ENCCNFG_MS_SHIFT 16 -+#define ENC_BURST_EN BIT(8) -+#define NFIECC_ENCDIADDR 0x008 -+#define NFIECC_ENCIDLE 0x00c -+#define NFIECC_ENCSTA 0x02c -+#define ENC_FSM_IDLE 1 -+#define NFIECC_ENCIRQEN 0x030 -+/* NFIECC_DECIRQEN has same bit define */ -+#define ECC_IRQEN BIT(0) -+#define ECC_PG_IRQ_SEL BIT(1) -+#define NFIECC_ENCIRQSTA 0x034 -+#define ENC_IRQSTA_GEN BIT(0) -+#define NFIECC_PIO_DIRDY 0x080 -+#define PIO_DI_RDY BIT(0) -+#define NFIECC_PIO_DI 0x084 -+#define NFIECC_DECCON 0x100 -+#define NFIECC_DECCNFG 0x104 -+#define DEC_BURST_EN BIT(8) -+#define DEC_EMPTY_EN BIT(31) -+#define DEC_CON_SHIFT 12 -+#define DECCNFG_MS_SHIFT 16 -+#define NFIECC_DECDIADDR 0x108 -+#define NFIECC_DECIDLE 0x10c -+#define NFIECC_DECENUM(x) (0x114 + (x) * 4) -+#define NFIECC_DECDONE 0x11c -+#define NFIECC_DECIRQEN 0x140 -+#define NFIECC_DECIRQSTA 0x144 -+#define DEC_IRQSTA_GEN BIT(0) -+#define NFIECC_DECFSM 0x14c -+#define FSM_MASK 0x7f0f0f0f -+#define FSM_IDLE 0x01010101 -+#define NFIECC_BYPASS 0x20c -+#define NFIECC_BYPASS_EN BIT(0) -+#define NFIECC_ENCPAR(x) (0x010 + (x) * 4) -+#define NFIECC_DECEL(x) (0x120 + (x) * 4) -+ -+#endif /* __NFIECC_REGS_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/driver/Nandx.mk -@@ -0,0 +1,18 @@ -+# -+# Copyright (C) 2017 MediaTek Inc. -+# Licensed under either -+# BSD Licence, (see NOTICE for more details) -+# GNU General Public License, version 2.0, (see NOTICE for more details) -+# -+ -+nandx-$(NANDX_SIMULATOR_SUPPORT) += simulator/driver.c -+ -+nandx-$(NANDX_CTP_SUPPORT) += ctp/ts_nand.c -+nandx-$(NANDX_CTP_SUPPORT) += ctp/nand_test.c -+nandx-header-$(NANDX_CTP_SUPPORT) += ctp/nand_test.h -+ -+nandx-$(NANDX_BBT_SUPPORT) += bbt/bbt.c -+nandx-$(NANDX_BROM_SUPPORT) += brom/driver.c -+nandx-$(NANDX_KERNEL_SUPPORT) += kernel/driver.c -+nandx-$(NANDX_LK_SUPPORT) += lk/driver.c -+nandx-$(NANDX_UBOOT_SUPPORT) += uboot/driver.c ---- /dev/null -+++ b/drivers/mtd/nandx/driver/bbt/bbt.c -@@ -0,0 +1,408 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "bbt.h" -+ -+/* Not support: multi-chip */ -+static u8 main_bbt_pattern[] = {'B', 'b', 't', '0' }; -+static u8 mirror_bbt_pattern[] = {'1', 't', 'b', 'B' }; -+ -+static struct bbt_manager g_bbt_manager = { -+ { {{main_bbt_pattern, 4}, 0, BBT_INVALID_ADDR}, -+ {{mirror_bbt_pattern, 4}, 0, BBT_INVALID_ADDR} -+ }, -+ NAND_BBT_SCAN_MAXBLOCKS, NULL -+}; -+ -+static inline void set_bbt_mark(u8 *bbt, int block, u8 mark) -+{ -+ int index, offset; -+ -+ index = GET_ENTRY(block); -+ offset = GET_POSITION(block); -+ -+ bbt[index] &= ~(BBT_ENTRY_MASK << offset); -+ bbt[index] |= (mark & BBT_ENTRY_MASK) << offset; -+ pr_info("%s %d block:%d, bbt[%d]:0x%x, offset:%d, mark:%d\n", -+ __func__, __LINE__, block, index, bbt[index], offset, mark); -+} -+ -+static inline u8 get_bbt_mark(u8 *bbt, int block) -+{ -+ int offset = GET_POSITION(block); -+ int index = GET_ENTRY(block); -+ u8 value = bbt[index]; -+ -+ return (value >> offset) & BBT_ENTRY_MASK; -+} -+ -+static void mark_nand_bad(struct nandx_info *nand, int block) -+{ -+ u8 *buf; -+ -+ buf = mem_alloc(1, nand->page_size + nand->oob_size); -+ if (!buf) { -+ pr_info("%s, %d, memory alloc fail, pagesize:%d, oobsize:%d\n", -+ __func__, __LINE__, nand->page_size, nand->oob_size); -+ return; -+ } -+ memset(buf, 0, nand->page_size + nand->oob_size); -+ nandx_erase(block * nand->block_size, nand->block_size); -+ nandx_write(buf, buf + nand->page_size, block * nand->block_size, -+ nand->page_size); -+ mem_free(buf); -+} -+ -+static inline bool is_bbt_data(u8 *buf, struct bbt_pattern *pattern) -+{ -+ int i; -+ -+ for (i = 0; i < pattern->len; i++) { -+ if (buf[i] != pattern->data[i]) -+ return false; -+ } -+ -+ return true; -+} -+ -+static u64 get_bbt_address(struct nandx_info *nand, u8 *bbt, -+ u64 mirror_addr, -+ int max_blocks) -+{ -+ u64 addr, end_addr; -+ u8 mark; -+ -+ addr = nand->total_size; -+ end_addr = nand->total_size - nand->block_size * max_blocks; -+ -+ while (addr > end_addr) { -+ addr -= nand->block_size; -+ mark = get_bbt_mark(bbt, div_down(addr, nand->block_size)); -+ -+ if (mark == BBT_BLOCK_WORN || mark == BBT_BLOCK_FACTORY_BAD) -+ continue; -+ if (addr != mirror_addr) -+ return addr; -+ } -+ -+ return BBT_INVALID_ADDR; -+} -+ -+static int read_bbt(struct bbt_desc *desc, u8 *bbt, u32 len) -+{ -+ int ret; -+ -+ ret = nandx_read(bbt, NULL, desc->bbt_addr + desc->pattern.len + 1, -+ len); -+ if (ret < 0) -+ pr_info("nand_bbt: error reading BBT page, ret:-%x\n", ret); -+ -+ return ret; -+} -+ -+static void create_bbt(struct nandx_info *nand, u8 *bbt) -+{ -+ u32 offset = 0, block = 0; -+ -+ do { -+ if (nandx_is_bad_block(offset)) { -+ pr_info("Create bbt at bad block:%d\n", block); -+ set_bbt_mark(bbt, block, BBT_BLOCK_FACTORY_BAD); -+ } -+ block++; -+ offset += nand->block_size; -+ } while (offset < nand->total_size); -+} -+ -+static int search_bbt(struct nandx_info *nand, struct bbt_desc *desc, -+ int max_blocks) -+{ -+ u64 addr, end_addr; -+ u8 *buf; -+ int ret; -+ -+ buf = mem_alloc(1, nand->page_size); -+ if (!buf) { -+ pr_info("%s, %d, mem alloc fail!!! len:%d\n", -+ __func__, __LINE__, nand->page_size); -+ return -ENOMEM; -+ } -+ -+ addr = nand->total_size; -+ end_addr = nand->total_size - max_blocks * nand->block_size; -+ while (addr > end_addr) { -+ addr -= nand->block_size; -+ -+ nandx_read(buf, NULL, addr, nand->page_size); -+ -+ if (is_bbt_data(buf, &desc->pattern)) { -+ desc->bbt_addr = addr; -+ desc->version = buf[desc->pattern.len]; -+ pr_info("BBT is found at addr 0x%llx, version %d\n", -+ desc->bbt_addr, desc->version); -+ ret = 0; -+ break; -+ } -+ ret = -EFAULT; -+ } -+ -+ mem_free(buf); -+ return ret; -+} -+ -+static int save_bbt(struct nandx_info *nand, struct bbt_desc *desc, -+ u8 *bbt) -+{ -+ u32 page_size_mask, total_block; -+ int write_len; -+ u8 *buf; -+ int ret; -+ -+ ret = nandx_erase(desc->bbt_addr, nand->block_size); -+ if (ret) { -+ pr_info("erase addr 0x%llx fail !!!, ret %d\n", -+ desc->bbt_addr, ret); -+ return ret; -+ } -+ -+ total_block = div_down(nand->total_size, nand->block_size); -+ write_len = GET_BBT_LENGTH(total_block) + desc->pattern.len + 1; -+ page_size_mask = nand->page_size - 1; -+ write_len = (write_len + page_size_mask) & (~page_size_mask); -+ -+ buf = (u8 *)mem_alloc(1, write_len); -+ if (!buf) { -+ pr_info("%s, %d, mem alloc fail!!! len:%d\n", -+ __func__, __LINE__, write_len); -+ return -ENOMEM; -+ } -+ memset(buf, 0xFF, write_len); -+ -+ memcpy(buf, desc->pattern.data, desc->pattern.len); -+ buf[desc->pattern.len] = desc->version; -+ -+ memcpy(buf + desc->pattern.len + 1, bbt, GET_BBT_LENGTH(total_block)); -+ -+ ret = nandx_write(buf, NULL, desc->bbt_addr, write_len); -+ -+ if (ret) -+ pr_info("nandx_write fail(%d), offset:0x%llx, len(%d)\n", -+ ret, desc->bbt_addr, write_len); -+ mem_free(buf); -+ -+ return ret; -+} -+ -+static int write_bbt(struct nandx_info *nand, struct bbt_desc *main, -+ struct bbt_desc *mirror, u8 *bbt, int max_blocks) -+{ -+ int block; -+ int ret; -+ -+ do { -+ if (main->bbt_addr == BBT_INVALID_ADDR) { -+ main->bbt_addr = get_bbt_address(nand, bbt, -+ mirror->bbt_addr, max_blocks); -+ if (main->bbt_addr == BBT_INVALID_ADDR) -+ return -ENOSPC; -+ } -+ -+ ret = save_bbt(nand, main, bbt); -+ if (!ret) -+ break; -+ -+ block = div_down(main->bbt_addr, nand->block_size); -+ set_bbt_mark(bbt, block, BBT_BLOCK_WORN); -+ main->version++; -+ mark_nand_bad(nand, block); -+ main->bbt_addr = BBT_INVALID_ADDR; -+ } while (1); -+ -+ return 0; -+} -+ -+static void mark_bbt_region(struct nandx_info *nand, u8 *bbt, int bbt_blocks) -+{ -+ int total_block; -+ int block; -+ u8 mark; -+ -+ total_block = div_down(nand->total_size, nand->block_size); -+ block = total_block - bbt_blocks; -+ -+ while (bbt_blocks) { -+ mark = get_bbt_mark(bbt, block); -+ if (mark == BBT_BLOCK_GOOD) -+ set_bbt_mark(bbt, block, BBT_BLOCK_RESERVED); -+ block++; -+ bbt_blocks--; -+ } -+} -+ -+static void unmark_bbt_region(struct nandx_info *nand, u8 *bbt, int bbt_blocks) -+{ -+ int total_block; -+ int block; -+ u8 mark; -+ -+ total_block = div_down(nand->total_size, nand->block_size); -+ block = total_block - bbt_blocks; -+ -+ while (bbt_blocks) { -+ mark = get_bbt_mark(bbt, block); -+ if (mark == BBT_BLOCK_RESERVED) -+ set_bbt_mark(bbt, block, BBT_BLOCK_GOOD); -+ block++; -+ bbt_blocks--; -+ } -+} -+ -+static int update_bbt(struct nandx_info *nand, struct bbt_desc *desc, -+ u8 *bbt, -+ int max_blocks) -+{ -+ int ret = 0, i; -+ -+ /* The reserved info is not stored in NAND*/ -+ unmark_bbt_region(nand, bbt, max_blocks); -+ -+ desc[0].version++; -+ for (i = 0; i < 2; i++) { -+ if (i > 0) -+ desc[i].version = desc[i - 1].version; -+ -+ ret = write_bbt(nand, &desc[i], &desc[1 - i], bbt, max_blocks); -+ if (ret) -+ break; -+ } -+ mark_bbt_region(nand, bbt, max_blocks); -+ -+ return ret; -+} -+ -+int scan_bbt(struct nandx_info *nand) -+{ -+ struct bbt_manager *manager = &g_bbt_manager; -+ struct bbt_desc *pdesc; -+ int total_block, len, i; -+ int valid_desc = 0; -+ int ret = 0; -+ u8 *bbt; -+ -+ total_block = div_down(nand->total_size, nand->block_size); -+ len = GET_BBT_LENGTH(total_block); -+ -+ if (!manager->bbt) { -+ manager->bbt = (u8 *)mem_alloc(1, len); -+ if (!manager->bbt) { -+ pr_info("%s, %d, mem alloc fail!!! len:%d\n", -+ __func__, __LINE__, len); -+ return -ENOMEM; -+ } -+ } -+ bbt = manager->bbt; -+ memset(bbt, 0xFF, len); -+ -+ /* scan bbt */ -+ for (i = 0; i < 2; i++) { -+ pdesc = &manager->desc[i]; -+ pdesc->bbt_addr = BBT_INVALID_ADDR; -+ pdesc->version = 0; -+ ret = search_bbt(nand, pdesc, manager->max_blocks); -+ if (!ret && (pdesc->bbt_addr != BBT_INVALID_ADDR)) -+ valid_desc += 1 << i; -+ } -+ -+ pdesc = &manager->desc[0]; -+ if ((valid_desc == 0x3) && (pdesc[0].version != pdesc[1].version)) -+ valid_desc = (pdesc[0].version > pdesc[1].version) ? 1 : 2; -+ -+ /* read bbt */ -+ for (i = 0; i < 2; i++) { -+ if (!(valid_desc & (1 << i))) -+ continue; -+ ret = read_bbt(&pdesc[i], bbt, len); -+ if (ret) { -+ pdesc->bbt_addr = BBT_INVALID_ADDR; -+ pdesc->version = 0; -+ valid_desc &= ~(1 << i); -+ } -+ /* If two BBT version is same, only need to read the first bbt*/ -+ if ((valid_desc == 0x3) && -+ (pdesc[0].version == pdesc[1].version)) -+ break; -+ } -+ -+ if (!valid_desc) { -+ create_bbt(nand, bbt); -+ pdesc[0].version = 1; -+ pdesc[1].version = 1; -+ } -+ -+ pdesc[0].version = max_t(u8, pdesc[0].version, pdesc[1].version); -+ pdesc[1].version = pdesc[0].version; -+ -+ for (i = 0; i < 2; i++) { -+ if (valid_desc & (1 << i)) -+ continue; -+ -+ ret = write_bbt(nand, &pdesc[i], &pdesc[1 - i], bbt, -+ manager->max_blocks); -+ if (ret) { -+ pr_info("write bbt(%d) fail, ret:%d\n", i, ret); -+ manager->bbt = NULL; -+ return ret; -+ } -+ } -+ -+ /* Prevent the bbt regions from erasing / writing */ -+ mark_bbt_region(nand, manager->bbt, manager->max_blocks); -+ -+ for (i = 0; i < total_block; i++) { -+ if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_WORN) -+ pr_info("Checked WORN bad blk: %d\n", i); -+ else if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_FACTORY_BAD) -+ pr_info("Checked Factory bad blk: %d\n", i); -+ else if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_RESERVED) -+ pr_info("Checked Reserved blk: %d\n", i); -+ else if (get_bbt_mark(manager->bbt, i) != BBT_BLOCK_GOOD) -+ pr_info("Checked unknown blk: %d\n", i); -+ } -+ -+ return 0; -+} -+ -+int bbt_mark_bad(struct nandx_info *nand, off_t offset) -+{ -+ struct bbt_manager *manager = &g_bbt_manager; -+ int block = div_down(offset, nand->block_size); -+ int ret = 0; -+ -+ mark_nand_bad(nand, block); -+ -+#if 0 -+ set_bbt_mark(manager->bbt, block, BBT_BLOCK_WORN); -+ -+ /* Update flash-based bad block table */ -+ ret = update_bbt(nand, manager->desc, manager->bbt, -+ manager->max_blocks); -+#endif -+ pr_info("block %d, update result %d.\n", block, ret); -+ -+ return ret; -+} -+ -+int bbt_is_bad(struct nandx_info *nand, off_t offset) -+{ -+ int block; -+ -+ block = div_down(offset, nand->block_size); -+ -+ return get_bbt_mark(g_bbt_manager.bbt, block) != BBT_BLOCK_GOOD; -+} ---- /dev/null -+++ b/drivers/mtd/nandx/driver/uboot/driver.c -@@ -0,0 +1,574 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "nandx_core.h" -+#include "nandx_util.h" -+#include "bbt.h" -+ -+typedef int (*func_nandx_operation)(u8 *, u8 *, u64, size_t); -+ -+struct nandx_clk { -+ struct clk *nfi_clk; -+ struct clk *ecc_clk; -+ struct clk *snfi_clk; -+ struct clk *snfi_clk_sel; -+ struct clk *snfi_parent_50m; -+}; -+ -+struct nandx_nfc { -+ struct nandx_info info; -+ struct nandx_clk clk; -+ struct nfi_resource *res; -+ -+ struct nand_chip *nand; -+ spinlock_t lock; -+}; -+ -+/* Default flash layout for MTK nand controller -+ * 64Bytes oob format. -+ */ -+static struct nand_ecclayout eccoob = { -+ .eccbytes = 42, -+ .eccpos = { -+ 17, 18, 19, 20, 21, 22, 23, 24, 25, -+ 26, 27, 28, 29, 30, 31, 32, 33, 34, -+ 35, 36, 37, 38, 39, 40, 41 -+ }, -+ .oobavail = 16, -+ .oobfree = { -+ { -+ .offset = 0, -+ .length = 16, -+ }, -+ } -+}; -+ -+static struct nandx_nfc *mtd_to_nfc(struct mtd_info *mtd) -+{ -+ struct nand_chip *nand = mtd_to_nand(mtd); -+ -+ return (struct nandx_nfc *)nand_get_controller_data(nand); -+} -+ -+static int nandx_enable_clk(struct nandx_clk *clk) -+{ -+ int ret; -+ -+ ret = clk_enable(clk->nfi_clk); -+ if (ret) { -+ pr_info("failed to enable nfi clk\n"); -+ return ret; -+ } -+ -+ ret = clk_enable(clk->ecc_clk); -+ if (ret) { -+ pr_info("failed to enable ecc clk\n"); -+ goto disable_nfi_clk; -+ } -+ -+ ret = clk_enable(clk->snfi_clk); -+ if (ret) { -+ pr_info("failed to enable snfi clk\n"); -+ goto disable_ecc_clk; -+ } -+ -+ ret = clk_enable(clk->snfi_clk_sel); -+ if (ret) { -+ pr_info("failed to enable snfi clk sel\n"); -+ goto disable_snfi_clk; -+ } -+ -+ ret = clk_set_parent(clk->snfi_clk_sel, clk->snfi_parent_50m); -+ if (ret) { -+ pr_info("failed to set snfi parent 50MHz\n"); -+ goto disable_snfi_clk; -+ } -+ -+ return 0; -+ -+disable_snfi_clk: -+ clk_disable(clk->snfi_clk); -+disable_ecc_clk: -+ clk_disable(clk->ecc_clk); -+disable_nfi_clk: -+ clk_disable(clk->nfi_clk); -+ -+ return ret; -+} -+ -+static void nandx_disable_clk(struct nandx_clk *clk) -+{ -+ clk_disable(clk->ecc_clk); -+ clk_disable(clk->nfi_clk); -+ clk_disable(clk->snfi_clk); -+} -+ -+static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oob_region) -+{ -+ struct nandx_nfc *nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ u32 eccsteps; -+ -+ eccsteps = div_down(mtd->writesize, mtd->ecc_step_size); -+ -+ if (section >= eccsteps) -+ return -EINVAL; -+ -+ oob_region->length = nfc->info.fdm_reg_size - nfc->info.fdm_ecc_size; -+ oob_region->offset = section * nfc->info.fdm_reg_size -+ + nfc->info.fdm_ecc_size; -+ -+ return 0; -+} -+ -+static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oob_region) -+{ -+ struct nandx_nfc *nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ u32 eccsteps; -+ -+ if (section) -+ return -EINVAL; -+ -+ eccsteps = div_down(mtd->writesize, mtd->ecc_step_size); -+ oob_region->offset = nfc->info.fdm_reg_size * eccsteps; -+ oob_region->length = mtd->oobsize - oob_region->offset; -+ -+ return 0; -+} -+ -+static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = { -+ .rfree = mtk_nfc_ooblayout_free, -+ .ecc = mtk_nfc_ooblayout_ecc, -+}; -+ -+struct nfc_compatible { -+ enum mtk_ic_version ic_ver; -+ -+ u32 clock_1x; -+ u32 *clock_2x; -+ int clock_2x_num; -+ -+ int min_oob_req; -+}; -+ -+static const struct nfc_compatible nfc_compats_mt7622 = { -+ .ic_ver = NANDX_MT7622, -+ .clock_1x = 26000000, -+ .clock_2x = NULL, -+ .clock_2x_num = 8, -+ .min_oob_req = 1, -+}; -+ -+static const struct udevice_id ic_of_match[] = { -+ {.compatible = "mediatek,mt7622-nfc", .data = &nfc_compats_mt7622}, -+ {} -+}; -+ -+static int nand_operation(struct mtd_info *mtd, loff_t addr, size_t len, -+ size_t *retlen, uint8_t *data, uint8_t *oob, bool read) -+{ -+ struct nandx_split64 split = {0}; -+ func_nandx_operation operation; -+ u64 block_oobs, val, align; -+ uint8_t *databuf, *oobbuf; -+ struct nandx_nfc *nfc; -+ bool readoob; -+ int ret = 0; -+ -+ nfc = (struct nandx_nfc *)nand_get_controller_data; -+ spin_lock(&nfc->lock); -+ -+ databuf = data; -+ oobbuf = oob; -+ -+ readoob = data ? false : true; -+ block_oobs = div_up(mtd->erasesize, mtd->writesize) * mtd->oobavail; -+ align = readoob ? block_oobs : mtd->erasesize; -+ -+ operation = read ? nandx_read : nandx_write; -+ -+ nandx_split(&split, addr, len, val, align); -+ -+ if (split.head_len) { -+ ret = operation((u8 *) databuf, oobbuf, addr, split.head_len); -+ -+ if (databuf) -+ databuf += split.head_len; -+ -+ if (oobbuf) -+ oobbuf += split.head_len; -+ -+ addr += split.head_len; -+ *retlen += split.head_len; -+ } -+ -+ if (split.body_len) { -+ while (div_up(split.body_len, align)) { -+ ret = operation((u8 *) databuf, oobbuf, addr, align); -+ -+ if (databuf) { -+ databuf += mtd->erasesize; -+ split.body_len -= mtd->erasesize; -+ *retlen += mtd->erasesize; -+ } -+ -+ if (oobbuf) { -+ oobbuf += block_oobs; -+ split.body_len -= block_oobs; -+ *retlen += block_oobs; -+ } -+ -+ addr += mtd->erasesize; -+ } -+ -+ } -+ -+ if (split.tail_len) { -+ ret = operation((u8 *) databuf, oobbuf, addr, split.tail_len); -+ *retlen += split.tail_len; -+ } -+ -+ spin_unlock(&nfc->lock); -+ -+ return ret; -+} -+ -+static int mtk_nand_read(struct mtd_info *mtd, loff_t from, size_t len, -+ size_t *retlen, u_char *buf) -+{ -+ return nand_operation(mtd, from, len, retlen, buf, NULL, true); -+} -+ -+static int mtk_nand_write(struct mtd_info *mtd, loff_t to, size_t len, -+ size_t *retlen, const u_char *buf) -+{ -+ return nand_operation(mtd, to, len, retlen, (uint8_t *)buf, -+ NULL, false); -+} -+ -+int mtk_nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) -+{ -+ size_t retlen; -+ -+ return nand_operation(mtd, from, ops->ooblen, &retlen, NULL, -+ ops->oobbuf, true); -+} -+ -+int mtk_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) -+{ -+ size_t retlen; -+ -+ return nand_operation(mtd, to, ops->ooblen, &retlen, NULL, -+ ops->oobbuf, false); -+} -+ -+static int mtk_nand_erase(struct mtd_info *mtd, struct erase_info *instr) -+{ -+ struct nandx_nfc *nfc; -+ u64 erase_len, erase_addr; -+ u32 block_size; -+ int ret = 0; -+ -+ nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ block_size = nfc->info.block_size; -+ erase_len = instr->len; -+ erase_addr = instr->addr; -+ spin_lock(&nfc->lock); -+ instr->state = MTD_ERASING; -+ -+ while (erase_len) { -+ if (mtk_nand_is_bad(mtd, erase_addr)) { -+ pr_info("block(0x%llx) is bad, not erase\n", -+ erase_addr); -+ instr->state = MTD_ERASE_FAILED; -+ goto erase_exit; -+ } else { -+ ret = nandx_erase(erase_addr, block_size); -+ if (ret < 0) { -+ instr->state = MTD_ERASE_FAILED; -+ goto erase_exit; -+ pr_info("erase fail at blk %llu, ret:%d\n", -+ erase_addr, ret); -+ } -+ } -+ erase_addr += block_size; -+ erase_len -= block_size; -+ } -+ -+ instr->state = MTD_ERASE_DONE; -+ -+erase_exit: -+ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; -+ -+ spin_unlock(&nfc->lock); -+ /* Do mtd call back function */ -+ if (!ret) -+ mtd_erase_callback(instr); -+ -+ return ret; -+} -+ -+int mtk_nand_is_bad(struct mtd_info *mtd, loff_t ofs) -+{ -+ struct nandx_nfc *nfc; -+ int ret; -+ -+ nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ spin_lock(&nfc->lock); -+ -+ /*ret = bbt_is_bad(&nfc->info, ofs);*/ -+ ret = nandx_is_bad_block(ofs); -+ spin_unlock(&nfc->lock); -+ -+ if (ret) { -+ pr_info("nand block 0x%x is bad, ret %d!\n", ofs, ret); -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+int mtk_nand_mark_bad(struct mtd_info *mtd, loff_t ofs) -+{ -+ struct nandx_nfc *nfc; -+ int ret; -+ -+ nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ spin_lock(&nfc->lock); -+ pr_info("%s, %d\n", __func__, __LINE__); -+ ret = bbt_mark_bad(&nfc->info, ofs); -+ -+ spin_unlock(&nfc->lock); -+ -+ return ret; -+} -+ -+void mtk_nand_sync(struct mtd_info *mtd) -+{ -+ nandx_sync(); -+} -+ -+static struct mtd_info *mtd_info_create(struct udevice *pdev, -+ struct nandx_nfc *nfc, struct nand_chip *nand) -+{ -+ struct mtd_info *mtd = nand_to_mtd(nand); -+ int ret; -+ -+ nand_set_controller_data(nand, nfc); -+ -+ nand->flash_node = dev_of_offset(pdev); -+ nand->ecc.layout = &eccoob; -+ -+ ret = nandx_ioctl(CORE_CTRL_NAND_INFO, &nfc->info); -+ if (ret) { -+ pr_info("fail to get nand info (%d)!\n", ret); -+ mem_free(mtd); -+ return NULL; -+ } -+ -+ mtd->owner = THIS_MODULE; -+ -+ mtd->name = "MTK-SNand"; -+ mtd->writesize = nfc->info.page_size; -+ mtd->erasesize = nfc->info.block_size; -+ mtd->oobsize = nfc->info.oob_size; -+ mtd->size = nfc->info.total_size; -+ mtd->type = MTD_NANDFLASH; -+ mtd->flags = MTD_CAP_NANDFLASH; -+ mtd->_erase = mtk_nand_erase; -+ mtd->_read = mtk_nand_read; -+ mtd->_write = mtk_nand_write; -+ mtd->_read_oob = mtk_nand_read_oob; -+ mtd->_write_oob = mtk_nand_write_oob; -+ mtd->_sync = mtk_nand_sync; -+ mtd->_lock = NULL; -+ mtd->_unlock = NULL; -+ mtd->_block_isbad = mtk_nand_is_bad; -+ mtd->_block_markbad = mtk_nand_mark_bad; -+ mtd->writebufsize = mtd->writesize; -+ -+ mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops); -+ -+ mtd->ecc_strength = nfc->info.ecc_strength; -+ mtd->ecc_step_size = nfc->info.sector_size; -+ -+ if (!mtd->bitflip_threshold) -+ mtd->bitflip_threshold = mtd->ecc_strength; -+ -+ return mtd; -+} -+ -+int board_nand_init(struct nand_chip *nand) -+{ -+ struct udevice *dev; -+ struct mtd_info *mtd; -+ struct nandx_nfc *nfc; -+ int arg = 1; -+ int ret; -+ -+ ret = uclass_get_device_by_driver(UCLASS_MTD, -+ DM_GET_DRIVER(mtk_snand_drv), -+ &dev); -+ if (ret) { -+ pr_err("Failed to get mtk_nand_drv. (error %d)\n", ret); -+ return ret; -+ } -+ -+ nfc = dev_get_priv(dev); -+ -+ ret = nandx_enable_clk(&nfc->clk); -+ if (ret) { -+ pr_err("failed to enable nfi clk (error %d)\n", ret); -+ return ret; -+ } -+ -+ ret = nandx_init(nfc->res); -+ if (ret) { -+ pr_err("nandx init error (%d)!\n", ret); -+ goto disable_clk; -+ } -+ -+ arg = 1; -+ nandx_ioctl(NFI_CTRL_DMA, &arg); -+ nandx_ioctl(NFI_CTRL_ECC, &arg); -+ -+#ifdef NANDX_UNIT_TEST -+ nandx_unit_test(0x780000, 0x800); -+#endif -+ -+ mtd = mtd_info_create(dev, nfc, nand); -+ if (!mtd) { -+ ret = -ENOMEM; -+ goto disable_clk; -+ } -+ -+ spin_lock_init(&nfc->lock); -+#if 0 -+ ret = scan_bbt(&nfc->info); -+ if (ret) { -+ pr_info("bbt init error (%d)!\n", ret); -+ goto disable_clk; -+ } -+#endif -+ return ret; -+ -+disable_clk: -+ nandx_disable_clk(&nfc->clk); -+ -+ return ret; -+} -+ -+static int mtk_snand_ofdata_to_platdata(struct udevice *dev) -+{ -+ struct nandx_nfc *nfc = dev_get_priv(dev); -+ struct nfc_compatible *compat; -+ struct nfi_resource *res; -+ -+ int ret = 0; -+ -+ res = mem_alloc(1, sizeof(struct nfi_resource)); -+ if (!res) -+ return -ENOMEM; -+ -+ nfc->res = res; -+ -+ res->nfi_regs = (void *)dev_read_addr_index(dev, 0); -+ res->ecc_regs = (void *)dev_read_addr_index(dev, 1); -+ pr_debug("mtk snand nfi_regs:0x%x ecc_regs:0x%x\n", -+ res->nfi_regs, res->ecc_regs); -+ -+ compat = (struct nfc_compatible *)dev_get_driver_data(dev); -+ -+ res->ic_ver = (enum mtk_ic_version)(compat->ic_ver); -+ res->clock_1x = compat->clock_1x; -+ res->clock_2x = compat->clock_2x; -+ res->clock_2x_num = compat->clock_2x_num; -+ -+ memset(&nfc->clk, 0, sizeof(struct nandx_clk)); -+ nfc->clk.nfi_clk = -+ kmalloc(sizeof(*nfc->clk.nfi_clk), GFP_KERNEL); -+ nfc->clk.ecc_clk = -+ kmalloc(sizeof(*nfc->clk.ecc_clk), GFP_KERNEL); -+ nfc->clk.snfi_clk= -+ kmalloc(sizeof(*nfc->clk.snfi_clk), GFP_KERNEL); -+ nfc->clk.snfi_clk_sel = -+ kmalloc(sizeof(*nfc->clk.snfi_clk_sel), GFP_KERNEL); -+ nfc->clk.snfi_parent_50m = -+ kmalloc(sizeof(*nfc->clk.snfi_parent_50m), GFP_KERNEL); -+ -+ if (!nfc->clk.nfi_clk || !nfc->clk.ecc_clk || !nfc->clk.snfi_clk || -+ !nfc->clk.snfi_clk_sel || !nfc->clk.snfi_parent_50m) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "nfi_clk", nfc->clk.nfi_clk); -+ if (IS_ERR(nfc->clk.nfi_clk)) { -+ ret = PTR_ERR(nfc->clk.nfi_clk); -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "ecc_clk", nfc->clk.ecc_clk); -+ if (IS_ERR(nfc->clk.ecc_clk)) { -+ ret = PTR_ERR(nfc->clk.ecc_clk); -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "snfi_clk", nfc->clk.snfi_clk); -+ if (IS_ERR(nfc->clk.snfi_clk)) { -+ ret = PTR_ERR(nfc->clk.snfi_clk); -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "spinfi_sel", nfc->clk.snfi_clk_sel); -+ if (IS_ERR(nfc->clk.snfi_clk_sel)) { -+ ret = PTR_ERR(nfc->clk.snfi_clk_sel); -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "spinfi_parent_50m", nfc->clk.snfi_parent_50m); -+ if (IS_ERR(nfc->clk.snfi_parent_50m)) -+ pr_info("spinfi parent 50MHz is not configed\n"); -+ -+ return 0; -+err: -+ if (nfc->clk.nfi_clk) -+ kfree(nfc->clk.nfi_clk); -+ if (nfc->clk.snfi_clk) -+ kfree(nfc->clk.snfi_clk); -+ if (nfc->clk.ecc_clk) -+ kfree(nfc->clk.ecc_clk); -+ if (nfc->clk.snfi_clk_sel) -+ kfree(nfc->clk.snfi_clk_sel); -+ if (nfc->clk.snfi_parent_50m) -+ kfree(nfc->clk.snfi_parent_50m); -+ -+ return ret; -+} -+ -+U_BOOT_DRIVER(mtk_snand_drv) = { -+ .name = "mtk_snand", -+ .id = UCLASS_MTD, -+ .of_match = ic_of_match, -+ .ofdata_to_platdata = mtk_snand_ofdata_to_platdata, -+ .priv_auto_alloc_size = sizeof(struct nandx_nfc), -+}; -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("MTK Nand Flash Controller Driver"); -+MODULE_AUTHOR("MediaTek"); ---- /dev/null -+++ b/drivers/mtd/nandx/include/Nandx.mk -@@ -0,0 +1,16 @@ -+# -+# Copyright (C) 2017 MediaTek Inc. -+# Licensed under either -+# BSD Licence, (see NOTICE for more details) -+# GNU General Public License, version 2.0, (see NOTICE for more details) -+# -+ -+nandx-header-y += internal/nandx_core.h -+nandx-header-y += internal/nandx_errno.h -+nandx-header-y += internal/nandx_util.h -+nandx-header-$(NANDX_BBT_SUPPORT) += internal/bbt.h -+nandx-header-$(NANDX_SIMULATOR_SUPPORT) += simulator/nandx_os.h -+nandx-header-$(NANDX_CTP_SUPPORT) += ctp/nandx_os.h -+nandx-header-$(NANDX_LK_SUPPORT) += lk/nandx_os.h -+nandx-header-$(NANDX_KERNEL_SUPPORT) += kernel/nandx_os.h -+nandx-header-$(NANDX_UBOOT_SUPPORT) += uboot/nandx_os.h ---- /dev/null -+++ b/drivers/mtd/nandx/include/internal/bbt.h -@@ -0,0 +1,62 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __BBT_H__ -+#define __BBT_H__ -+ -+#define BBT_BLOCK_GOOD 0x03 -+#define BBT_BLOCK_WORN 0x02 -+#define BBT_BLOCK_RESERVED 0x01 -+#define BBT_BLOCK_FACTORY_BAD 0x00 -+ -+#define BBT_INVALID_ADDR 0 -+/* The maximum number of blocks to scan for a bbt */ -+#define NAND_BBT_SCAN_MAXBLOCKS 4 -+#define NAND_BBT_USE_FLASH 0x00020000 -+#define NAND_BBT_NO_OOB 0x00040000 -+ -+/* Search good / bad pattern on the first and the second page */ -+#define NAND_BBT_SCAN2NDPAGE 0x00008000 -+/* Search good / bad pattern on the last page of the eraseblock */ -+#define NAND_BBT_SCANLASTPAGE 0x00010000 -+ -+#define NAND_DRAM_BUF_DATABUF_ADDR (NAND_BUF_ADDR) -+ -+struct bbt_pattern { -+ u8 *data; -+ int len; -+}; -+ -+struct bbt_desc { -+ struct bbt_pattern pattern; -+ u8 version; -+ u64 bbt_addr;/*0: invalid value; otherwise, valid value*/ -+}; -+ -+struct bbt_manager { -+ /* main bbt descriptor and mirror descriptor */ -+ struct bbt_desc desc[2];/* 0: main bbt; 1: mirror bbt */ -+ int max_blocks; -+ u8 *bbt; -+}; -+ -+#define BBT_ENTRY_MASK 0x03 -+#define BBT_ENTRY_SHIFT 2 -+ -+#define GET_BBT_LENGTH(blocks) (blocks >> 2) -+#define GET_ENTRY(block) ((block) >> BBT_ENTRY_SHIFT) -+#define GET_POSITION(block) (((block) & BBT_ENTRY_MASK) * 2) -+#define GET_MARK_VALUE(block, mark) \ -+ (((mark) & BBT_ENTRY_MASK) << GET_POSITION(block)) -+ -+int scan_bbt(struct nandx_info *nand); -+ -+int bbt_mark_bad(struct nandx_info *nand, off_t offset); -+ -+int bbt_is_bad(struct nandx_info *nand, off_t offset); -+ -+#endif /*__BBT_H__*/ ---- /dev/null -+++ b/drivers/mtd/nandx/include/internal/nandx_core.h -@@ -0,0 +1,250 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NANDX_CORE_H__ -+#define __NANDX_CORE_H__ -+ -+/** -+ * mtk_ic_version - indicates specifical IC, IP need this to load some info -+ */ -+enum mtk_ic_version { -+ NANDX_MT7622, -+}; -+ -+/** -+ * nandx_ioctl_cmd - operations supported by nandx -+ * -+ * @NFI_CTRL_DMA dma enable or not -+ * @NFI_CTRL_NFI_MODE customer/read/program/erase... -+ * @NFI_CTRL_ECC ecc enable or not -+ * @NFI_CTRL_ECC_MODE nfi/dma/pio -+ * @CHIP_CTRL_DRIVE_STRENGTH enum chip_ctrl_drive_strength -+ */ -+enum nandx_ctrl_cmd { -+ CORE_CTRL_NAND_INFO, -+ -+ NFI_CTRL_DMA, -+ NFI_CTRL_NFI_MODE, -+ NFI_CTRL_AUTOFORMAT, -+ NFI_CTRL_NFI_IRQ, -+ NFI_CTRL_PAGE_IRQ, -+ NFI_CTRL_RANDOMIZE, -+ NFI_CTRL_BAD_MARK_SWAP, -+ -+ NFI_CTRL_ECC, -+ NFI_CTRL_ECC_MODE, -+ NFI_CTRL_ECC_CLOCK, -+ NFI_CTRL_ECC_IRQ, -+ NFI_CTRL_ECC_PAGE_IRQ, -+ NFI_CTRL_ECC_DECODE_MODE, -+ -+ SNFI_CTRL_OP_MODE, -+ SNFI_CTRL_RX_MODE, -+ SNFI_CTRL_TX_MODE, -+ SNFI_CTRL_DELAY_MODE, -+ -+ CHIP_CTRL_OPS_CACHE, -+ CHIP_CTRL_OPS_MULTI, -+ CHIP_CTRL_PSLC_MODE, -+ CHIP_CTRL_DRIVE_STRENGTH, -+ CHIP_CTRL_DDR_MODE, -+ CHIP_CTRL_ONDIE_ECC, -+ CHIP_CTRL_TIMING_MODE -+}; -+ -+enum snfi_ctrl_op_mode { -+ SNFI_CUSTOM_MODE, -+ SNFI_AUTO_MODE, -+ SNFI_MAC_MODE -+}; -+ -+enum snfi_ctrl_rx_mode { -+ SNFI_RX_111, -+ SNFI_RX_112, -+ SNFI_RX_114, -+ SNFI_RX_122, -+ SNFI_RX_144 -+}; -+ -+enum snfi_ctrl_tx_mode { -+ SNFI_TX_111, -+ SNFI_TX_114, -+}; -+ -+enum chip_ctrl_drive_strength { -+ CHIP_DRIVE_NORMAL, -+ CHIP_DRIVE_HIGH, -+ CHIP_DRIVE_MIDDLE, -+ CHIP_DRIVE_LOW -+}; -+ -+enum chip_ctrl_timing_mode { -+ CHIP_TIMING_MODE0, -+ CHIP_TIMING_MODE1, -+ CHIP_TIMING_MODE2, -+ CHIP_TIMING_MODE3, -+ CHIP_TIMING_MODE4, -+ CHIP_TIMING_MODE5, -+}; -+ -+/** -+ * nandx_info - basic information -+ */ -+struct nandx_info { -+ u32 max_io_count; -+ u32 min_write_pages; -+ u32 plane_num; -+ u32 oob_size; -+ u32 page_parity_size; -+ u32 page_size; -+ u32 block_size; -+ u64 total_size; -+ u32 fdm_reg_size; -+ u32 fdm_ecc_size; -+ u32 ecc_strength; -+ u32 sector_size; -+}; -+ -+/** -+ * nfi_resource - the resource needed by nfi & ecc to do initialization -+ */ -+struct nfi_resource { -+ int ic_ver; -+ void *dev; -+ -+ void *ecc_regs; -+ int ecc_irq_id; -+ -+ void *nfi_regs; -+ int nfi_irq_id; -+ -+ u32 clock_1x; -+ u32 *clock_2x; -+ int clock_2x_num; -+ -+ int min_oob_req; -+}; -+ -+/** -+ * nandx_init - init all related modules below -+ * -+ * @res: basic resource of the project -+ * -+ * return 0 if init success, otherwise return negative error code -+ */ -+int nandx_init(struct nfi_resource *res); -+ -+/** -+ * nandx_exit - release resource those that obtained in init flow -+ */ -+void nandx_exit(void); -+ -+/** -+ * nandx_read - read data from nand this function can read data and related -+ * oob from specifical address -+ * if do multi_ops, set one operation per time, and call nandx_sync at last -+ * in multi mode, not support page partial read -+ * oob not support partial read -+ * -+ * @data: buf to receive data from nand -+ * @oob: buf to receive oob data from nand which related to data page -+ * length of @oob should oob size aligned, oob not support partial read -+ * @offset: offset address on the whole flash -+ * @len: the length of @data that need to read -+ * -+ * if read success return 0, otherwise return negative error code -+ */ -+int nandx_read(u8 *data, u8 *oob, u64 offset, size_t len); -+ -+/** -+ * nandx_write - write data to nand -+ * this function can write data and related oob to specifical address -+ * if do multi_ops, set one operation per time, and call nandx_sync at last -+ * -+ * @data: source data to be written to nand, -+ * for multi operation, the length of @data should be page size aliged -+ * @oob: source oob which related to data page to be written to nand, -+ * length of @oob should oob size aligned -+ * @offset: offset address on the whole flash, the value should be start address -+ * of a page -+ * @len: the length of @data that need to write, -+ * for multi operation, the len should be page size aliged -+ * -+ * if write success return 0, otherwise return negative error code -+ * if return value > 0, it indicates that how many pages still need to write, -+ * and data has not been written to nand -+ * please call nandx_sync after pages alligned $nandx_info.min_write_pages -+ */ -+int nandx_write(u8 *data, u8 *oob, u64 offset, size_t len); -+ -+/** -+ * nandx_erase - erase an area of nand -+ * if do multi_ops, set one operation per time, and call nandx_sync at last -+ * -+ * @offset: offset address on the flash -+ * @len: erase length which should be block size aligned -+ * -+ * if erase success return 0, otherwise return negative error code -+ */ -+int nandx_erase(u64 offset, size_t len); -+ -+/** -+ * nandx_sync - sync all operations to nand -+ * when do multi_ops, this function will be called at last operation -+ * when write data, if number of pages not alligned -+ * by $nandx_info.min_write_pages, this interface could be called to do -+ * force write, 0xff will be padded to blanked pages. -+ */ -+int nandx_sync(void); -+ -+/** -+ * nandx_is_bad_block - check if the block is bad -+ * only check the flag that marked by the flash vendor -+ * -+ * @offset: offset address on the whole flash -+ * -+ * return true if the block is bad, otherwise return false -+ */ -+bool nandx_is_bad_block(u64 offset); -+ -+/** -+ * nandx_ioctl - set/get property of nand chip -+ * -+ * @cmd: parameter that defined in enum nandx_ioctl_cmd -+ * @arg: operate parameter -+ * -+ * return 0 if operate success, otherwise return negative error code -+ */ -+int nandx_ioctl(int cmd, void *arg); -+ -+/** -+ * nandx_suspend - suspend nand, and store some data -+ * -+ * return 0 if suspend success, otherwise return negative error code -+ */ -+int nandx_suspend(void); -+ -+/** -+ * nandx_resume - resume nand, and replay some data -+ * -+ * return 0 if resume success, otherwise return negative error code -+ */ -+int nandx_resume(void); -+ -+#ifdef NANDX_UNIT_TEST -+/** -+ * nandx_unit_test - unit test -+ * -+ * @offset: offset address on the whole flash -+ * @len: should be not larger than a block size, we only test a block per time -+ * -+ * return 0 if test success, otherwise return negative error code -+ */ -+int nandx_unit_test(u64 offset, size_t len); -+#endif -+ -+#endif /* __NANDX_CORE_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/include/internal/nandx_errno.h -@@ -0,0 +1,40 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NANDX_ERRNO_H__ -+#define __NANDX_ERRNO_H__ -+ -+#ifndef EIO -+#define EIO 5 /* I/O error */ -+#define ENOMEM 12 /* Out of memory */ -+#define EFAULT 14 /* Bad address */ -+#define EBUSY 16 /* Device or resource busy */ -+#define ENODEV 19 /* No such device */ -+#define EINVAL 22 /* Invalid argument */ -+#define ENOSPC 28 /* No space left on device */ -+/* Operation not supported on transport endpoint */ -+#define EOPNOTSUPP 95 -+#define ETIMEDOUT 110 /* Connection timed out */ -+#endif -+ -+#define ENANDFLIPS 1024 /* Too many bitflips, uncorrected */ -+#define ENANDREAD 1025 /* Read fail, can't correct */ -+#define ENANDWRITE 1026 /* Write fail */ -+#define ENANDERASE 1027 /* Erase fail */ -+#define ENANDBAD 1028 /* Bad block */ -+#define ENANDWP 1029 -+ -+#define IS_NAND_ERR(err) ((err) >= -ENANDBAD && (err) <= -ENANDFLIPS) -+ -+#ifndef MAX_ERRNO -+#define MAX_ERRNO 4096 -+#define ERR_PTR(errno) ((void *)((long)errno)) -+#define PTR_ERR(ptr) ((long)(ptr)) -+#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)-MAX_ERRNO) -+#endif -+ -+#endif /* __NANDX_ERRNO_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/include/internal/nandx_util.h -@@ -0,0 +1,221 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NANDX_UTIL_H__ -+#define __NANDX_UTIL_H__ -+ -+typedef unsigned char u8; -+typedef unsigned short u16; -+typedef unsigned int u32; -+typedef unsigned long long u64; -+ -+enum nand_irq_return { -+ NAND_IRQ_NONE, -+ NAND_IRQ_HANDLED, -+}; -+ -+enum nand_dma_operation { -+ NDMA_FROM_DEV, -+ NDMA_TO_DEV, -+}; -+ -+ -+/* -+ * Compatible function -+ * used for preloader/lk/kernel environment -+ */ -+#include "nandx_os.h" -+#include "nandx_errno.h" -+ -+#ifndef BIT -+#define BIT(a) (1 << (a)) -+#endif -+ -+#ifndef min_t -+#define min_t(type, x, y) ({ \ -+ type __min1 = (x); \ -+ type __min2 = (y); \ -+ __min1 < __min2 ? __min1 : __min2; }) -+ -+#define max_t(type, x, y) ({ \ -+ type __max1 = (x); \ -+ type __max2 = (y); \ -+ __max1 > __max2 ? __max1 : __max2; }) -+#endif -+ -+#ifndef GENMASK -+#define GENMASK(h, l) \ -+ (((~0UL) << (l)) & (~0UL >> ((sizeof(unsigned long) * 8) - 1 - (h)))) -+#endif -+ -+#ifndef __weak -+#define __weak __attribute__((__weak__)) -+#endif -+ -+#ifndef __packed -+#define __packed __attribute__((__packed__)) -+#endif -+ -+#ifndef KB -+#define KB(x) ((x) << 10) -+#define MB(x) (KB(x) << 10) -+#define GB(x) (MB(x) << 10) -+#endif -+ -+#ifndef offsetof -+#define offsetof(type, member) ((size_t)&((type *)0)->member) -+#endif -+ -+#ifndef NULL -+#define NULL (void *)0 -+#endif -+static inline u32 nandx_popcount(u32 x) -+{ -+ x = (x & 0x55555555) + ((x >> 1) & 0x55555555); -+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333); -+ x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F); -+ x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF); -+ x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF); -+ -+ return x; -+} -+ -+#ifndef zero_popcount -+#define zero_popcount(x) (32 - nandx_popcount(x)) -+#endif -+ -+#ifndef do_div -+#define do_div(n, base) \ -+ ({ \ -+ u32 __base = (base); \ -+ u32 __rem; \ -+ __rem = ((u64)(n)) % __base; \ -+ (n) = ((u64)(n)) / __base; \ -+ __rem; \ -+ }) -+#endif -+ -+#define div_up(x, y) \ -+ ({ \ -+ u64 __temp = ((x) + (y) - 1); \ -+ do_div(__temp, (y)); \ -+ __temp; \ -+ }) -+ -+#define div_down(x, y) \ -+ ({ \ -+ u64 __temp = (x); \ -+ do_div(__temp, (y)); \ -+ __temp; \ -+ }) -+ -+#define div_round_up(x, y) (div_up(x, y) * (y)) -+#define div_round_down(x, y) (div_down(x, y) * (y)) -+ -+#define reminder(x, y) \ -+ ({ \ -+ u64 __temp = (x); \ -+ do_div(__temp, (y)); \ -+ }) -+ -+#ifndef round_up -+#define round_up(x, y) ((((x) - 1) | ((y) - 1)) + 1) -+#define round_down(x, y) ((x) & ~((y) - 1)) -+#endif -+ -+#ifndef readx_poll_timeout_atomic -+#define readx_poll_timeout_atomic(op, addr, val, cond, delay_us, timeout_us) \ -+ ({ \ -+ u64 end = get_current_time_us() + timeout_us; \ -+ for (;;) { \ -+ u64 now = get_current_time_us(); \ -+ (val) = op(addr); \ -+ if (cond) \ -+ break; \ -+ if (now > end) { \ -+ (val) = op(addr); \ -+ break; \ -+ } \ -+ } \ -+ (cond) ? 0 : -ETIMEDOUT; \ -+ }) -+ -+#define readl_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ -+ readx_poll_timeout_atomic(readl, addr, val, cond, delay_us, timeout_us) -+#define readw_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ -+ readx_poll_timeout_atomic(readw, addr, val, cond, delay_us, timeout_us) -+#define readb_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ -+ readx_poll_timeout_atomic(readb, addr, val, cond, delay_us, timeout_us) -+#endif -+ -+struct nandx_split64 { -+ u64 head; -+ size_t head_len; -+ u64 body; -+ size_t body_len; -+ u64 tail; -+ size_t tail_len; -+}; -+ -+struct nandx_split32 { -+ u32 head; -+ u32 head_len; -+ u32 body; -+ u32 body_len; -+ u32 tail; -+ u32 tail_len; -+}; -+ -+#define nandx_split(split, offset, len, val, align) \ -+ do { \ -+ (split)->head = (offset); \ -+ (val) = div_round_down((offset), (align)); \ -+ (val) = (align) - ((offset) - (val)); \ -+ if ((val) == (align)) \ -+ (split)->head_len = 0; \ -+ else if ((val) > (len)) \ -+ (split)->head_len = len; \ -+ else \ -+ (split)->head_len = val; \ -+ (split)->body = (offset) + (split)->head_len; \ -+ (split)->body_len = div_round_down((len) - \ -+ (split)->head_len,\ -+ (align)); \ -+ (split)->tail = (split)->body + (split)->body_len; \ -+ (split)->tail_len = (len) - (split)->head_len - \ -+ (split)->body_len; \ -+ } while (0) -+ -+#ifndef container_of -+#define container_of(ptr, type, member) \ -+ ({const __typeof__(((type *)0)->member) * __mptr = (ptr); \ -+ (type *)((char *)__mptr - offsetof(type, member)); }) -+#endif -+ -+static inline u32 nandx_cpu_to_be32(u32 val) -+{ -+ u32 temp = 1; -+ u8 *p_temp = (u8 *)&temp; -+ -+ if (*p_temp) -+ return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | -+ ((val >> 8) & 0xff00) | ((val >> 24) & 0xff); -+ -+ return val; -+} -+ -+static inline void nandx_set_bits32(unsigned long addr, u32 mask, -+ u32 val) -+{ -+ u32 temp = readl((void *)addr); -+ -+ temp &= ~(mask); -+ temp |= val; -+ writel(temp, (void *)addr); -+} -+ -+#endif /* __NANDX_UTIL_H__ */ ---- /dev/null -+++ b/drivers/mtd/nandx/include/uboot/nandx_os.h -@@ -0,0 +1,78 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NANDX_OS_H__ -+#define __NANDX_OS_H__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define NANDX_BULK_IO_USE_DRAM 0 -+ -+#define nandx_event_create() NULL -+#define nandx_event_destroy(event) -+#define nandx_event_complete(event) -+#define nandx_event_init(event) -+#define nandx_event_wait_complete(event, timeout) true -+ -+#define nandx_irq_register(dev, irq, irq_handler, name, data) NULL -+ -+static inline void *mem_alloc(u32 count, u32 size) -+{ -+ return kmalloc(count * size, GFP_KERNEL | __GFP_ZERO); -+} -+ -+static inline void mem_free(void *mem) -+{ -+ kfree(mem); -+} -+ -+static inline u64 get_current_time_us(void) -+{ -+ return timer_get_us(); -+} -+ -+static inline u32 nandx_dma_map(void *dev, void *buf, u64 len, -+ enum nand_dma_operation op) -+{ -+ unsigned long addr = (unsigned long)buf; -+ u64 size; -+ -+ size = ALIGN(len, ARCH_DMA_MINALIGN); -+ -+ if (op == NDMA_FROM_DEV) -+ invalidate_dcache_range(addr, addr + size); -+ else -+ flush_dcache_range(addr, addr + size); -+ -+ return addr; -+} -+ -+static inline void nandx_dma_unmap(void *dev, void *buf, void *addr, -+ u64 len, enum nand_dma_operation op) -+{ -+ u64 size; -+ -+ size = ALIGN(len, ARCH_DMA_MINALIGN); -+ -+ if (op != NDMA_FROM_DEV) -+ invalidate_dcache_range((unsigned long)addr, addr + size); -+ else -+ flush_dcache_range((unsigned long)addr, addr + size); -+ -+ return addr; -+} -+ -+#endif /* __NANDX_OS_H__ */ ---- a/include/configs/mt7622.h -+++ b/include/configs/mt7622.h -@@ -11,6 +11,31 @@ - - #include - -+/* SPI Nand */ -+#if defined(CONFIG_MTD_RAW_NAND) -+#define CONFIG_SYS_MAX_NAND_DEVICE 1 -+#define CONFIG_SYS_NAND_BASE 0x1100d000 -+ -+#define ENV_BOOT_READ_IMAGE \ -+ "boot_rd_img=" \ -+ "nand read 0x4007ff28 0x380000 0x1400000" \ -+ ";iminfo 0x4007ff28 \0" -+ -+#define ENV_BOOT_WRITE_IMAGE \ -+ "boot_wr_img=" \ -+ "nand write 0x4007ff28 0x380000 0x1400000" \ -+ ";iminfo 0x4007ff28 \0" -+ -+#define ENV_BOOT_CMD \ -+ "mtk_boot=run boot_rd_img;bootm;\0" -+ -+#define CONFIG_EXTRA_ENV_SETTINGS \ -+ ENV_BOOT_READ_IMAGE \ -+ ENV_BOOT_CMD \ -+ "bootcmd=run mtk_boot;\0" -+ -+#endif -+ - #define CONFIG_SYS_MAXARGS 8 - #define CONFIG_SYS_BOOTM_LEN SZ_64M - #define CONFIG_SYS_CBSIZE SZ_1K diff --git a/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch b/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch deleted file mode 100644 index 7167a498ad..0000000000 --- a/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch +++ /dev/null @@ -1,57 +0,0 @@ -From b1b3c3d2ce62872c8dec4a7d645af6b3c565e094 Mon Sep 17 00:00:00 2001 -From: Sam Shih -Date: Mon, 20 Apr 2020 17:11:32 +0800 -Subject: [PATCH 2/3] mt7622 uboot: add dts and config for spi nand - -This patch add dts and config for mt7622 spi nand - -Signed-off-by: Xiangsheng Hou ---- - arch/arm/dts/mt7622-rfb.dts | 6 ++++++ - arch/arm/dts/mt7622.dtsi | 20 ++++++++++++++++++++ - 2 files changed, 26 insertions(+) - ---- a/arch/arm/dts/mt7622-rfb.dts -+++ b/arch/arm/dts/mt7622-rfb.dts -@@ -174,6 +174,12 @@ - }; - }; - -+&nandc { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&snfi_pins>; -+ status = "okay"; -+}; -+ - &uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_pins>; ---- a/arch/arm/dts/mt7622.dtsi -+++ b/arch/arm/dts/mt7622.dtsi -@@ -53,6 +53,26 @@ - #size-cells = <0>; - }; - -+ nandc: nfi@1100d000 { -+ compatible = "mediatek,mt7622-nfc"; -+ reg = <0x1100d000 0x1000>, -+ <0x1100e000 0x1000>; -+ interrupts = , -+ ; -+ clocks = <&pericfg CLK_PERI_NFI_PD>, -+ <&pericfg CLK_PERI_NFIECC_PD>, -+ <&pericfg CLK_PERI_SNFI_PD>, -+ <&topckgen CLK_TOP_NFI_INFRA_SEL>, -+ <&topckgen CLK_TOP_UNIVPLL2_D8>; -+ clock-names = "nfi_clk", -+ "ecc_clk", -+ "snfi_clk", -+ "spinfi_sel", -+ "spinfi_parent_50m"; -+ nand-ecc-mode = "hw"; -+ status = "disabled"; -+ }; -+ - timer { - compatible = "arm,armv8-timer"; - interrupt-parent = <&gic>; diff --git a/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch b/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch deleted file mode 100644 index 6999e5e235..0000000000 --- a/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch +++ /dev/null @@ -1,22 +0,0 @@ -From e5745143a2984cf44fbfc0b3aedb49e57873f109 Mon Sep 17 00:00:00 2001 -From: Sam Shih -Date: Mon, 20 Apr 2020 17:17:04 +0800 -Subject: [PATCH 3/3] configs: enable mtd and mtk_spi_nand in defconfig - -This patch enable mtk and mtk_spi_nand in mt7622_rfb defconfig - -Signed-off-by: Sam Shih ---- - configs/mt7622_rfb_defconfig | 5 +++++ - 1 file changed, 5 insertions(+) - ---- a/configs/mt7622_rfb_defconfig -+++ b/configs/mt7622_rfb_defconfig -@@ -13,6 +13,7 @@ CONFIG_DEFAULT_FDT_FILE="mt7622-rfb" - CONFIG_SYS_PROMPT="MT7622> " - CONFIG_CMD_BOOTMENU=y - CONFIG_CMD_MMC=y -+CONFIG_CMD_NAND=y - CONFIG_CMD_PCI=y - CONFIG_CMD_SF_TEST=y - CONFIG_CMD_PING=y diff --git a/package/boot/uboot-mediatek/patches/006-cmd-button-return-button-status.patch b/package/boot/uboot-mediatek/patches/006-cmd-button-return-button-status.patch deleted file mode 100644 index a413688f1c..0000000000 --- a/package/boot/uboot-mediatek/patches/006-cmd-button-return-button-status.patch +++ /dev/null @@ -1,38 +0,0 @@ -From a6bfd71a96201127836d59736abcb54dc2d5e1a5 Mon Sep 17 00:00:00 2001 -From: Heinrich Schuchardt -Date: Mon, 14 Sep 2020 12:50:56 +0200 -Subject: [PATCH] cmd/button: return button status - -To make the button command useful in a shell script it should return the -status of the button: - -* 0 (true) - pressed, on -* 1 (false) - not pressed, off - -The button command takes only one argument. Correct maxargs. - -Adjust the Python unit test. - -Signed-off-by: Heinrich Schuchardt -Reviewed-by: Philippe Reynes ---- - cmd/button.c | 4 ++-- - test/py/tests/test_button.py | 34 ++++++++++++++++++++++++++-------- - 2 files changed, 28 insertions(+), 10 deletions(-) - ---- a/cmd/button.c -+++ b/cmd/button.c -@@ -75,11 +75,11 @@ int do_button(struct cmd_tbl *cmdtp, int - - ret = show_button_state(dev); - -- return 0; -+ return !ret; - } - - U_BOOT_CMD( -- button, 4, 1, do_button, -+ button, 2, 1, do_button, - "manage buttons", - " \tGet button state\n" - "button list\t\tShow a list of buttons" diff --git a/package/boot/uboot-mediatek/patches/010-ahci-mediatek-fix-missing-dev_err-definition.patch b/package/boot/uboot-mediatek/patches/010-ahci-mediatek-fix-missing-dev_err-definition.patch new file mode 100644 index 0000000000..5e2b4aa39b --- /dev/null +++ b/package/boot/uboot-mediatek/patches/010-ahci-mediatek-fix-missing-dev_err-definition.patch @@ -0,0 +1,24 @@ +From 7089c413216f1c0e374d71187030fe41ae4b3071 Mon Sep 17 00:00:00 2001 +From: Frank Wunderlich +Date: Tue, 3 Nov 2020 19:45:30 +0100 +Subject: [PATCH] ahci: mediatek: fix missing dev_err definition + +--- + drivers/ata/mtk_ahci.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/ata/mtk_ahci.c b/drivers/ata/mtk_ahci.c +index 554175bc00..2c5227df30 100644 +--- a/drivers/ata/mtk_ahci.c ++++ b/drivers/ata/mtk_ahci.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #define SYS_CFG 0x14 + #define SYS_CFG_SATA_MSK GENMASK(31, 30) +-- +2.30.2 + diff --git a/package/boot/uboot-mediatek/patches/010-no-binman.patch b/package/boot/uboot-mediatek/patches/010-no-binman.patch deleted file mode 100644 index 7071a6c410..0000000000 --- a/package/boot/uboot-mediatek/patches/010-no-binman.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- a/Makefile -+++ b/Makefile -@@ -1716,6 +1716,10 @@ u-boot-elf.lds: arch/u-boot-elf.lds prep - - ifeq ($(CONFIG_SPL),y) - spl/u-boot-spl-mtk.bin: spl/u-boot-spl -+OBJCOPYFLAGS_u-boot-mtk.bin = -I binary -O binary \ -+ --pad-to=$(CONFIG_SPL_PAD_TO) --gap-fill=0xff -+u-boot-mtk.bin: u-boot.img spl/u-boot-spl-mtk.bin FORCE -+ $(call if_changed,pad_cat) - else - MKIMAGEFLAGS_u-boot-mtk.bin = -T mtk_image \ - -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) \ ---- a/arch/arm/mach-mediatek/Kconfig -+++ b/arch/arm/mach-mediatek/Kconfig -@@ -36,7 +36,6 @@ config TARGET_MT7629 - bool "MediaTek MT7629 SoC" - select CPU_V7A - select SPL -- select BINMAN - help - The MediaTek MT7629 is a ARM-based SoC with a dual-core Cortex-A7 - including DDR3, crypto engine, 3x3 11n/ac Wi-Fi, Gigabit Ethernet, diff --git a/package/boot/uboot-mediatek/patches/100-increase-CONFIG_SYS_BOOTM_LEN.patch b/package/boot/uboot-mediatek/patches/100-increase-CONFIG_SYS_BOOTM_LEN.patch deleted file mode 100644 index 811e8489dd..0000000000 --- a/package/boot/uboot-mediatek/patches/100-increase-CONFIG_SYS_BOOTM_LEN.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/include/configs/mt7622.h -+++ b/include/configs/mt7622.h -@@ -37,7 +37,7 @@ - #endif - - #define CONFIG_SYS_MAXARGS 8 --#define CONFIG_SYS_BOOTM_LEN SZ_64M -+#define CONFIG_SYS_BOOTM_LEN SZ_128M - #define CONFIG_SYS_CBSIZE SZ_1K - #define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ - sizeof(CONFIG_SYS_PROMPT) + 16) diff --git a/package/boot/uboot-mediatek/patches/100-scripts-remove-dependency-on-swig.patch b/package/boot/uboot-mediatek/patches/100-scripts-remove-dependency-on-swig.patch new file mode 100644 index 0000000000..0505589385 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-scripts-remove-dependency-on-swig.patch @@ -0,0 +1,24 @@ +From b137ca16b54c67d76714ea5a0138741959b0dc29 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Mon, 13 Jul 2020 23:37:37 +0200 +Subject: [PATCH] scripts: remove dependency on swig + +Don't build the libfdt tool, as it has a dependency on swig (which +OpenWrt does not ship). + +This requires more hacks, as of-platdata generation does not work +without it. + +Signed-off-by: David Bauer +--- + scripts/dtc/Makefile | 2 -- + 1 file changed, 2 deletions(-) + +--- a/scripts/dtc/Makefile ++++ b/scripts/dtc/Makefile +@@ -18,5 +18,3 @@ HOSTCFLAGS_dtc-parser.tab.o := -I$(src) + # dependencies on generated files need to be listed explicitly + $(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h + +-# Added for U-Boot +-subdir-$(CONFIG_PYLIBFDT) += pylibfdt diff --git a/package/boot/uboot-mediatek/patches/016-fit-totalsize.patch b/package/boot/uboot-mediatek/patches/200-cmd-add-imsz-and-imszb.patch similarity index 94% rename from package/boot/uboot-mediatek/patches/016-fit-totalsize.patch rename to package/boot/uboot-mediatek/patches/200-cmd-add-imsz-and-imszb.patch index 72fb4ac516..a96345e6fe 100644 --- a/package/boot/uboot-mediatek/patches/016-fit-totalsize.patch +++ b/package/boot/uboot-mediatek/patches/200-cmd-add-imsz-and-imszb.patch @@ -1,6 +1,6 @@ --- a/cmd/bootm.c +++ b/cmd/bootm.c -@@ -227,6 +227,65 @@ U_BOOT_CMD( +@@ -228,6 +228,65 @@ U_BOOT_CMD( /* iminfo - print header info for a requested image */ /*******************************************************************/ #if defined(CONFIG_CMD_IMI) @@ -10,7 +10,7 @@ +{ + ulong addr; + void *fit; -+ int bsize, tsize, maxhdrsize; ++ int bsize, tsize; + char buf[16]; + + if (argc >= 2) @@ -68,7 +68,7 @@ { --- a/common/image-fit.c +++ b/common/image-fit.c -@@ -1878,6 +1878,51 @@ static const char *fit_get_image_type_pr +@@ -1970,6 +1970,51 @@ static const char *fit_get_image_type_pr return "unknown"; } @@ -122,7 +122,7 @@ int arch, int image_type, int bootstage_id, --- a/include/image.h +++ b/include/image.h -@@ -1027,6 +1027,7 @@ int fit_parse_subimage(const char *spec, +@@ -1041,6 +1041,7 @@ int fit_parse_subimage(const char *spec, ulong *addr, const char **image_name); int fit_get_subimage_count(const void *fit, int images_noffset); diff --git a/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch b/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch new file mode 100644 index 0000000000..82d97f756c --- /dev/null +++ b/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch @@ -0,0 +1,192 @@ +From 26d4e2e58bf0007db74b47c783785c3305ea1fa0 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 19 Jan 2021 10:58:48 +0800 +Subject: [PATCH 17/23] cmd: bootmenu: add ability to select item by shortkey + +Add ability to use shortkey to select item for bootmenu command + +Signed-off-by: Weijie Gao +--- + cmd/bootmenu.c | 77 +++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 70 insertions(+), 7 deletions(-) + +--- a/cmd/bootmenu.c ++++ b/cmd/bootmenu.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -38,6 +39,7 @@ struct bootmenu_data { + int active; /* active menu entry */ + int count; /* total count of menu entries */ + struct bootmenu_entry *first; /* first menu entry */ ++ bool last_choiced; + }; + + enum bootmenu_key { +@@ -46,8 +48,27 @@ enum bootmenu_key { + KEY_DOWN, + KEY_SELECT, + KEY_QUIT, ++ KEY_CHOICE, + }; + ++static const char choice_chars[] = { ++ '1', '2', '3', '4', '5', '6', '7', '8', '9', ++ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', ++ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', ++ 'u', 'v', 'w', 'x', 'y', 'z' ++}; ++ ++static int find_choice(char choice) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(choice_chars); i++) ++ if (tolower(choice) == choice_chars[i]) ++ return i; ++ ++ return -1; ++} ++ + static char *bootmenu_getoption(unsigned short int n) + { + char name[MAX_ENV_SIZE]; +@@ -82,7 +103,7 @@ static void bootmenu_print_entry(void *d + } + + static void bootmenu_autoboot_loop(struct bootmenu_data *menu, +- enum bootmenu_key *key, int *esc) ++ enum bootmenu_key *key, int *esc, int *choice) + { + int i, c; + +@@ -115,6 +136,19 @@ static void bootmenu_autoboot_loop(struc + break; + default: + *key = KEY_NONE; ++ if (*esc) ++ break; ++ ++ *choice = find_choice(c); ++ if ((*choice >= 0 && ++ *choice < menu->count - 1)) { ++ *key = KEY_CHOICE; ++ } else if (c == '0') { ++ *choice = menu->count - 1; ++ *key = KEY_CHOICE; ++ } else { ++ *key = KEY_NONE; ++ } + break; + } + +@@ -136,10 +170,16 @@ static void bootmenu_autoboot_loop(struc + } + + static void bootmenu_loop(struct bootmenu_data *menu, +- enum bootmenu_key *key, int *esc) ++ enum bootmenu_key *key, int *esc, int *choice) + { + int c; + ++ if (menu->last_choiced) { ++ menu->last_choiced = false; ++ *key = KEY_SELECT; ++ return; ++ } ++ + if (*esc == 1) { + if (tstc()) { + c = getchar(); +@@ -165,6 +205,14 @@ static void bootmenu_loop(struct bootmen + if (c == '\e') { + *esc = 1; + *key = KEY_NONE; ++ } else { ++ *choice = find_choice(c); ++ if ((*choice >= 0 && *choice < menu->count - 1)) { ++ *key = KEY_CHOICE; ++ } else if (c == '0') { ++ *choice = menu->count - 1; ++ *key = KEY_CHOICE; ++ } + } + break; + case 1: +@@ -216,16 +264,17 @@ static char *bootmenu_choice_entry(void + struct bootmenu_data *menu = data; + struct bootmenu_entry *iter; + enum bootmenu_key key = KEY_NONE; ++ int choice = -1; + int esc = 0; + int i; + + while (1) { + if (menu->delay >= 0) { + /* Autoboot was not stopped */ +- bootmenu_autoboot_loop(menu, &key, &esc); ++ bootmenu_autoboot_loop(menu, &key, &esc, &choice); + } else { + /* Some key was pressed, so autoboot was stopped */ +- bootmenu_loop(menu, &key, &esc); ++ bootmenu_loop(menu, &key, &esc, &choice); + } + + switch (key) { +@@ -239,6 +288,12 @@ static char *bootmenu_choice_entry(void + ++menu->active; + /* no menu key selected, regenerate menu */ + return NULL; ++ case KEY_CHOICE: ++ menu->active = choice; ++ if (!menu->last_choiced) { ++ menu->last_choiced = true; ++ return NULL; ++ } + case KEY_SELECT: + iter = menu->first; + for (i = 0; i < menu->active; ++i) +@@ -294,6 +349,7 @@ static struct bootmenu_data *bootmenu_cr + menu->delay = delay; + menu->active = 0; + menu->first = NULL; ++ menu->last_choiced = false; + + default_str = env_get("bootmenu_default"); + if (default_str) +@@ -311,12 +367,19 @@ static struct bootmenu_data *bootmenu_cr + goto cleanup; + + len = sep-option; +- entry->title = malloc(len + 1); ++ entry->title = malloc(len + 4); + if (!entry->title) { + free(entry); + goto cleanup; + } +- memcpy(entry->title, option, len); ++ ++ if (i < ARRAY_SIZE(choice_chars)) { ++ len = sprintf(entry->title, "%c. %.*s", choice_chars[i], ++ len, option); ++ } else { ++ len = sprintf(entry->title, " %.*s", len, option); ++ } ++ + entry->title[len] = 0; + + len = strlen(sep + 1); +@@ -353,7 +416,7 @@ static struct bootmenu_data *bootmenu_cr + if (!entry) + goto cleanup; + +- entry->title = strdup("U-Boot console"); ++ entry->title = strdup("0. U-Boot console"); + if (!entry->title) { + free(entry); + goto cleanup; diff --git a/package/boot/uboot-mediatek/patches/008-bootmenu-custom-title.patch b/package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch similarity index 80% rename from package/boot/uboot-mediatek/patches/008-bootmenu-custom-title.patch rename to package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch index 32f26ece8e..3f98f13c6e 100644 --- a/package/boot/uboot-mediatek/patches/008-bootmenu-custom-title.patch +++ b/package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch @@ -1,14 +1,14 @@ --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c -@@ -38,6 +38,7 @@ struct bootmenu_data { +@@ -39,6 +39,7 @@ struct bootmenu_data { int active; /* active menu entry */ int count; /* total count of menu entries */ struct bootmenu_entry *first; /* first menu entry */ + char *mtitle; /* custom menu title */ + bool last_choiced; }; - enum bootmenu_key { -@@ -380,7 +381,12 @@ static void menu_display_statusline(stru +@@ -471,7 +472,12 @@ static void menu_display_statusline(stru printf(ANSI_CURSOR_POSITION, 1, 1); puts(ANSI_CLEAR_LINE); printf(ANSI_CURSOR_POSITION, 2, 1); @@ -22,7 +22,7 @@ puts(ANSI_CLEAR_LINE_TO_END); printf(ANSI_CURSOR_POSITION, 3, 1); puts(ANSI_CLEAR_LINE); -@@ -434,6 +440,7 @@ static void bootmenu_show(int delay) +@@ -525,6 +531,7 @@ static void bootmenu_show(int delay) return; } diff --git a/package/boot/uboot-mediatek/patches/007-env-readmem.patch b/package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch similarity index 91% rename from package/boot/uboot-mediatek/patches/007-env-readmem.patch rename to package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch index a8c88a2757..705b749e71 100644 --- a/package/boot/uboot-mediatek/patches/007-env-readmem.patch +++ b/package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch @@ -1,6 +1,6 @@ --- a/cmd/Kconfig +++ b/cmd/Kconfig -@@ -571,6 +571,12 @@ config CMD_ENV_EXISTS +@@ -465,6 +465,12 @@ config CMD_ENV_EXISTS Check if a variable is defined in the environment for use in shell scripting. @@ -15,7 +15,7 @@ help --- a/cmd/nvedit.c +++ b/cmd/nvedit.c -@@ -469,6 +469,60 @@ int do_env_ask(struct cmd_tbl *cmdtp, in +@@ -473,6 +473,60 @@ int do_env_ask(struct cmd_tbl *cmdtp, in } #endif @@ -76,7 +76,7 @@ #if defined(CONFIG_CMD_ENV_CALLBACK) static int print_static_binding(const char *var_name, const char *callback_name, void *priv) -@@ -1373,6 +1427,9 @@ static struct cmd_tbl cmd_env_sub[] = { +@@ -1377,6 +1431,9 @@ static struct cmd_tbl cmd_env_sub[] = { U_BOOT_CMD_MKENT(load, 1, 0, do_env_load, "", ""), #endif U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""), @@ -86,7 +86,7 @@ #if defined(CONFIG_CMD_RUN) U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""), #endif -@@ -1461,6 +1518,9 @@ static char env_help_text[] = +@@ -1465,6 +1522,9 @@ static char env_help_text[] = #if defined(CONFIG_CMD_NVEDIT_EFI) "env print -e [-guid guid] [-n] [name ...] - print UEFI environment\n" #endif @@ -96,7 +96,7 @@ #if defined(CONFIG_CMD_RUN) "env run var [...] - run commands in an environment variable\n" #endif -@@ -1570,6 +1630,17 @@ U_BOOT_CMD( +@@ -1574,6 +1634,17 @@ U_BOOT_CMD( ); #endif diff --git a/package/boot/uboot-mediatek/patches/260-add-missing-type-u64.patch b/package/boot/uboot-mediatek/patches/260-add-missing-type-u64.patch new file mode 100644 index 0000000000..a6204c7b69 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/260-add-missing-type-u64.patch @@ -0,0 +1,10 @@ +--- a/include/linux/types.h ++++ b/include/linux/types.h +@@ -1,6 +1,7 @@ + #ifndef _LINUX_TYPES_H + #define _LINUX_TYPES_H + ++typedef unsigned long long __u64; + #include + #include + #include diff --git a/package/boot/uboot-mediatek/patches/009-mt7622-generic-reset-button-ignore-env.patch b/package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch similarity index 88% rename from package/boot/uboot-mediatek/patches/009-mt7622-generic-reset-button-ignore-env.patch rename to package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch index 037bbb89ea..aeb4c49b9e 100644 --- a/package/boot/uboot-mediatek/patches/009-mt7622-generic-reset-button-ignore-env.patch +++ b/package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch @@ -1,13 +1,15 @@ --- a/board/mediatek/mt7622/mt7622_rfb.c +++ b/board/mediatek/mt7622/mt7622_rfb.c -@@ -6,9 +6,15 @@ +@@ -6,10 +6,17 @@ #include #include +#include +#include ++ #include #include + #include +#ifndef CONFIG_RESET_BUTTON_LABEL +#define CONFIG_RESET_BUTTON_LABEL "reset" @@ -16,7 +18,7 @@ DECLARE_GLOBAL_DATA_PTR; int board_init(void) -@@ -19,7 +25,15 @@ int board_init(void) +@@ -20,7 +27,15 @@ int board_init(void) int board_late_init(void) { diff --git a/package/boot/uboot-mediatek/patches/005-update-bpir2-defconfig.patch b/package/boot/uboot-mediatek/patches/400-update-bpir2-defconfig.patch similarity index 84% rename from package/boot/uboot-mediatek/patches/005-update-bpir2-defconfig.patch rename to package/boot/uboot-mediatek/patches/400-update-bpir2-defconfig.patch index b750dda6e8..ec0fed84b8 100644 --- a/package/boot/uboot-mediatek/patches/005-update-bpir2-defconfig.patch +++ b/package/boot/uboot-mediatek/patches/400-update-bpir2-defconfig.patch @@ -1,6 +1,6 @@ --- a/configs/mt7623n_bpir2_defconfig +++ b/configs/mt7623n_bpir2_defconfig -@@ -51,5 +51,15 @@ CONFIG_SYSRESET=y +@@ -51,6 +51,16 @@ CONFIG_SYSRESET=y CONFIG_SYSRESET_WATCHDOG=y CONFIG_TIMER=y CONFIG_MTK_TIMER=y @@ -16,3 +16,4 @@ +CONFIG_CMD_SETEXPR=y CONFIG_WDT_MTK=y CONFIG_LZMA=y + # CONFIG_EFI_GRUB_ARM32_WORKAROUND is not set diff --git a/package/boot/uboot-mediatek/patches/010-update-u7623-defconfig.patch b/package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch similarity index 100% rename from package/boot/uboot-mediatek/patches/010-update-u7623-defconfig.patch rename to package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch diff --git a/package/boot/uboot-mediatek/patches/015-update-bananapi-bpi-r64-device-tree.patch b/package/boot/uboot-mediatek/patches/402-update-bananapi-bpi-r64-device-tree.patch similarity index 64% rename from package/boot/uboot-mediatek/patches/015-update-bananapi-bpi-r64-device-tree.patch rename to package/boot/uboot-mediatek/patches/402-update-bananapi-bpi-r64-device-tree.patch index dd4b94e2e1..781a685721 100644 --- a/package/boot/uboot-mediatek/patches/015-update-bananapi-bpi-r64-device-tree.patch +++ b/package/boot/uboot-mediatek/patches/402-update-bananapi-bpi-r64-device-tree.patch @@ -8,7 +8,7 @@ }; memory@40000000 { -@@ -27,6 +29,42 @@ +@@ -27,6 +28,42 @@ reg = <0x40000000 0x40000000>; }; @@ -51,30 +51,7 @@ reg_1p8v: regulator-1p8v { compatible = "regulator-fixed"; regulator-name = "fixed-1.8V"; -@@ -139,11 +177,12 @@ - - }; - -- mmc1_pins_default: mmc1default { -+ sd0_pins_default: sd0-pins-default { - mux { - function = "sd"; -- groups = "sd_0"; -+ groups = "sd_0"; - }; -+ - /* "I2S2_OUT, "I2S4_IN"", "I2S3_IN", "I2S2_IN", - * "I2S4_OUT", "I2S3_OUT" are used as DAT0, DAT1, - * DAT2, DAT3, CMD, CLK for SD respectively. -@@ -164,7 +203,6 @@ - pins = "TXD3"; - bias-pull-up; - }; -- - }; - }; - -@@ -199,7 +237,7 @@ +@@ -199,7 +236,7 @@ status = "okay"; bus-width = <8>; max-frequency = <50000000>; @@ -83,21 +60,12 @@ vmmc-supply = <®_3p3v>; vqmmc-supply = <®_3p3v>; non-removable; -@@ -207,14 +245,15 @@ - - &mmc1 { - pinctrl-names = "default"; -- pinctrl-0 = <&mmc1_pins_default>; -+ pinctrl-0 = <&sd0_pins_default>; +@@ -210,7 +247,7 @@ + pinctrl-0 = <&mmc1_pins_default>; status = "okay"; bus-width = <4>; - max-frequency = <50000000>; -+ max-frequency = <20000000>; ++ max-frequency = <12000000>; cap-sd-highspeed; r_smpl = <1>; vmmc-supply = <®_3p3v>; - vqmmc-supply = <®_3p3v>; -+ cd-gpios = <&gpio 81 GPIO_ACTIVE_LOW>; - }; - - &watchdog { diff --git a/package/boot/uboot-mediatek/patches/017-add-bananapi_bpi-r64_defconfigs.patch b/package/boot/uboot-mediatek/patches/403-add-bananapi_bpi-r64_defconfigs.patch similarity index 81% rename from package/boot/uboot-mediatek/patches/017-add-bananapi_bpi-r64_defconfigs.patch rename to package/boot/uboot-mediatek/patches/403-add-bananapi_bpi-r64_defconfigs.patch index 3624c0102a..9d52cc4feb 100644 --- a/package/boot/uboot-mediatek/patches/017-add-bananapi_bpi-r64_defconfigs.patch +++ b/package/boot/uboot-mediatek/patches/403-add-bananapi_bpi-r64_defconfigs.patch @@ -1,16 +1,21 @@ --- /dev/null +++ b/configs/mt7622_bananapi_bpi-r64-sdmmc_defconfig -@@ -0,0 +1,123 @@ +@@ -0,0 +1,144 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y ++CONFIG_TARGET_MT7622=y +CONFIG_SYS_TEXT_BASE=0x41e00000 +CONFIG_SYS_MALLOC_F_LEN=0x4000 +CONFIG_USE_DEFAULT_ENV_FILE=y +CONFIG_BOARD_LATE_INIT=y +CONFIG_BOOTP_SEND_HOSTNAME=y +CONFIG_NR_DRAM_BANKS=1 ++CONFIG_DEBUG_UART_BASE=0x11002000 ++CONFIG_DEBUG_UART_CLOCK=25000000 +CONFIG_DEFAULT_DEVICE_TREE="mt7622-bananapi-bpi-r64" ++CONFIG_OF_LIBFDT_OVERLAY=y ++CONFIG_DEBUG_UART=y +CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-sdmmc_env" +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_SMBIOS_PRODUCT_NAME="" @@ -20,6 +25,7 @@ +CONFIG_CFB_CONSOLE_ANSI=y +CONFIG_BUTTON=y +CONFIG_BUTTON_GPIO=y ++CONFIG_GPIO_HOG=y +CONFIG_CMD_ENV_FLAGS=y +CONFIG_FIT=y +CONFIG_FIT_ENABLE_SHA256_SUPPORT=y @@ -33,6 +39,7 @@ +CONFIG_CMD_BOOTMENU=y +CONFIG_CMD_BOOTP=y +CONFIG_CMD_BUTTON=y ++CONFIG_CMD_CACHE=y +CONFIG_CMD_CDP=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_DNS=y @@ -50,23 +57,25 @@ +CONFIG_CMD_LED=y +CONFIG_CMD_LICENSE=y +CONFIG_CMD_LINK_LOCAL=y -+CONFIG_CMD_MBR=y ++# CONFIG_CMD_MBR is not set +CONFIG_CMD_MMC=y +CONFIG_CMD_PCI=y +CONFIG_CMD_SF_TEST=y +CONFIG_CMD_PING=y +CONFIG_CMD_PXE=y ++CONFIG_CMD_PWM=y +CONFIG_CMD_SMC=y +CONFIG_CMD_TFTPBOOT=y +CONFIG_CMD_TFTPSRV=y +CONFIG_CMD_ASKENV=y +CONFIG_CMD_PART=y -+CONFIG_CMD_PSTORE=y ++# CONFIG_CMD_PSTORE is not set +CONFIG_CMD_RARP=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_SOURCE=y ++CONFIG_CMD_STRINGS=y +CONFIG_CMD_USB=y +CONFIG_CMD_UUID=y +CONFIG_DISPLAY_CPUINFO=y @@ -75,6 +84,8 @@ +CONFIG_DM_REGULATOR_FIXED=y +CONFIG_DM_REGULATOR_GPIO=y +CONFIG_DM_USB=y ++CONFIG_DM_PWM=y ++CONFIG_PWM_MTK=y +CONFIG_HUSH_PARSER=y +CONFIG_SYS_REDUNDAND_ENVIRONMENT=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y @@ -90,7 +101,17 @@ +CONFIG_REGMAP=y +CONFIG_SYSCON=y +CONFIG_CLK=y ++CONFIG_DM_GPIO=y ++CONFIG_DM_SCSI=y ++CONFIG_AHCI=y ++CONFIG_AHCI_PCI=y ++CONFIG_SCSI_AHCI=y ++CONFIG_SCSI=y ++CONFIG_CMD_SCSI=y ++CONFIG_PHY=y ++CONFIG_PHY_MTK_TPHY=y +CONFIG_PHY_FIXED=y ++CONFIG_MTK_AHCI=y +CONFIG_DM_ETH=y +CONFIG_MEDIATEK_ETH=y +CONFIG_PCI=y @@ -108,11 +129,11 @@ +CONFIG_MTK_SERIAL=y +CONFIG_MMC=y +CONFIG_MMC_DEFAULT_DEV=1 ++CONFIG_MMC_HS200_SUPPORT=y +CONFIG_MMC_MTK=y ++CONFIG_MMC_SUPPORTS_TUNING=y +CONFIG_SUPPORT_EMMC_BOOT=y +CONFIG_SYSRESET_WATCHDOG=y -+CONFIG_TIMER=y -+CONFIG_MTK_TIMER=y +CONFIG_WDT_MTK=y +CONFIG_LZO=y +CONFIG_ZSTD=y @@ -131,26 +152,26 @@ +serverip=192.168.1.254 +loadaddr=0x4007ff28 +bootcmd=run boot_sdmmc -+bootargs=earlycon=uart8250,mmio32,0x11002000 console=ttyS0,115200n1 swiotlb=512 root=/dev/mmcblk1p6 ++bootargs=root=/dev/mmcblk1p6 +bootdelay=0 +bootfile=openwrt-mediatek-mt7622-bananapi_bpi-r64-initramfs-recovery.itb +bootfile_upg=openwrt-mediatek-mt7622-bananapi_bpi-r64-squashfs-sysupgrade.itb +bootfile_emmcbl3=openwrt-mediatek-mt7622-bananapi_bpi-r64-boot-emmc.img +bootfile_emmcbl2=openwrt-mediatek-mt7622-bananapi_bpi-r64-bl2-emmc.bin -+bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu ++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60 +bootmenu_default=0 +bootmenu_delay=0 +bootmenu_title= ( ( ( OpenWrt ) ) ) [SD card] -+bootmenu_0=0. Initialize environment.=run _firstboot -+bootmenu_0d=0. Run default boot command.=run boot_default -+bootmenu_1=1. Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return -+bootmenu_2=2. Boot production system from SD card.=run boot_production ; run bootmenu_confirm_return -+bootmenu_3=3. Boot recovery system from SD card.=run boot_recovery ; run bootmenu_confirm_return -+bootmenu_4=4. Load production system via TFTP then write to SD card.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return -+bootmenu_5=5. Load recovery system via TFTP then write to SD card.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return -+bootmenu_6=6. Install bootloader and recovery to eMMC.=run emmc_init ; run bootmenu_confirm_return -+bootmenu_7=7. Reboot.=reset -+bootmenu_8=8. Reset all settings to factory defaults.=run reset_factory ; reset ++bootmenu_0=Initialize environment.=run _firstboot ++bootmenu_0d=Run default boot command.=run boot_default ++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return ++bootmenu_2=Boot production system from SD card.=run boot_production ; run bootmenu_confirm_return ++bootmenu_3=Boot recovery system from SD card.=run boot_recovery ; run bootmenu_confirm_return ++bootmenu_4=Load production system via TFTP then write to SD card.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return ++bootmenu_5=Load recovery system via TFTP then write to SD card.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return ++bootmenu_6=Install bootloader and recovery to eMMC.=run emmc_init ; run bootmenu_confirm_return ++bootmenu_7=Reboot.=reset ++bootmenu_8=Reset all settings to factory defaults.=run reset_factory ; reset +boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever +boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu +boot_production=led bpi-r64:pio:green on ; run sdmmc_read_production && bootm $loadaddr @@ -183,18 +204,23 @@ +_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title $ver" --- /dev/null +++ b/configs/mt7622_bananapi_bpi-r64-emmc_defconfig -@@ -0,0 +1,123 @@ +@@ -0,0 +1,144 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y ++CONFIG_TARGET_MT7622=y +CONFIG_SYS_TEXT_BASE=0x41e00000 +CONFIG_SYS_MALLOC_F_LEN=0x4000 +CONFIG_USE_DEFAULT_ENV_FILE=y +CONFIG_BOARD_LATE_INIT=y +CONFIG_BOOTP_SEND_HOSTNAME=y +CONFIG_NR_DRAM_BANKS=1 ++CONFIG_DEBUG_UART_BASE=0x11002000 ++CONFIG_DEBUG_UART_CLOCK=25000000 +CONFIG_DEFAULT_DEVICE_TREE="mt7622-bananapi-bpi-r64" -+CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-emmc_env" ++CONFIG_OF_LIBFDT_OVERLAY=y ++CONFIG_DEBUG_UART=y ++CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-sdmmc_env" +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_SMBIOS_PRODUCT_NAME="" +CONFIG_AUTOBOOT_KEYED=y @@ -203,6 +229,7 @@ +CONFIG_CFB_CONSOLE_ANSI=y +CONFIG_BUTTON=y +CONFIG_BUTTON_GPIO=y ++CONFIG_GPIO_HOG=y +CONFIG_CMD_ENV_FLAGS=y +CONFIG_FIT=y +CONFIG_FIT_ENABLE_SHA256_SUPPORT=y @@ -216,6 +243,7 @@ +CONFIG_CMD_BOOTMENU=y +CONFIG_CMD_BOOTP=y +CONFIG_CMD_BUTTON=y ++CONFIG_CMD_CACHE=y +CONFIG_CMD_CDP=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_DNS=y @@ -233,23 +261,25 @@ +CONFIG_CMD_LED=y +CONFIG_CMD_LICENSE=y +CONFIG_CMD_LINK_LOCAL=y -+CONFIG_CMD_MBR=y ++# CONFIG_CMD_MBR is not set +CONFIG_CMD_MMC=y +CONFIG_CMD_PCI=y +CONFIG_CMD_SF_TEST=y +CONFIG_CMD_PING=y +CONFIG_CMD_PXE=y ++CONFIG_CMD_PWM=y +CONFIG_CMD_SMC=y +CONFIG_CMD_TFTPBOOT=y +CONFIG_CMD_TFTPSRV=y +CONFIG_CMD_ASKENV=y +CONFIG_CMD_PART=y -+CONFIG_CMD_PSTORE=y ++# CONFIG_CMD_PSTORE is not set +CONFIG_CMD_RARP=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_SOURCE=y ++CONFIG_CMD_STRINGS=y +CONFIG_CMD_USB=y +CONFIG_CMD_UUID=y +CONFIG_DISPLAY_CPUINFO=y @@ -258,6 +288,8 @@ +CONFIG_DM_REGULATOR_FIXED=y +CONFIG_DM_REGULATOR_GPIO=y +CONFIG_DM_USB=y ++CONFIG_DM_PWM=y ++CONFIG_PWM_MTK=y +CONFIG_HUSH_PARSER=y +CONFIG_SYS_REDUNDAND_ENVIRONMENT=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y @@ -273,7 +305,17 @@ +CONFIG_REGMAP=y +CONFIG_SYSCON=y +CONFIG_CLK=y ++CONFIG_DM_GPIO=y ++CONFIG_DM_SCSI=y ++CONFIG_AHCI=y ++CONFIG_AHCI_PCI=y ++CONFIG_SCSI_AHCI=y ++CONFIG_SCSI=y ++CONFIG_CMD_SCSI=y ++CONFIG_PHY=y ++CONFIG_PHY_MTK_TPHY=y +CONFIG_PHY_FIXED=y ++CONFIG_MTK_AHCI=y +CONFIG_DM_ETH=y +CONFIG_MEDIATEK_ETH=y +CONFIG_PCI=y @@ -291,11 +333,11 @@ +CONFIG_MTK_SERIAL=y +CONFIG_MMC=y +CONFIG_MMC_DEFAULT_DEV=0 ++CONFIG_MMC_HS200_SUPPORT=y +CONFIG_MMC_MTK=y ++CONFIG_MMC_SUPPORTS_TUNING=y +CONFIG_SUPPORT_EMMC_BOOT=y +CONFIG_SYSRESET_WATCHDOG=y -+CONFIG_TIMER=y -+CONFIG_MTK_TIMER=y +CONFIG_WDT_MTK=y +CONFIG_LZO=y +CONFIG_ZSTD=y @@ -314,23 +356,23 @@ +serverip=192.168.1.254 +loadaddr=0x4007ff28 +bootcmd=run boot_emmc -+bootargs=earlycon=uart8250,mmio32,0x11002000 console=ttyS0,115200n1 swiotlb=512 root=/dev/mmcblk0p5 ++bootargs=root=/dev/mmcblk0p5 +bootdelay=0 +bootfile=openwrt-mediatek-mt7622-bananapi_bpi-r64-initramfs-recovery.itb +bootfile_upg=openwrt-mediatek-mt7622-bananapi_bpi-r64-squashfs-sysupgrade.itb -+bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu ++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60 +bootmenu_default=0 +bootmenu_delay=0 +bootmenu_title= ( ( ( OpenWrt ) ) ) [eMMC] -+bootmenu_0=0. Initialize environment.=run _firstboot -+bootmenu_0d=0. Run default boot command.=run boot_default -+bootmenu_1=1. Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return -+bootmenu_2=2. Boot production system from eMMC.=run boot_production ; run bootmenu_confirm_return -+bootmenu_3=3. Boot recovery system from eMMC.=run boot_recovery ; run bootmenu_confirm_return -+bootmenu_4=4. Load production system via TFTP then write to eMMC.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return -+bootmenu_5=5. Load recovery system via TFTP then write to eMMC.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return -+bootmenu_6=6. Reboot.=reset -+bootmenu_7=7. Reset all settings to factory defaults.=run reset_factory ; reset ++bootmenu_0=Initialize environment.=run _firstboot ++bootmenu_0d=Run default boot command.=run boot_default ++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return ++bootmenu_2=Boot production system from eMMC.=run boot_production ; run bootmenu_confirm_return ++bootmenu_3=Boot recovery system from eMMC.=run boot_recovery ; run bootmenu_confirm_return ++bootmenu_4=Load production system via TFTP then write to eMMC.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return ++bootmenu_5=Load recovery system via TFTP then write to eMMC.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return ++bootmenu_6=Reboot.=reset ++bootmenu_7=Reset all settings to factory defaults.=run reset_factory ; reset +boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever +boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu +boot_production=led bpi-r64:pio:green on ; run emmc_read_production && bootm $loadaddr diff --git a/package/boot/uboot-mediatek/patches/020-add-linksys-e8450.patch b/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch similarity index 76% rename from package/boot/uboot-mediatek/patches/020-add-linksys-e8450.patch rename to package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch index 711c1e6983..9fe83725b3 100644 --- a/package/boot/uboot-mediatek/patches/020-add-linksys-e8450.patch +++ b/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch @@ -1,9 +1,10 @@ --- /dev/null +++ b/configs/mt7622_linksys_e8450_defconfig -@@ -0,0 +1,130 @@ +@@ -0,0 +1,133 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y ++CONFIG_TARGET_MT7622=y +CONFIG_SYS_TEXT_BASE=0x41e00000 +CONFIG_SYS_MALLOC_F_LEN=0x4000 +CONFIG_USE_DEFAULT_ENV_FILE=y @@ -11,7 +12,10 @@ +CONFIG_BOOTP_SEND_HOSTNAME=y +CONFIG_DEFAULT_ENV_FILE="linksys_e8450_env" +CONFIG_NR_DRAM_BANKS=1 ++CONFIG_DEBUG_UART_BASE=0x11002000 ++CONFIG_DEBUG_UART_CLOCK=25000000 +CONFIG_DEFAULT_DEVICE_TREE="mt7622-linksys-e8450-ubi" ++CONFIG_DEBUG_UART=y +CONFIG_SMBIOS_PRODUCT_NAME="" +CONFIG_AUTOBOOT_KEYED=y +CONFIG_BOOTDELAY=30 @@ -19,6 +23,7 @@ +CONFIG_CFB_CONSOLE_ANSI=y +CONFIG_BUTTON=y +CONFIG_BUTTON_GPIO=y ++CONFIG_GPIO_HOG=y +CONFIG_CMD_ENV_FLAGS=y +CONFIG_FIT=y +CONFIG_FIT_ENABLE_SHA256_SUPPORT=y @@ -49,10 +54,9 @@ +CONFIG_CMD_LED=y +CONFIG_CMD_LICENSE=y +CONFIG_CMD_LINK_LOCAL=y -+CONFIG_CMD_MBR=y ++# CONFIG_CMD_MBR is not set +CONFIG_CMD_MTD=y +CONFIG_CMD_MTDPART=y -+CONFIG_CMD_NAND=y +CONFIG_CMD_PCI=y +CONFIG_CMD_SF_TEST=y +CONFIG_CMD_PING=y @@ -65,7 +69,7 @@ +CONFIG_CMD_UBIFS=y +CONFIG_CMD_ASKENV=y +CONFIG_CMD_PART=y -+CONFIG_CMD_PSTORE=y ++# CONFIG_CMD_PSTORE is not set +CONFIG_CMD_RARP=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_SLEEP=y @@ -93,12 +97,14 @@ +CONFIG_SYSCON=y +CONFIG_CLK=y +CONFIG_DM_MTD=y ++CONFIG_DM_GPIO=y ++CONFIG_PHY=y ++CONFIG_PHY_MTK_TPHY=y +CONFIG_PHY_FIXED=y +CONFIG_DM_ETH=y +CONFIG_MEDIATEK_ETH=y +CONFIG_PCI=y +CONFIG_MTD=y -+CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_UBI_FASTMAP=y +CONFIG_DM_PCI=y +CONFIG_PCIE_MEDIATEK=y @@ -114,12 +120,9 @@ +CONFIG_MTK_SERIAL=y +CONFIG_SPI=y +CONFIG_DM_SPI=y -+CONFIG_MTK_SNFI_SPI=y +CONFIG_MTK_SPI_NAND=y -+CONFIG_NAND_SUPPORT=y ++CONFIG_MTK_SPI_NAND_MTD=y +CONFIG_SYSRESET_WATCHDOG=y -+CONFIG_TIMER=y -+CONFIG_MTK_TIMER=y +CONFIG_WDT_MTK=y +CONFIG_LZO=y +CONFIG_ZSTD=y @@ -133,7 +136,7 @@ +CONFIG_USB_STORAGE=y --- /dev/null +++ b/arch/arm/dts/mt7622-linksys-e8450-ubi.dts -@@ -0,0 +1,206 @@ +@@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 MediaTek Inc. @@ -155,7 +158,7 @@ + }; + + aliases { -+ spi0 = &snfi; ++ spi0 = &snand; + }; + + gpio-keys { @@ -288,22 +291,11 @@ + }; +}; + -+&snfi { ++&snand { + pinctrl-names = "default"; + pinctrl-0 = <&snfi_pins>; + status = "okay"; -+ -+ mediatek,bmt-v2; -+ -+ spi-flash@0{ -+ compatible = "jedec,spi-nor"; -+ reg = <0>; -+ u-boot,dm-pre-reloc; -+ }; -+}; -+ -+&nandc { -+ status = "okay"; ++ quad-spi; +}; + +&uart0 { @@ -342,7 +334,7 @@ +}; --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile -@@ -963,6 +963,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \ +@@ -996,6 +996,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \ mt7622-rfb.dtb \ mt7623a-unielec-u7623-02-emmc.dtb \ mt7622-bananapi-bpi-r64.dtb \ @@ -350,35 +342,11 @@ mt7623n-bananapi-bpi-r2.dtb \ mt7629-rfb.dtb \ mt8512-bm1-emmc.dtb \ ---- a/drivers/mtd/nandx/core/nand/device_spi.c -+++ b/drivers/mtd/nandx/core/nand/device_spi.c -@@ -150,6 +150,21 @@ static struct device_spi spi_nand[] = { - &spi_extend_cmds, 0xff, 0xff - }, - { -+ NAND_DEVICE("FM35X1GA", -+ NAND_PACK_ID(0xe5, 0x71, 0, 0, 0, 0, 0, 0), -+ 2, 0, 3, 3, -+ 1, 1, 1, 1024, KB(128), KB(2), 64, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { - NAND_DEVICE("NO-DEVICE", - NAND_PACK_ID(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, --- /dev/null +++ b/linksys_e8450_env @@ -0,0 +1,57 @@ -+mtdparts=nand0:512k(bl2),1280k(fip),1024k(factory),256k(reserved),-(ubi) -+ethaddr_factory=nand read 0x40080000 0x220000 0x20000 && env readmem -b ethaddr 0x4009fff4 0x6 ; setenv ethaddr_factory ++mtdparts=spi-nand0:512k(bl2),1280k(fip),1024k(factory),256k(reserved),-(ubi) ++ethaddr_factory=mtd read spi-nand0 0x40080000 0x220000 0x20000 && env readmem -b ethaddr 0x4009fff4 0x6 ; setenv ethaddr_factory +ipaddr=192.168.1.1 +serverip=192.168.1.254 +loadaddr=0x4007ff28 @@ -388,21 +356,21 @@ +bootfile_bl2=openwrt-mediatek-mt7622-linksys_e8450-ubi-preloader.bin +bootfile_fip=openwrt-mediatek-mt7622-linksys_e8450-ubi-bl31-uboot.fip +bootfile_upg=openwrt-mediatek-mt7622-linksys_e8450-ubi-squashfs-sysupgrade.itb -+bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu ++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60 +bootmenu_default=0 +bootmenu_delay=0 +bootmenu_title= ( ( ( OpenWrt ) ) ) -+bootmenu_0=0. Initialize environment.=run _firstboot -+bootmenu_0d=0. Run default boot command.=run boot_default -+bootmenu_1=1. Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return -+bootmenu_3=2. Boot production system from flash.=run boot_production ; run bootmenu_confirm_return -+bootmenu_2=3. Boot recovery system from flash.=run boot_recovery ; run bootmenu_confirm_return -+bootmenu_5=4. Load production system via TFTP then write to flash.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return -+bootmenu_4=5. Load recovery system via TFTP then write to flash.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return -+bootmenu_6=6. Load BL31+U-Boot FIP via TFTP then write to flash.=run boot_tftp_write_fip ; run bootmenu_confirm_return -+bootmenu_7=7. Load BL2 preloader via TFTP then write to flash.=run boot_tftp_write_preloader ; run bootmenu_confirm_return -+bootmenu_8=8. Reboot.=reset -+bootmenu_9=9. Reset all settings to factory defaults.=run reset_factory ; reset ++bootmenu_0=Initialize environment.=run _firstboot ++bootmenu_0d=Run default boot command.=run boot_default ++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return ++bootmenu_2=Boot production system from flash.=run boot_production ; run bootmenu_confirm_return ++bootmenu_3=Boot recovery system from flash.=run boot_recovery ; run bootmenu_confirm_return ++bootmenu_4=Load production system via TFTP then write to flash.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return ++bootmenu_5=Load recovery system via TFTP then write to flash.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return ++bootmenu_6=Load BL31+U-Boot FIP via TFTP then write to flash.=run boot_tftp_write_fip ; run bootmenu_confirm_return ++bootmenu_7=Load BL2 preloader via TFTP then write to flash.=run boot_tftp_write_preloader ; run bootmenu_confirm_return ++bootmenu_8=Reboot.=reset ++bootmenu_9=Reset all settings to factory defaults.=run reset_factory ; reset +boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu +boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever +boot_production=led power:blue on ; run ubi_read_production && bootm $loadaddr @@ -417,18 +385,18 @@ +boot_tftp_write_fip=tftpboot $loadaddr $bootfile_fip && run boot_write_fip +boot_tftp_write_preloader=tftpboot $loadaddr $bootfile_bl2 && run boot_write_preloader +boot_ubi=ubi part ubi && run boot_production_or_recovery -+boot_write_fip=nand erase 0x80000 0x140000 && nand write $loadaddr 0x80000 0x140000 -+boot_write_preloader=nand erase 0x0 0x80000 && nand write $loadaddr 0x0 0x20000 && nand write $loadaddr 0x20000 0x20000 && nand write $loadaddr 0x40000 0x20000 && nand write $loadaddr 0x60000 0x20000 ++boot_write_fip=mtd erase spi-nand0 0x80000 0x140000 && mtd write spi-nand0 $loadaddr 0x80000 0x140000 ++boot_write_preloader=mtd erase spi-nand0 0x0 0x80000 && mtd write spi-nand0 $loadaddr 0x0 0x20000 && mtd write spi-nand0 $loadaddr 0x20000 0x20000 && mtd write spi-nand0 $loadaddr 0x40000 0x20000 && mtd write spi-nand0 $loadaddr 0x60000 0x20000 +check_recovery=run ubi_read_recovery ; if iminfo $loadaddr ; then bootm $loadaddr ; else ubi remove recovery ; fi +check_ubi=ubi part ubi || run ubi_format +reset_factory=ubi part ubi ; ubi write 0x0 ubootenv 0x0 ; ubi write 0x0 ubootenv2 0x0 ; ubi remove rootfs_data -+ubi_format=ubi detach ; nand erase 0x300000 0x7D00000 && ubi part ubi ; reset ++ubi_format=ubi detach ; mtd erase spi-nand0 0x300000 0x7D00000 && ubi part ubi ; reset +ubi_prepare_rootfs=if ubi check rootfs_data ; then else if env exists rootfs_data_max ; then ubi create rootfs_data $rootfs_data_max dynamic || ubi create rootfs_data - dynamic ; else ubi create rootfs_data - dynamic ; fi ; fi +ubi_read_production=ubi read $loadaddr fit && iminfo $loadaddr && run ubi_prepare_rootfs +ubi_read_recovery=ubi check recovery && ubi read $loadaddr recovery +ubi_remove_rootfs=ubi check rootfs_data && ubi remove rootfs_data -+ubi_write_production=run ubi_remove_rootfs ; ubi check fit && ubi remove fit ; run ubi_remove_rootfs ; ubi create fit $filesize dynamic && ubi write $loadaddr fit $filesize -+ubi_write_recovery=run ubi_remove_rootfs ; ubi check recovery && ubi remove recovery; ubi create recovery $filesize dynamic && ubi write $loadaddr recovery $filesize ++ubi_write_production=ubi check fit && ubi remove fit ; run ubi_remove_rootfs ; ubi create fit $filesize dynamic && ubi write $loadaddr fit $filesize ++ubi_write_recovery=ubi check recovery && ubi remove recovery ; run ubi_remove_rootfs ; ubi create recovery $filesize dynamic && ubi write $loadaddr recovery $filesize +_create_env=ubi create ubootenv 0x100000 dynamic ; ubi create ubootenv2 0x100000 dynamic +_init_env=setenv _init_env ; if ubi check ubootenv && ubi check ubootenv2 ; then else run _create_env ; fi ; setenv _create_env ; saveenv || run ubi_format ; saveenv || run ubi_format +_firstboot=setenv _firstboot ; led power:orange on ; run _switch_to_menu ; run ethaddr_factory ; run check_ubi ; run _init_env ; run boot_first diff --git a/package/libs/elfutils/Makefile b/package/libs/elfutils/Makefile index aa6743e688..dd0b8ecdde 100644 --- a/package/libs/elfutils/Makefile +++ b/package/libs/elfutils/Makefile @@ -25,7 +25,6 @@ PKG_USE_MIPS16:=0 PKG_BUILD_DEPENDS:=!USE_GLIBC:argp-standalone include $(INCLUDE_DIR)/package.mk -include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/nls.mk define Package/elfutils/Default @@ -99,4 +98,3 @@ endef $(eval $(call BuildPackage,libelf)) $(eval $(call BuildPackage,libdw)) $(eval $(call BuildPackage,libasm)) -$(eval $(call HostBuild)) diff --git a/package/network/config/gre/Makefile b/package/network/config/gre/Makefile index b16dd72cde..c920abeb0d 100644 --- a/package/network/config/gre/Makefile +++ b/package/network/config/gre/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=gre -PKG_RELEASE:=12 +PKG_RELEASE:=13 PKG_LICENSE:=GPL-2.0 include $(INCLUDE_DIR)/package.mk diff --git a/package/network/config/gre/files/gre.sh b/package/network/config/gre/files/gre.sh index eb3df5b48c..b57d5d4cdb 100755 --- a/package/network/config/gre/files/gre.sh +++ b/package/network/config/gre/files/gre.sh @@ -291,8 +291,6 @@ proto_grev6tap_init_config() { } [ -n "$INCLUDE_ONLY" ] || { - [ -f /lib/modules/$(uname -r)/gre.ko ] && add_protocol gre - [ -f /lib/modules/$(uname -r)/gre.ko ] && add_protocol gretap - [ -f /lib/modules/$(uname -r)/ip6_gre.ko ] && add_protocol grev6 - [ -f /lib/modules/$(uname -r)/ip6_gre.ko ] && add_protocol grev6tap + [ -d /sys/module/ip_gre ] && { add_protocol gre; add_protocol gretap; } + [ -d /sys/module/ip6_gre ] && { add_protocol grev6; add_protocol grev6tap; } } diff --git a/package/network/config/vti/Makefile b/package/network/config/vti/Makefile index ffac77ab91..292ab111e5 100644 --- a/package/network/config/vti/Makefile +++ b/package/network/config/vti/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=vti -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_LICENSE:=GPL-2.0 include $(INCLUDE_DIR)/package.mk diff --git a/package/network/config/vti/files/vti.sh b/package/network/config/vti/files/vti.sh index ebfd9d41e1..96b65e76d9 100755 --- a/package/network/config/vti/files/vti.sh +++ b/package/network/config/vti/files/vti.sh @@ -149,6 +149,6 @@ proto_vti6_init_config() { } [ -n "$INCLUDE_ONLY" ] || { - [ -f /lib/modules/$(uname -r)/ip_vti.ko ] && add_protocol vti - [ -f /lib/modules/$(uname -r)/ip6_vti.ko ] && add_protocol vti6 + [ -d /sys/module/ip_vti ] && add_protocol vti + [ -d /sys/module/ip6_vti ] && add_protocol vti6 } diff --git a/package/network/config/xfrm/Makefile b/package/network/config/xfrm/Makefile index bcee89f82c..777f20c77c 100644 --- a/package/network/config/xfrm/Makefile +++ b/package/network/config/xfrm/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=xfrm -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_LICENSE:=GPL-2.0 include $(INCLUDE_DIR)/package.mk diff --git a/package/network/config/xfrm/files/xfrm.sh b/package/network/config/xfrm/files/xfrm.sh index ca7c3380e6..bdebd4b9c3 100755 --- a/package/network/config/xfrm/files/xfrm.sh +++ b/package/network/config/xfrm/files/xfrm.sh @@ -68,5 +68,5 @@ proto_xfrm_init_config() { [ -n "$INCLUDE_ONLY" ] || { - [ -f /lib/modules/$(uname -r)/xfrm_interface.ko -o -d /sys/module/xfrm_interface ] && add_protocol xfrm + [ -d /sys/module/xfrm_interface ] && add_protocol xfrm } diff --git a/package/system/selinux-policy/Makefile b/package/system/selinux-policy/Makefile index 759a8eff93..4980ed6f21 100644 --- a/package/system/selinux-policy/Makefile +++ b/package/system/selinux-policy/Makefile @@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=selinux-policy PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://git.defensec.nl/selinux-policy.git -PKG_VERSION:=0.7 -PKG_MIRROR_HASH:=f9c555dfa2bb16e6bfd86f1481aad26fce72e5aa45e297e1b39636b6dd0ce021 +PKG_VERSION:=0.8 +PKG_MIRROR_HASH:=3b58f751a21394e3aef47fd6c9fe9430fadde6427deb5c79f08478904837ec91 PKG_SOURCE_VERSION:=v$(PKG_VERSION) PKG_BUILD_DEPENDS:=secilc/host policycoreutils/host diff --git a/package/utils/busybox/Makefile b/package/utils/busybox/Makefile index 3fef000c62..9df358ef78 100644 --- a/package/utils/busybox/Makefile +++ b/package/utils/busybox/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=busybox PKG_VERSION:=1.33.0 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_FLAGS:=essential PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 diff --git a/package/utils/busybox/patches/001-backport1330fix-ash-make-strdup-copy.patch b/package/utils/busybox/patches/001-backport1330fix-ash-make-strdup-copy.patch new file mode 100644 index 0000000000..b495227d9f --- /dev/null +++ b/package/utils/busybox/patches/001-backport1330fix-ash-make-strdup-copy.patch @@ -0,0 +1,40 @@ +From 67cc582d4289c5de521d11b08307c8ab26ee1e28 Mon Sep 17 00:00:00 2001 +From: Denys Vlasenko +Date: Sun, 3 Jan 2021 10:55:39 +0100 +Subject: ash: make a strdup copy of $HISTFILE for line editing + +Otherwise if $HISTFILE is unset or reassigned, bad things can happen. + +function old new delta +ash_main 1210 1218 +8 + +Signed-off-by: Denys Vlasenko +--- + shell/ash.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/shell/ash.c b/shell/ash.c +index f16d7fb6a..ecbfbf091 100644 +--- a/shell/ash.c ++++ b/shell/ash.c +@@ -14499,7 +14499,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) + + if (sflag || minusc == NULL) { + #if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY +- if (iflag) { ++ if (line_input_state) { + const char *hp = lookupvar("HISTFILE"); + if (!hp) { + hp = lookupvar("HOME"); +@@ -14513,7 +14513,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) + } + } + if (hp) +- line_input_state->hist_file = hp; ++ line_input_state->hist_file = xstrdup(hp); + # if ENABLE_FEATURE_SH_HISTFILESIZE + hp = lookupvar("HISTFILESIZE"); + line_input_state->max_history = size_from_HISTFILESIZE(hp); +-- +cgit v1.2.1 + diff --git a/package/utils/busybox/patches/002-backport1330fix-traceroute.patch b/package/utils/busybox/patches/002-backport1330fix-traceroute.patch new file mode 100644 index 0000000000..eb03094eee --- /dev/null +++ b/package/utils/busybox/patches/002-backport1330fix-traceroute.patch @@ -0,0 +1,26 @@ +From 89358a7131d3e75c74af834bb117b4fad7914983 Mon Sep 17 00:00:00 2001 +From: Denys Vlasenko +Date: Tue, 2 Feb 2021 13:48:21 +0100 +Subject: traceroute: fix option parsing + +Signed-off-by: Denys Vlasenko +--- + networking/traceroute.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/networking/traceroute.c b/networking/traceroute.c +index 3f1a9ab46..29f5e480b 100644 +--- a/networking/traceroute.c ++++ b/networking/traceroute.c +@@ -896,7 +896,7 @@ traceroute_init(int op, char **argv) + + op |= getopt32(argv, "^" + OPT_STRING +- "\0" "-1:x-x" /* minimum 1 arg */ ++ "\0" "-1" /* minimum 1 arg */ + , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str + , &source, &waittime_str, &pausemsecs_str, &first_ttl_str + ); +-- +cgit v1.2.1 + diff --git a/target/linux/bcm27xx/patches-5.4/950-0280-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch b/target/linux/bcm27xx/patches-5.4/950-0280-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch index ee886e31a5..71d2f7da05 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0280-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0280-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch @@ -33,7 +33,7 @@ Signed-off-by: Jonathan Bell #define USB_VENDOR_ID_BELKIN 0x050d #define USB_DEVICE_ID_FLIP_KVM 0x3201 -@@ -1254,6 +1257,9 @@ +@@ -1257,6 +1260,9 @@ #define USB_VENDOR_ID_XAT 0x2505 #define USB_DEVICE_ID_XAT_CSR 0x0220 @@ -53,7 +53,7 @@ Signed-off-by: Jonathan Bell { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE2), HID_QUIRK_ALWAYS_POLL }, -@@ -189,6 +190,7 @@ static const struct hid_device_id hid_qu +@@ -190,6 +191,7 @@ static const struct hid_device_id hid_qu { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE), HID_QUIRK_MULTI_INPUT }, diff --git a/target/linux/bcm27xx/patches-5.4/950-0482-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch b/target/linux/bcm27xx/patches-5.4/950-0482-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch index d82c3196b8..4d7440b999 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0482-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0482-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch @@ -39,7 +39,7 @@ Signed-off-by: Mauro Carvalho Chehab default: return -EINVAL; } -@@ -2422,6 +2428,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s +@@ -2423,6 +2429,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header); break; @@ -49,7 +49,7 @@ Signed-off-by: Mauro Carvalho Chehab default: if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); -@@ -4086,6 +4095,18 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l +@@ -4087,6 +4096,18 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string); diff --git a/target/linux/bcm27xx/patches-5.4/950-0492-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch b/target/linux/bcm27xx/patches-5.4/950-0492-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch index 0fe0f8cea4..e08d2481ed 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0492-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0492-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch @@ -820,7 +820,7 @@ Signed-off-by: Mauro Carvalho Chehab default: return -EINVAL; } -@@ -2433,6 +2531,15 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s +@@ -2434,6 +2532,15 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header); break; diff --git a/target/linux/bcm27xx/patches-5.4/950-0495-media-uapi-hevc-Add-scaling-matrix-control.patch b/target/linux/bcm27xx/patches-5.4/950-0495-media-uapi-hevc-Add-scaling-matrix-control.patch index c2cf27a40e..c63a0a8bbc 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0495-media-uapi-hevc-Add-scaling-matrix-control.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0495-media-uapi-hevc-Add-scaling-matrix-control.patch @@ -106,7 +106,7 @@ Signed-off-by: Jernej Skrabec case V4L2_CTRL_TYPE_AREA: area = p; if (!area->width || !area->height) -@@ -2540,6 +2547,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s +@@ -2541,6 +2548,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params); break; diff --git a/target/linux/bcm27xx/patches-5.4/950-0908-media-v4l2-ctrls-Add-helper-to-register-properties.patch b/target/linux/bcm27xx/patches-5.4/950-0908-media-v4l2-ctrls-Add-helper-to-register-properties.patch index a76de95bf3..cf637cea04 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0908-media-v4l2-ctrls-Add-helper-to-register-properties.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0908-media-v4l2-ctrls-Add-helper-to-register-properties.patch @@ -29,7 +29,7 @@ Signed-off-by: Laurent Pinchart #define dprintk(vdev, fmt, arg...) do { \ if (!WARN_ON(!(vdev)) && ((vdev)->dev_debug & V4L2_DEV_DEBUG_CTRL)) \ -@@ -4577,3 +4578,42 @@ __poll_t v4l2_ctrl_poll(struct file *fil +@@ -4578,3 +4579,42 @@ __poll_t v4l2_ctrl_poll(struct file *fil return 0; } EXPORT_SYMBOL(v4l2_ctrl_poll); diff --git a/target/linux/bcm47xx/patches-5.4/820-wgt634u-nvram-fix.patch b/target/linux/bcm47xx/patches-5.4/820-wgt634u-nvram-fix.patch index 6d58c1b042..5f4af85df2 100644 --- a/target/linux/bcm47xx/patches-5.4/820-wgt634u-nvram-fix.patch +++ b/target/linux/bcm47xx/patches-5.4/820-wgt634u-nvram-fix.patch @@ -250,28 +250,18 @@ out the configuration than the in kernel cfe config reader. +static int cfe_env; +extern char *cfe_env_get(char *nv_buf, const char *name); - static u32 find_nvram_size(void __iomem *end) - { -@@ -52,7 +54,9 @@ static u32 find_nvram_size(void __iomem - static int nvram_find_and_copy(void __iomem *iobase, u32 lim) - { - struct nvram_header __iomem *header; -+ int i; - u32 off; -+ u32 *src, *dst; - u32 size; - - if (nvram_len) { -@@ -60,6 +64,26 @@ static int nvram_find_and_copy(void __io + /** + * bcm47xx_nvram_is_valid - check for a valid NVRAM at specified memory +@@ -80,6 +82,26 @@ static int bcm47xx_nvram_find_and_copy(v return -EEXIST; } + cfe_env = 0; + + /* XXX: hack for supporting the CFE environment stuff on WGT634U */ -+ if (lim >= 8 * 1024 * 1024) { -+ src = (u32 *)(iobase + 8 * 1024 * 1024 - 0x2000); -+ dst = (u32 *)nvram_buf; ++ if (res_size >= 8 * 1024 * 1024) { ++ u32 *src = (u32 *)(flash_start + 8 * 1024 * 1024 - 0x2000); ++ u32 *dst = (u32 *)nvram_buf; + + if ((*src & 0xff00ff) == 0x000001) { + printk("early_nvram_init: WGT634U NVRAM found.\n"); @@ -287,9 +277,9 @@ out the configuration than the in kernel cfe config reader. + } + /* TODO: when nvram is on nand flash check for bad blocks first. */ - off = FLASH_MIN; - while (off <= lim) { -@@ -170,6 +194,13 @@ int bcm47xx_nvram_getenv(const char *nam + + /* Try every possible flash size and check for NVRAM at its end */ +@@ -172,6 +194,13 @@ int bcm47xx_nvram_getenv(const char *nam if (!name) return -EINVAL; diff --git a/target/linux/bcm53xx/patches-5.4/800-0001-firmware-bcm47xx_nvram-support-init-from-IO-memory.patch b/target/linux/bcm53xx/patches-5.4/800-0001-firmware-bcm47xx_nvram-support-init-from-IO-memory.patch new file mode 100644 index 0000000000..86792d6ebf --- /dev/null +++ b/target/linux/bcm53xx/patches-5.4/800-0001-firmware-bcm47xx_nvram-support-init-from-IO-memory.patch @@ -0,0 +1,61 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 11 Mar 2021 08:24:44 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: support init from IO memory +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Rafał Miłecki +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 17 +++++++++++++++++ + include/linux/bcm47xx_nvram.h | 6 ++++++ + 2 files changed, 23 insertions(+) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -110,6 +110,23 @@ found: + return 0; + } + ++int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start, size_t res_size) ++{ ++ if (nvram_len) { ++ pr_warn("nvram already initialized\n"); ++ return -EEXIST; ++ } ++ ++ if (!bcm47xx_nvram_is_valid(nvram_start)) { ++ pr_err("No valid NVRAM found\n"); ++ return -ENOENT; ++ } ++ ++ bcm47xx_nvram_copy(nvram_start, res_size); ++ ++ return 0; ++} ++ + /* + * On bcm47xx we need access to the NVRAM very early, so we can't use mtd + * subsystem to access flash. We can't even use platform device / driver to +--- a/include/linux/bcm47xx_nvram.h ++++ b/include/linux/bcm47xx_nvram.h +@@ -11,6 +11,7 @@ + #include + + #ifdef CONFIG_BCM47XX_NVRAM ++int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start, size_t res_size); + int bcm47xx_nvram_init_from_mem(u32 base, u32 lim); + int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len); + int bcm47xx_nvram_gpio_pin(const char *name); +@@ -20,6 +21,11 @@ static inline void bcm47xx_nvram_release + vfree(nvram); + }; + #else ++static inline int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start, ++ size_t res_size) ++{ ++ return -ENOTSUPP; ++} + static inline int bcm47xx_nvram_init_from_mem(u32 base, u32 lim) + { + return -ENOTSUPP; diff --git a/target/linux/bcm53xx/patches-5.4/800-0002-nvmem-brcm_nvram-provide-NVMEM-content-to-the-NVRAM-.patch b/target/linux/bcm53xx/patches-5.4/800-0002-nvmem-brcm_nvram-provide-NVMEM-content-to-the-NVRAM-.patch new file mode 100644 index 0000000000..cf5952ad5f --- /dev/null +++ b/target/linux/bcm53xx/patches-5.4/800-0002-nvmem-brcm_nvram-provide-NVMEM-content-to-the-NVRAM-.patch @@ -0,0 +1,31 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 11 Mar 2021 08:26:14 +0100 +Subject: [PATCH] nvmem: brcm_nvram: provide NVMEM content to the NVRAM driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Rafał Miłecki +--- + drivers/nvmem/brcm_nvram.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/nvmem/brcm_nvram.c ++++ b/drivers/nvmem/brcm_nvram.c +@@ -3,6 +3,7 @@ + * Copyright (C) 2021 Rafał Miłecki + */ + ++#include + #include + #include + #include +@@ -46,6 +47,8 @@ static int brcm_nvram_probe(struct platf + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + ++ bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res)); ++ + config.dev = dev; + config.priv = priv; + config.size = resource_size(res); diff --git a/target/linux/bmips/Makefile b/target/linux/bmips/Makefile index 0adc8cd97e..14eff38bb6 100644 --- a/target/linux/bmips/Makefile +++ b/target/linux/bmips/Makefile @@ -18,6 +18,6 @@ endef include $(INCLUDE_DIR)/target.mk -DEFAULT_PACKAGES += kmod-gpio-button-hotplug +DEFAULT_PACKAGES += ethtool kmod-gpio-button-hotplug $(eval $(call BuildTarget)) diff --git a/target/linux/bmips/config-5.10 b/target/linux/bmips/config-5.10 index 749cc0eb69..96a7547593 100644 --- a/target/linux/bmips/config-5.10 +++ b/target/linux/bmips/config-5.10 @@ -3,8 +3,15 @@ CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_ARCH_MMAP_RND_BITS_MAX=15 CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_B53=y +# CONFIG_B53_MDIO_DRIVER is not set +CONFIG_B53_MMAP_DRIVER=y +# CONFIG_B53_SERDES is not set +CONFIG_B53_SPI_DRIVER=y +# CONFIG_B53_SRAB_DRIVER is not set CONFIG_BCM6345_EXT_IRQ=y CONFIG_BCM6345_L1_IRQ=y +CONFIG_BCM6368_ENETSW=y CONFIG_BCM63XX_POWER=y CONFIG_BCM7038_L1_IRQ=y CONFIG_BCM7038_WDT=y @@ -103,6 +110,7 @@ CONFIG_GPIOLIB=y # CONFIG_GPIO_BRCMSTB is not set CONFIG_GPIO_GENERIC=y CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GRO_CELLS=y CONFIG_HANDLE_DOMAIN_IRQ=y CONFIG_HARDIRQS_SW_RESEND=y CONFIG_HAS_DMA=y @@ -128,6 +136,8 @@ CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LZO_COMPRESS=y CONFIG_LZO_DECOMPRESS=y CONFIG_MDIO_BUS=y +CONFIG_MDIO_BUS_MUX=y +CONFIG_MDIO_BUS_MUX_BCM6368=y CONFIG_MDIO_DEVICE=y CONFIG_MEMFD_CREATE=y CONFIG_MFD_SYSCON=y @@ -163,7 +173,14 @@ CONFIG_MTD_CFI_STAA=y CONFIG_MTD_JEDECPROBE=y # CONFIG_MTD_PARSER_IMAGETAG is not set CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_TAG_BRCM=y +CONFIG_NET_DSA_TAG_BRCM_COMMON=y +CONFIG_NET_DSA_TAG_BRCM_LEGACY=y +CONFIG_NET_DSA_TAG_BRCM_PREPEND=y CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SWITCHDEV=y CONFIG_NO_EXCEPT_FILL=y CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y CONFIG_NR_CPUS=2 @@ -181,6 +198,7 @@ CONFIG_PCI_DRIVERS_LEGACY=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 CONFIG_PHYLIB=y +CONFIG_PHYLINK=y CONFIG_PHYSICAL_START=0x80010000 CONFIG_PHY_BCM63XX_USBH=y # CONFIG_PHY_BRCM_SATA is not set diff --git a/target/linux/bmips/dts/bcm63168-comtrend-vr-3032u.dts b/target/linux/bmips/dts/bcm63168-comtrend-vr-3032u.dts index ed7f51890c..d82247d26c 100644 --- a/target/linux/bmips/dts/bcm63168-comtrend-vr-3032u.dts +++ b/target/linux/bmips/dts/bcm63168-comtrend-vr-3032u.dts @@ -39,6 +39,12 @@ status = "okay"; }; +ðernet { + status = "okay"; + + mtd-mac-address = <&cferom 0x6a0>; +}; + &leds { status = "okay"; @@ -177,6 +183,38 @@ status = "okay"; }; +&switch0 { + ports { + port@0 { + reg = <0>; + label = "lan2"; + + phy-handle = <&phy1>; + }; + + port@1 { + reg = <1>; + label = "lan3"; + + phy-handle = <&phy2>; + }; + + port@2 { + reg = <2>; + label = "lan4"; + + phy-handle = <&phy3>; + }; + + port@3 { + reg = <3>; + label = "lan1"; + + phy-handle = <&phy4>; + }; + }; +}; + &uart0 { status = "okay"; }; diff --git a/target/linux/bmips/dts/bcm6318-comtrend-ar-5315u.dts b/target/linux/bmips/dts/bcm6318-comtrend-ar-5315u.dts index 35c74fd3ac..a5ae435a3a 100644 --- a/target/linux/bmips/dts/bcm6318-comtrend-ar-5315u.dts +++ b/target/linux/bmips/dts/bcm6318-comtrend-ar-5315u.dts @@ -37,6 +37,12 @@ status = "okay"; }; +ðernet { + status = "okay"; + + mtd-mac-address = <&cfe 0x6a0>; +}; + &hsspi { status = "okay"; @@ -168,6 +174,38 @@ }; }; +&switch0 { + ports { + port@0 { + reg = <0>; + label = "lan4"; + + phy-handle = <&phy1>; + }; + + port@1 { + reg = <1>; + label = "lan3"; + + phy-handle = <&phy2>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + + phy-handle = <&phy3>; + }; + + port@3 { + reg = <3>; + label = "lan1"; + + phy-handle = <&phy4>; + }; + }; +}; + &uart0 { status = "okay"; }; diff --git a/target/linux/bmips/dts/bcm6318.dtsi b/target/linux/bmips/dts/bcm6318.dtsi index f5cfd77438..e7dc38e937 100644 --- a/target/linux/bmips/dts/bcm6318.dtsi +++ b/target/linux/bmips/dts/bcm6318.dtsi @@ -346,5 +346,103 @@ status = "disabled"; }; + + switch0: switch@10080000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6328-switch"; + reg = <0x10080000 0x8000>; + big-endian; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@8 { + reg = <8>; + label = "cpu"; + + phy-mode = "internal"; + ethernet = <ðernet>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + + mdio: mdio@100800b0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6368-mdio-mux"; + reg = <0x100800b0 0x8>; + + mdio_int: mdio@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + phy2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + phy3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + phy4: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + }; + }; + + mdio_ext: mdio@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; + + ethernet: ethernet@10088000 { + compatible = "brcm,bcm6318-enetsw"; + reg = <0x10088000 0x80>, + <0x10088200 0x80>, + <0x10088400 0x80>; + reg-names = "dma", + "dma-channels", + "dma-sram"; + + interrupt-parent = <&periph_intc>; + interrupts = , + ; + interrupt-names = "rx", + "tx"; + + clocks = <&periph_clk BCM6318_CLK_ROBOSW250>, + <&periph_clk BCM6318_CLK_ROBOSW025>, + <&ubus_clk BCM6318_UCLK_ROBOSW>; + + resets = <&periph_rst BCM6318_RST_ENETSW>, + <&periph_rst BCM6318_RST_EPHY>; + + power-domains = <&periph_pwr BCM6318_POWER_DOMAIN_EPHY0>, + <&periph_pwr BCM6318_POWER_DOMAIN_EPHY1>, + <&periph_pwr BCM6318_POWER_DOMAIN_EPHY2>, + <&periph_pwr BCM6318_POWER_DOMAIN_EPHY3>; + + dma-rx = <0>; + dma-tx = <1>; + + status = "disabled"; + }; }; }; diff --git a/target/linux/bmips/dts/bcm63268.dtsi b/target/linux/bmips/dts/bcm63268.dtsi index bf9998a552..284d2c8ef0 100644 --- a/target/linux/bmips/dts/bcm63268.dtsi +++ b/target/linux/bmips/dts/bcm63268.dtsi @@ -432,5 +432,105 @@ status = "disabled"; }; + + ethernet: ethernet@1000d800 { + compatible = "brcm,bcm63268-enetsw"; + reg = <0x1000d800 0x80>, + <0x1000da00 0x80>, + <0x1000dc00 0x80>; + reg-names = "dma", + "dma-channels", + "dma-sram"; + + interrupt-parent = <&periph_intc>; + interrupts = , + ; + interrupt-names = "rx", + "tx"; + + clocks = <&periph_clk BCM63268_CLK_GMAC>, + <&periph_clk BCM63268_CLK_ROBOSW>, + <&periph_clk BCM63268_CLK_ROBOSW250>, + <&timer_clk BCM63268_TCLK_EPHY1>, + <&timer_clk BCM63268_TCLK_EPHY2>, + <&timer_clk BCM63268_TCLK_EPHY3>, + <&timer_clk BCM63268_TCLK_GPHY1>; + + resets = <&periph_rst BCM63268_RST_ENETSW>, + <&periph_rst BCM63268_RST_EPHY>, + <&periph_rst BCM63268_RST_GPHY>; + + power-domains = <&periph_pwr BCM63268_POWER_DOMAIN_ROBOSW>; + + dma-rx = <0>; + dma-tx = <1>; + + status = "disabled"; + }; + + switch0: switch@10700000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6328-switch"; + reg = <0x10700000 0x8000>; + big-endian; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@8 { + reg = <8>; + label = "cpu"; + + phy-mode = "internal"; + ethernet = <ðernet>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + + mdio: mdio@107000b0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6368-mdio-mux"; + reg = <0x107000b0 0x8>; + + mdio_int: mdio@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + phy2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + phy3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + phy4: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + }; + }; + + mdio_ext: mdio@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; }; }; diff --git a/target/linux/bmips/dts/bcm6328-comtrend-ar-5387un.dts b/target/linux/bmips/dts/bcm6328-comtrend-ar-5387un.dts index a3192dffa4..9ebd6c5c4d 100644 --- a/target/linux/bmips/dts/bcm6328-comtrend-ar-5387un.dts +++ b/target/linux/bmips/dts/bcm6328-comtrend-ar-5387un.dts @@ -30,6 +30,12 @@ status = "okay"; }; +ðernet { + status = "okay"; + + mtd-mac-address = <&cfe 0x6a0>; +}; + &hsspi { status = "okay"; @@ -113,6 +119,38 @@ }; }; +&switch0 { + ports { + port@0 { + reg = <0>; + label = "lan1"; + + phy-handle = <&phy1>; + }; + + port@1 { + reg = <1>; + label = "lan2"; + + phy-handle = <&phy2>; + }; + + port@2 { + reg = <2>; + label = "lan3"; + + phy-handle = <&phy3>; + }; + + port@3 { + reg = <3>; + label = "lan4"; + + phy-handle = <&phy4>; + }; + }; +}; + &uart0 { status = "okay"; }; diff --git a/target/linux/bmips/dts/bcm6328.dtsi b/target/linux/bmips/dts/bcm6328.dtsi index aa90cc2a9b..64fd04142c 100644 --- a/target/linux/bmips/dts/bcm6328.dtsi +++ b/target/linux/bmips/dts/bcm6328.dtsi @@ -368,5 +368,99 @@ status = "disabled"; }; + + ethernet: ethernet@1000d800 { + compatible = "brcm,bcm6328-enetsw"; + reg = <0x1000d800 0x80>, + <0x1000da00 0x80>, + <0x1000dc00 0x80>; + reg-names = "dma", + "dma-channels", + "dma-sram"; + + interrupt-parent = <&periph_intc>; + interrupts = , + ; + interrupt-names = "rx", + "tx"; + + clocks = <&periph_clk BCM6328_CLK_ROBOSW>; + + resets = <&periph_rst BCM6328_RST_ENETSW>, + <&periph_rst BCM6328_RST_EPHY>; + + power-domains = <&periph_pwr BCM6328_POWER_DOMAIN_ROBOSW>, + <&periph_pwr BCM6328_POWER_DOMAIN_EPHY>; + + dma-rx = <0>; + dma-tx = <1>; + + status = "disabled"; + }; + + switch0: switch@10e00000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6328-switch"; + reg = <0x10e00000 0x8000>; + big-endian; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@8 { + reg = <8>; + label = "cpu"; + + phy-mode = "internal"; + ethernet = <ðernet>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + + mdio: mdio@10e000b0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6368-mdio-mux"; + reg = <0x10e000b0 0x8>; + + mdio_int: mdio@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + phy2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + phy3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + phy4: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + }; + }; + + mdio_ext: mdio@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; }; }; diff --git a/target/linux/bmips/dts/bcm6362-netgear-dgnd3700-v2.dts b/target/linux/bmips/dts/bcm6362-netgear-dgnd3700-v2.dts index 4da673cafa..fe26ebeed0 100644 --- a/target/linux/bmips/dts/bcm6362-netgear-dgnd3700-v2.dts +++ b/target/linux/bmips/dts/bcm6362-netgear-dgnd3700-v2.dts @@ -62,6 +62,12 @@ status = "okay"; }; +ðernet { + status = "okay"; + + mtd-mac-address = <&cferom 0x6a0>; +}; + &leds { status = "okay"; diff --git a/target/linux/bmips/dts/bcm6362.dtsi b/target/linux/bmips/dts/bcm6362.dtsi index f46074045c..9a3d9c1f66 100644 --- a/target/linux/bmips/dts/bcm6362.dtsi +++ b/target/linux/bmips/dts/bcm6362.dtsi @@ -465,5 +465,99 @@ status = "disabled"; }; + + ethernet: ethernet@1000d800 { + compatible = "brcm,bcm6362-enetsw"; + reg = <0x1000d800 0x80>, + <0x1000da00 0x80>, + <0x1000dc00 0x80>; + reg-names = "dma", + "dma-channels", + "dma-sram"; + + interrupt-parent = <&periph_intc>; + interrupts = ; + interrupt-names = "rx"; + + clocks = <&periph_clk BCM6362_CLK_SWPKT_USB>, + <&periph_clk BCM6362_CLK_SWPKT_SAR>, + <&periph_clk BCM6362_CLK_ROBOSW>; + + resets = <&periph_rst BCM6362_RST_ENETSW>, + <&periph_rst BCM6362_RST_EPHY>; + + power-domains = <&periph_pwr BCM6362_POWER_DOMAIN_ROBOSW>, + <&periph_pwr BCM6362_POWER_DOMAIN_GMII_PADS>; + + dma-rx = <0>; + dma-tx = <1>; + + status = "disabled"; + }; + + switch0: switch@10e00000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6328-switch"; + reg = <0x10e00000 0x8000>; + big-endian; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@8 { + reg = <8>; + label = "cpu"; + + phy-mode = "internal"; + ethernet = <ðernet>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + + mdio: mdio@10e000b0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6368-mdio-mux"; + reg = <0x10e000b0 0x8>; + + mdio_int: mdio@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + phy2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + phy3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + phy4: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + }; + }; + + mdio_ext: mdio@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; }; }; diff --git a/target/linux/bmips/dts/bcm6368-comtrend-vr-3025u.dts b/target/linux/bmips/dts/bcm6368-comtrend-vr-3025u.dts index 683b01875e..b597a05f3a 100644 --- a/target/linux/bmips/dts/bcm6368-comtrend-vr-3025u.dts +++ b/target/linux/bmips/dts/bcm6368-comtrend-vr-3025u.dts @@ -59,6 +59,12 @@ status = "okay"; }; +ðernet { + status = "okay"; + + mtd-mac-address = <&cfe 0x6a0>; +}; + &ohci { status = "okay"; }; @@ -96,6 +102,38 @@ &pinctrl_ephy2_led &pinctrl_ephy3_led>; }; +&switch0 { + ports { + port@0 { + reg = <0>; + label = "lan1"; + + phy-handle = <&phy1>; + }; + + port@1 { + reg = <1>; + label = "lan2"; + + phy-handle = <&phy2>; + }; + + port@2 { + reg = <2>; + label = "lan3"; + + phy-handle = <&phy3>; + }; + + port@3 { + reg = <3>; + label = "lan4"; + + phy-handle = <&phy4>; + }; + }; +}; + &uart0 { status = "okay"; }; diff --git a/target/linux/bmips/dts/bcm6368.dtsi b/target/linux/bmips/dts/bcm6368.dtsi index 5aa2b82221..5b5064b29e 100644 --- a/target/linux/bmips/dts/bcm6368.dtsi +++ b/target/linux/bmips/dts/bcm6368.dtsi @@ -467,6 +467,99 @@ resets = <&periph_rst BCM6368_RST_IPSEC>; }; + + ethernet: ethernet@10006800 { + compatible = "brcm,bcm6368-enetsw"; + reg = <0x10006800 0x80>, + <0x10006a00 0x80>, + <0x10006c00 0x80>; + reg-names = "dma", + "dma-channels", + "dma-sram"; + + interrupt-parent = <&periph_intc>; + interrupts = , + ; + interrupt-names = "rx", + "tx"; + + clocks = <&periph_clk BCM6368_CLK_SWPKT_USB>, + <&periph_clk BCM6368_CLK_SWPKT_SAR>, + <&periph_clk BCM6368_CLK_ROBOSW>; + + resets = <&periph_rst BCM6368_RST_SWITCH>, + <&periph_rst BCM6368_RST_EPHY>; + + dma-rx = <0>; + dma-tx = <1>; + + status = "disabled"; + }; + + switch0: switch@10f00000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6328-switch"; + reg = <0x10f00000 0x8000>; + big-endian; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@8 { + reg = <8>; + label = "cpu"; + + phy-mode = "internal"; + ethernet = <ðernet>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + + mdio: mdio@10f000b0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,bcm6368-mdio-mux"; + reg = <0x10f000b0 0x8>; + + mdio_int: mdio@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + phy2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + phy3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + phy4: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + }; + }; + + mdio_ext: mdio@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; }; pflash: nor@18000000 { diff --git a/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6368-enetsw.c b/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6368-enetsw.c new file mode 100644 index 0000000000..2a27118aa7 --- /dev/null +++ b/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6368-enetsw.c @@ -0,0 +1,1089 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * BCM6368 Ethernet Switch Controller Driver + * + * Copyright (C) 2021 Álvaro Fernández Rojas + * Copyright (C) 2015 Jonas Gorski + * Copyright (C) 2008 Maxime Bizon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MTU */ +#define ENETSW_TAG_SIZE 6 +#define ENETSW_MTU_OVERHEAD (VLAN_ETH_HLEN + VLAN_HLEN + \ + ENETSW_TAG_SIZE) + +/* default number of descriptor */ +#define ENETSW_DEF_RX_DESC 64 +#define ENETSW_DEF_TX_DESC 32 +#define ENETSW_DEF_CPY_BREAK 128 + +/* maximum burst len for dma (4 bytes unit) */ +#define ENETSW_DMA_MAXBURST 8 + +/* DMA channels */ +#define DMA_CHAN_WIDTH 0x10 + +/* Controller Configuration Register */ +#define DMA_CFG_REG 0x0 +#define DMA_CFG_EN_SHIFT 0 +#define DMA_CFG_EN_MASK (1 << DMA_CFG_EN_SHIFT) +#define DMA_CFG_FLOWCH_MASK(x) (1 << ((x >> 1) + 1)) + +/* Flow Control Descriptor Low Threshold register */ +#define DMA_FLOWCL_REG(x) (0x4 + (x) * 6) + +/* Flow Control Descriptor High Threshold register */ +#define DMA_FLOWCH_REG(x) (0x8 + (x) * 6) + +/* Flow Control Descriptor Buffer Alloca Threshold register */ +#define DMA_BUFALLOC_REG(x) (0xc + (x) * 6) +#define DMA_BUFALLOC_FORCE_SHIFT 31 +#define DMA_BUFALLOC_FORCE_MASK (1 << DMA_BUFALLOC_FORCE_SHIFT) + +/* Channel Configuration register */ +#define DMAC_CHANCFG_REG 0x0 +#define DMAC_CHANCFG_EN_SHIFT 0 +#define DMAC_CHANCFG_EN_MASK (1 << DMAC_CHANCFG_EN_SHIFT) +#define DMAC_CHANCFG_PKTHALT_SHIFT 1 +#define DMAC_CHANCFG_PKTHALT_MASK (1 << DMAC_CHANCFG_PKTHALT_SHIFT) +#define DMAC_CHANCFG_BUFHALT_SHIFT 2 +#define DMAC_CHANCFG_BUFHALT_MASK (1 << DMAC_CHANCFG_BUFHALT_SHIFT) +#define DMAC_CHANCFG_CHAINING_SHIFT 2 +#define DMAC_CHANCFG_CHAINING_MASK (1 << DMAC_CHANCFG_CHAINING_SHIFT) +#define DMAC_CHANCFG_WRAP_EN_SHIFT 3 +#define DMAC_CHANCFG_WRAP_EN_MASK (1 << DMAC_CHANCFG_WRAP_EN_SHIFT) +#define DMAC_CHANCFG_FLOWC_EN_SHIFT 4 +#define DMAC_CHANCFG_FLOWC_EN_MASK (1 << DMAC_CHANCFG_FLOWC_EN_SHIFT) + +/* Interrupt Control/Status register */ +#define DMAC_IR_REG 0x4 +#define DMAC_IR_BUFDONE_MASK (1 << 0) +#define DMAC_IR_PKTDONE_MASK (1 << 1) +#define DMAC_IR_NOTOWNER_MASK (1 << 2) + +/* Interrupt Mask register */ +#define DMAC_IRMASK_REG 0x8 + +/* Maximum Burst Length */ +#define DMAC_MAXBURST_REG 0xc + +/* Ring Start Address register */ +#define DMAS_RSTART_REG 0x0 + +/* State Ram Word 2 */ +#define DMAS_SRAM2_REG 0x4 + +/* State Ram Word 3 */ +#define DMAS_SRAM3_REG 0x8 + +/* State Ram Word 4 */ +#define DMAS_SRAM4_REG 0xc + +struct bcm6368_enetsw_desc { + u32 len_stat; + u32 address; +}; + +/* control */ +#define DMADESC_LENGTH_SHIFT 16 +#define DMADESC_LENGTH_MASK (0xfff << DMADESC_LENGTH_SHIFT) +#define DMADESC_OWNER_MASK (1 << 15) +#define DMADESC_EOP_MASK (1 << 14) +#define DMADESC_SOP_MASK (1 << 13) +#define DMADESC_ESOP_MASK (DMADESC_EOP_MASK | DMADESC_SOP_MASK) +#define DMADESC_WRAP_MASK (1 << 12) +#define DMADESC_USB_NOZERO_MASK (1 << 1) +#define DMADESC_USB_ZERO_MASK (1 << 0) + +/* status */ +#define DMADESC_UNDER_MASK (1 << 9) +#define DMADESC_APPEND_CRC (1 << 8) +#define DMADESC_OVSIZE_MASK (1 << 4) +#define DMADESC_RXER_MASK (1 << 2) +#define DMADESC_CRC_MASK (1 << 1) +#define DMADESC_OV_MASK (1 << 0) +#define DMADESC_ERR_MASK (DMADESC_UNDER_MASK | \ + DMADESC_OVSIZE_MASK | \ + DMADESC_RXER_MASK | \ + DMADESC_CRC_MASK | \ + DMADESC_OV_MASK) + +struct bcm6368_enetsw { + void __iomem *dma_base; + void __iomem *dma_chan; + void __iomem *dma_sram; + + struct device **pm; + struct device_link **link_pm; + int num_pms; + + struct clk **clock; + unsigned int num_clocks; + + struct reset_control **reset; + unsigned int num_resets; + + int copybreak; + + int irq_rx; + int irq_tx; + + /* hw view of rx & tx dma ring */ + dma_addr_t rx_desc_dma; + dma_addr_t tx_desc_dma; + + /* allocated size (in bytes) for rx & tx dma ring */ + unsigned int rx_desc_alloc_size; + unsigned int tx_desc_alloc_size; + + struct napi_struct napi; + + /* dma channel id for rx */ + int rx_chan; + + /* number of dma desc in rx ring */ + int rx_ring_size; + + /* cpu view of rx dma ring */ + struct bcm6368_enetsw_desc *rx_desc_cpu; + + /* current number of armed descriptor given to hardware for rx */ + int rx_desc_count; + + /* next rx descriptor to fetch from hardware */ + int rx_curr_desc; + + /* next dirty rx descriptor to refill */ + int rx_dirty_desc; + + /* size of allocated rx skbs */ + unsigned int rx_skb_size; + + /* list of skb given to hw for rx */ + struct sk_buff **rx_skb; + + /* used when rx skb allocation failed, so we defer rx queue + * refill */ + struct timer_list rx_timeout; + + /* lock rx_timeout against rx normal operation */ + spinlock_t rx_lock; + + /* dma channel id for tx */ + int tx_chan; + + /* number of dma desc in tx ring */ + int tx_ring_size; + + /* maximum dma burst size */ + int dma_maxburst; + + /* cpu view of rx dma ring */ + struct bcm6368_enetsw_desc *tx_desc_cpu; + + /* number of available descriptor for tx */ + int tx_desc_count; + + /* next tx descriptor avaiable */ + int tx_curr_desc; + + /* next dirty tx descriptor to reclaim */ + int tx_dirty_desc; + + /* list of skb given to hw for tx */ + struct sk_buff **tx_skb; + + /* lock used by tx reclaim and xmit */ + spinlock_t tx_lock; + + /* network device reference */ + struct net_device *net_dev; + + /* platform device reference */ + struct platform_device *pdev; + + /* dma channel enable mask */ + u32 dma_chan_en_mask; + + /* dma channel interrupt mask */ + u32 dma_chan_int_mask; + + /* dma channel width */ + unsigned int dma_chan_width; +}; + +static inline void dma_writel(struct bcm6368_enetsw *priv, u32 val, u32 off) +{ + __raw_writel(val, priv->dma_base + off); +} + +static inline u32 dma_readl(struct bcm6368_enetsw *priv, u32 off, int chan) +{ + return __raw_readl(priv->dma_chan + off + chan * priv->dma_chan_width); +} + +static inline void dmac_writel(struct bcm6368_enetsw *priv, u32 val, + u32 off, int chan) +{ + __raw_writel(val, priv->dma_chan + off + chan * priv->dma_chan_width); +} + +static inline void dmas_writel(struct bcm6368_enetsw *priv, u32 val, + u32 off, int chan) +{ + __raw_writel(val, priv->dma_sram + off + chan * priv->dma_chan_width); +} + +/* + * refill rx queue + */ +static int bcm6368_enetsw_refill_rx(struct net_device *dev) +{ + struct bcm6368_enetsw *priv = netdev_priv(dev); + + while (priv->rx_desc_count < priv->rx_ring_size) { + struct bcm6368_enetsw_desc *desc; + struct sk_buff *skb; + dma_addr_t p; + int desc_idx; + u32 len_stat; + + desc_idx = priv->rx_dirty_desc; + desc = &priv->rx_desc_cpu[desc_idx]; + + if (!priv->rx_skb[desc_idx]) { + skb = netdev_alloc_skb(dev, priv->rx_skb_size); + if (!skb) + break; + priv->rx_skb[desc_idx] = skb; + p = dma_map_single(&priv->pdev->dev, skb->data, + priv->rx_skb_size, + DMA_FROM_DEVICE); + desc->address = p; + } + + len_stat = priv->rx_skb_size << DMADESC_LENGTH_SHIFT; + len_stat |= DMADESC_OWNER_MASK; + if (priv->rx_dirty_desc == priv->rx_ring_size - 1) { + len_stat |= DMADESC_WRAP_MASK; + priv->rx_dirty_desc = 0; + } else { + priv->rx_dirty_desc++; + } + wmb(); + desc->len_stat = len_stat; + + priv->rx_desc_count++; + + /* tell dma engine we allocated one buffer */ + dma_writel(priv, 1, DMA_BUFALLOC_REG(priv->rx_chan)); + } + + /* If rx ring is still empty, set a timer to try allocating + * again at a later time. */ + if (priv->rx_desc_count == 0 && netif_running(dev)) { + dev_warn(&priv->pdev->dev, "unable to refill rx ring\n"); + priv->rx_timeout.expires = jiffies + HZ; + add_timer(&priv->rx_timeout); + } + + return 0; +} + +/* + * timer callback to defer refill rx queue in case we're OOM + */ +static void bcm6368_enetsw_refill_rx_timer(struct timer_list *t) +{ + struct bcm6368_enetsw *priv = from_timer(priv, t, rx_timeout); + struct net_device *dev = priv->net_dev; + + spin_lock(&priv->rx_lock); + bcm6368_enetsw_refill_rx(dev); + spin_unlock(&priv->rx_lock); +} + +/* + * extract packet from rx queue + */ +static int bcm6368_enetsw_receive_queue(struct net_device *dev, int budget) +{ + struct bcm6368_enetsw *priv = netdev_priv(dev); + struct device *kdev = &priv->pdev->dev; + int processed = 0; + + /* don't scan ring further than number of refilled + * descriptor */ + if (budget > priv->rx_desc_count) + budget = priv->rx_desc_count; + + do { + struct bcm6368_enetsw_desc *desc; + struct sk_buff *skb; + int desc_idx; + u32 len_stat; + unsigned int len; + + desc_idx = priv->rx_curr_desc; + desc = &priv->rx_desc_cpu[desc_idx]; + + /* make sure we actually read the descriptor status at + * each loop */ + rmb(); + + len_stat = desc->len_stat; + + /* break if dma ownership belongs to hw */ + if (len_stat & DMADESC_OWNER_MASK) + break; + + processed++; + priv->rx_curr_desc++; + if (priv->rx_curr_desc == priv->rx_ring_size) + priv->rx_curr_desc = 0; + priv->rx_desc_count--; + + /* if the packet does not have start of packet _and_ + * end of packet flag set, then just recycle it */ + if ((len_stat & DMADESC_ESOP_MASK) != DMADESC_ESOP_MASK) { + dev->stats.rx_dropped++; + continue; + } + + /* valid packet */ + skb = priv->rx_skb[desc_idx]; + len = (len_stat & DMADESC_LENGTH_MASK) + >> DMADESC_LENGTH_SHIFT; + /* don't include FCS */ + len -= 4; + + if (len < priv->copybreak) { + struct sk_buff *nskb; + + nskb = napi_alloc_skb(&priv->napi, len); + if (!nskb) { + /* forget packet, just rearm desc */ + dev->stats.rx_dropped++; + continue; + } + + dma_sync_single_for_cpu(kdev, desc->address, + len, DMA_FROM_DEVICE); + memcpy(nskb->data, skb->data, len); + dma_sync_single_for_device(kdev, desc->address, + len, DMA_FROM_DEVICE); + skb = nskb; + } else { + dma_unmap_single(&priv->pdev->dev, desc->address, + priv->rx_skb_size, DMA_FROM_DEVICE); + priv->rx_skb[desc_idx] = NULL; + } + + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, dev); + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; + netif_receive_skb(skb); + } while (--budget > 0); + + if (processed || !priv->rx_desc_count) { + bcm6368_enetsw_refill_rx(dev); + + /* kick rx dma */ + dmac_writel(priv, priv->dma_chan_en_mask, + DMAC_CHANCFG_REG, priv->rx_chan); + } + + return processed; +} + +/* + * try to or force reclaim of transmitted buffers + */ +static int bcm6368_enetsw_tx_reclaim(struct net_device *dev, int force) +{ + struct bcm6368_enetsw *priv = netdev_priv(dev); + int released = 0; + + while (priv->tx_desc_count < priv->tx_ring_size) { + struct bcm6368_enetsw_desc *desc; + struct sk_buff *skb; + + /* We run in a bh and fight against start_xmit, which + * is called with bh disabled */ + spin_lock(&priv->tx_lock); + + desc = &priv->tx_desc_cpu[priv->tx_dirty_desc]; + + if (!force && (desc->len_stat & DMADESC_OWNER_MASK)) { + spin_unlock(&priv->tx_lock); + break; + } + + /* ensure other field of the descriptor were not read + * before we checked ownership */ + rmb(); + + skb = priv->tx_skb[priv->tx_dirty_desc]; + priv->tx_skb[priv->tx_dirty_desc] = NULL; + dma_unmap_single(&priv->pdev->dev, desc->address, skb->len, + DMA_TO_DEVICE); + + priv->tx_dirty_desc++; + if (priv->tx_dirty_desc == priv->tx_ring_size) + priv->tx_dirty_desc = 0; + priv->tx_desc_count++; + + spin_unlock(&priv->tx_lock); + + if (desc->len_stat & DMADESC_UNDER_MASK) + dev->stats.tx_errors++; + + dev_kfree_skb(skb); + released++; + } + + if (netif_queue_stopped(dev) && released) + netif_wake_queue(dev); + + return released; +} + +/* + * poll func, called by network core + */ +static int bcm6368_enetsw_poll(struct napi_struct *napi, int budget) +{ + struct bcm6368_enetsw *priv = container_of(napi, struct bcm6368_enetsw, napi); + struct net_device *dev = priv->net_dev; + int rx_work_done; + + /* ack interrupts */ + dmac_writel(priv, priv->dma_chan_int_mask, + DMAC_IR_REG, priv->rx_chan); + dmac_writel(priv, priv->dma_chan_int_mask, + DMAC_IR_REG, priv->tx_chan); + + /* reclaim sent skb */ + bcm6368_enetsw_tx_reclaim(dev, 0); + + spin_lock(&priv->rx_lock); + rx_work_done = bcm6368_enetsw_receive_queue(dev, budget); + spin_unlock(&priv->rx_lock); + + if (rx_work_done >= budget) { + /* rx queue is not yet empty/clean */ + return rx_work_done; + } + + /* no more packet in rx/tx queue, remove device from poll + * queue */ + napi_complete_done(napi, rx_work_done); + + /* restore rx/tx interrupt */ + dmac_writel(priv, priv->dma_chan_int_mask, + DMAC_IRMASK_REG, priv->rx_chan); + dmac_writel(priv, priv->dma_chan_int_mask, + DMAC_IRMASK_REG, priv->tx_chan); + + return rx_work_done; +} + +/* + * rx/tx dma interrupt handler + */ +static irqreturn_t bcm6368_enetsw_isr_dma(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct bcm6368_enetsw *priv = netdev_priv(dev); + + /* mask rx/tx interrupts */ + dmac_writel(priv, 0, DMAC_IRMASK_REG, priv->rx_chan); + dmac_writel(priv, 0, DMAC_IRMASK_REG, priv->tx_chan); + + napi_schedule(&priv->napi); + + return IRQ_HANDLED; +} + +/* + * tx request callback + */ +static netdev_tx_t +bcm6368_enetsw_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct bcm6368_enetsw *priv = netdev_priv(dev); + struct bcm6368_enetsw_desc *desc; + u32 len_stat; + netdev_tx_t ret; + + /* lock against tx reclaim */ + spin_lock(&priv->tx_lock); + + /* make sure the tx hw queue is not full, should not happen + * since we stop queue before it's the case */ + if (unlikely(!priv->tx_desc_count)) { + netif_stop_queue(dev); + dev_err(&priv->pdev->dev, "xmit called with no tx desc " + "available?\n"); + ret = NETDEV_TX_BUSY; + goto out_unlock; + } + + /* pad small packets */ + if (skb->len < (ETH_ZLEN + ETH_FCS_LEN)) { + int needed = (ETH_ZLEN + ETH_FCS_LEN) - skb->len; + char *data; + + if (unlikely(skb_tailroom(skb) < needed)) { + struct sk_buff *nskb; + + nskb = skb_copy_expand(skb, 0, needed, GFP_ATOMIC); + if (!nskb) { + ret = NETDEV_TX_BUSY; + goto out_unlock; + } + + dev_kfree_skb(skb); + skb = nskb; + } + data = skb_put_zero(skb, needed); + } + + /* point to the next available desc */ + desc = &priv->tx_desc_cpu[priv->tx_curr_desc]; + priv->tx_skb[priv->tx_curr_desc] = skb; + + /* fill descriptor */ + desc->address = dma_map_single(&priv->pdev->dev, skb->data, skb->len, + DMA_TO_DEVICE); + + len_stat = (skb->len << DMADESC_LENGTH_SHIFT) & DMADESC_LENGTH_MASK; + len_stat |= DMADESC_ESOP_MASK | DMADESC_APPEND_CRC | + DMADESC_OWNER_MASK; + + priv->tx_curr_desc++; + if (priv->tx_curr_desc == priv->tx_ring_size) { + priv->tx_curr_desc = 0; + len_stat |= DMADESC_WRAP_MASK; + } + priv->tx_desc_count--; + + /* dma might be already polling, make sure we update desc + * fields in correct order */ + wmb(); + desc->len_stat = len_stat; + wmb(); + + /* kick tx dma */ + dmac_writel(priv, priv->dma_chan_en_mask, DMAC_CHANCFG_REG, + priv->tx_chan); + + /* stop queue if no more desc available */ + if (!priv->tx_desc_count) + netif_stop_queue(dev); + + dev->stats.tx_bytes += skb->len; + dev->stats.tx_packets++; + ret = NETDEV_TX_OK; + +out_unlock: + spin_unlock(&priv->tx_lock); + return ret; +} + +/* + * disable dma in given channel + */ +static void bcm6368_enetsw_disable_dma(struct bcm6368_enetsw *priv, int chan) +{ + int limit = 1000; + + dmac_writel(priv, 0, DMAC_CHANCFG_REG, chan); + + do { + u32 val; + + val = dma_readl(priv, DMAC_CHANCFG_REG, chan); + if (!(val & DMAC_CHANCFG_EN_MASK)) + break; + + udelay(1); + } while (limit--); +} + +static int bcm6368_enetsw_open(struct net_device *dev) +{ + struct bcm6368_enetsw *priv = netdev_priv(dev); + struct device *kdev = &priv->pdev->dev; + int i, ret; + unsigned int size; + void *p; + u32 val; + + /* mask all interrupts and request them */ + dmac_writel(priv, 0, DMAC_IRMASK_REG, priv->rx_chan); + dmac_writel(priv, 0, DMAC_IRMASK_REG, priv->tx_chan); + + ret = request_irq(priv->irq_rx, bcm6368_enetsw_isr_dma, + 0, dev->name, dev); + if (ret) + goto out_freeirq; + + if (priv->irq_tx != -1) { + ret = request_irq(priv->irq_tx, bcm6368_enetsw_isr_dma, + 0, dev->name, dev); + if (ret) + goto out_freeirq_rx; + } + + /* allocate rx dma ring */ + size = priv->rx_ring_size * sizeof(struct bcm6368_enetsw_desc); + p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma, GFP_KERNEL); + if (!p) { + dev_err(kdev, "cannot allocate rx ring %u\n", size); + ret = -ENOMEM; + goto out_freeirq_tx; + } + + memset(p, 0, size); + priv->rx_desc_alloc_size = size; + priv->rx_desc_cpu = p; + + /* allocate tx dma ring */ + size = priv->tx_ring_size * sizeof(struct bcm6368_enetsw_desc); + p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma, GFP_KERNEL); + if (!p) { + dev_err(kdev, "cannot allocate tx ring\n"); + ret = -ENOMEM; + goto out_free_rx_ring; + } + + memset(p, 0, size); + priv->tx_desc_alloc_size = size; + priv->tx_desc_cpu = p; + + priv->tx_skb = kzalloc(sizeof(struct sk_buff *) * priv->tx_ring_size, + GFP_KERNEL); + if (!priv->tx_skb) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out_free_tx_ring; + } + + priv->tx_desc_count = priv->tx_ring_size; + priv->tx_dirty_desc = 0; + priv->tx_curr_desc = 0; + spin_lock_init(&priv->tx_lock); + + /* init & fill rx ring with skbs */ + priv->rx_skb = kzalloc(sizeof(struct sk_buff *) * priv->rx_ring_size, + GFP_KERNEL); + if (!priv->rx_skb) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out_free_tx_skb; + } + + priv->rx_desc_count = 0; + priv->rx_dirty_desc = 0; + priv->rx_curr_desc = 0; + + /* initialize flow control buffer allocation */ + dma_writel(priv, DMA_BUFALLOC_FORCE_MASK | 0, + DMA_BUFALLOC_REG(priv->rx_chan)); + + if (bcm6368_enetsw_refill_rx(dev)) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out; + } + + /* write rx & tx ring addresses */ + dmas_writel(priv, priv->rx_desc_dma, + DMAS_RSTART_REG, priv->rx_chan); + dmas_writel(priv, priv->tx_desc_dma, + DMAS_RSTART_REG, priv->tx_chan); + + /* clear remaining state ram for rx & tx channel */ + dmas_writel(priv, 0, DMAS_SRAM2_REG, priv->rx_chan); + dmas_writel(priv, 0, DMAS_SRAM2_REG, priv->tx_chan); + dmas_writel(priv, 0, DMAS_SRAM3_REG, priv->rx_chan); + dmas_writel(priv, 0, DMAS_SRAM3_REG, priv->tx_chan); + dmas_writel(priv, 0, DMAS_SRAM4_REG, priv->rx_chan); + dmas_writel(priv, 0, DMAS_SRAM4_REG, priv->tx_chan); + + /* set dma maximum burst len */ + dmac_writel(priv, priv->dma_maxburst, + DMAC_MAXBURST_REG, priv->rx_chan); + dmac_writel(priv, priv->dma_maxburst, + DMAC_MAXBURST_REG, priv->tx_chan); + + /* set flow control low/high threshold to 1/3 / 2/3 */ + val = priv->rx_ring_size / 3; + dma_writel(priv, val, DMA_FLOWCL_REG(priv->rx_chan)); + val = (priv->rx_ring_size * 2) / 3; + dma_writel(priv, val, DMA_FLOWCH_REG(priv->rx_chan)); + + /* all set, enable mac and interrupts, start dma engine and + * kick rx dma channel + */ + wmb(); + dma_writel(priv, DMA_CFG_EN_MASK, DMA_CFG_REG); + dmac_writel(priv, DMAC_CHANCFG_EN_MASK, + DMAC_CHANCFG_REG, priv->rx_chan); + + /* watch "packet transferred" interrupt in rx and tx */ + dmac_writel(priv, DMAC_IR_PKTDONE_MASK, + DMAC_IR_REG, priv->rx_chan); + dmac_writel(priv, DMAC_IR_PKTDONE_MASK, + DMAC_IR_REG, priv->tx_chan); + + /* make sure we enable napi before rx interrupt */ + napi_enable(&priv->napi); + + dmac_writel(priv, DMAC_IR_PKTDONE_MASK, + DMAC_IRMASK_REG, priv->rx_chan); + dmac_writel(priv, DMAC_IR_PKTDONE_MASK, + DMAC_IRMASK_REG, priv->tx_chan); + + netif_carrier_on(dev); + netif_start_queue(dev); + + return 0; + +out: + for (i = 0; i < priv->rx_ring_size; i++) { + struct bcm6368_enetsw_desc *desc; + + if (!priv->rx_skb[i]) + continue; + + desc = &priv->rx_desc_cpu[i]; + dma_unmap_single(kdev, desc->address, priv->rx_skb_size, + DMA_FROM_DEVICE); + kfree_skb(priv->rx_skb[i]); + } + kfree(priv->rx_skb); + +out_free_tx_skb: + kfree(priv->tx_skb); + +out_free_tx_ring: + dma_free_coherent(kdev, priv->tx_desc_alloc_size, + priv->tx_desc_cpu, priv->tx_desc_dma); + +out_free_rx_ring: + dma_free_coherent(kdev, priv->rx_desc_alloc_size, + priv->rx_desc_cpu, priv->rx_desc_dma); + +out_freeirq_tx: + if (priv->irq_tx != -1) + free_irq(priv->irq_tx, dev); + +out_freeirq_rx: + free_irq(priv->irq_rx, dev); + +out_freeirq: + return ret; +} + +static int bcm6368_enetsw_stop(struct net_device *dev) +{ + struct bcm6368_enetsw *priv = netdev_priv(dev); + struct device *kdev = &priv->pdev->dev; + int i; + + netif_stop_queue(dev); + napi_disable(&priv->napi); + del_timer_sync(&priv->rx_timeout); + + /* mask all interrupts */ + dmac_writel(priv, 0, DMAC_IRMASK_REG, priv->rx_chan); + dmac_writel(priv, 0, DMAC_IRMASK_REG, priv->tx_chan); + + /* disable dma & mac */ + bcm6368_enetsw_disable_dma(priv, priv->tx_chan); + bcm6368_enetsw_disable_dma(priv, priv->rx_chan); + + /* force reclaim of all tx buffers */ + bcm6368_enetsw_tx_reclaim(dev, 1); + + /* free the rx skb ring */ + for (i = 0; i < priv->rx_ring_size; i++) { + struct bcm6368_enetsw_desc *desc; + + if (!priv->rx_skb[i]) + continue; + + desc = &priv->rx_desc_cpu[i]; + dma_unmap_single_attrs(kdev, desc->address, priv->rx_skb_size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + kfree_skb(priv->rx_skb[i]); + } + + /* free remaining allocated memory */ + kfree(priv->rx_skb); + kfree(priv->tx_skb); + dma_free_coherent(kdev, priv->rx_desc_alloc_size, + priv->rx_desc_cpu, priv->rx_desc_dma); + dma_free_coherent(kdev, priv->tx_desc_alloc_size, + priv->tx_desc_cpu, priv->tx_desc_dma); + if (priv->irq_tx != -1) + free_irq(priv->irq_tx, dev); + free_irq(priv->irq_rx, dev); + + return 0; +} + +static const struct net_device_ops bcm6368_enetsw_ops = { + .ndo_open = bcm6368_enetsw_open, + .ndo_stop = bcm6368_enetsw_stop, + .ndo_start_xmit = bcm6368_enetsw_start_xmit, +}; + +static int bcm6368_enetsw_probe(struct platform_device *pdev) +{ + struct bcm6368_enetsw *priv; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct net_device *ndev; + struct resource *res; + const void *mac; + unsigned i; + int ret; + + ndev = alloc_etherdev(sizeof(*priv)); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + + priv->num_pms = of_count_phandle_with_args(node, "power-domains", + "#power-domain-cells"); + if (priv->num_pms > 1) { + priv->pm = devm_kcalloc(dev, priv->num_pms, + sizeof(struct device *), GFP_KERNEL); + if (!priv->pm) + return -ENOMEM; + + priv->link_pm = devm_kcalloc(dev, priv->num_pms, + sizeof(struct device_link *), + GFP_KERNEL); + if (!priv->link_pm) + return -ENOMEM; + + for (i = 0; i < priv->num_pms; i++) { + priv->pm[i] = genpd_dev_pm_attach_by_id(dev, i); + if (IS_ERR(priv->pm[i])) { + dev_err(dev, "error getting pm %d\n", i); + return -EINVAL; + } + + priv->link_pm[i] = device_link_add(dev, priv->pm[i], + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + } + } + + pm_runtime_enable(dev); + pm_runtime_no_callbacks(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_disable(dev); + dev_info(dev, "PM prober defer: ret=%d\n", ret); + return -EPROBE_DEFER; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + priv->dma_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->dma_base)) + return PTR_ERR(priv->dma_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "dma-channels"); + priv->dma_chan = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->dma_chan)) + return PTR_ERR(priv->dma_chan); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma-sram"); + priv->dma_sram = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->dma_sram)) + return PTR_ERR(priv->dma_sram); + + priv->irq_rx = platform_get_irq_byname(pdev, "rx"); + if (!priv->irq_rx) + return -ENODEV; + + priv->irq_tx = platform_get_irq_byname(pdev, "tx"); + if (!priv->irq_tx) + return -ENODEV; + else if (priv->irq_tx < 0) + priv->irq_tx = -1; + + if (device_property_read_u32(dev, "dma-rx", &priv->rx_chan)) + return -ENODEV; + + if (device_property_read_u32(dev, "dma-tx", &priv->tx_chan)) + return -ENODEV; + + priv->rx_ring_size = ENETSW_DEF_RX_DESC; + priv->tx_ring_size = ENETSW_DEF_TX_DESC; + + priv->dma_maxburst = ENETSW_DMA_MAXBURST; + + priv->copybreak = ENETSW_DEF_CPY_BREAK; + + priv->dma_chan_en_mask = DMAC_CHANCFG_EN_MASK; + priv->dma_chan_int_mask = DMAC_IR_PKTDONE_MASK; + priv->dma_chan_width = DMA_CHAN_WIDTH; + + mac = of_get_mac_address(node); + if (!IS_ERR_OR_NULL(mac)) { + memcpy(ndev->dev_addr, mac, ETH_ALEN); + dev_info(dev, "mtd mac %pM\n", ndev->dev_addr); + } else { + random_ether_addr(ndev->dev_addr); + dev_info(dev, "random mac %pM\n", ndev->dev_addr); + } + + priv->rx_skb_size = ALIGN(ndev->mtu + ENETSW_MTU_OVERHEAD, + priv->dma_maxburst * 4); + + priv->num_clocks = of_clk_get_parent_count(node); + if (priv->num_clocks) { + priv->clock = devm_kcalloc(dev, priv->num_clocks, + sizeof(struct clk *), GFP_KERNEL); + if (!priv->clock) + return -ENOMEM; + } + for (i = 0; i < priv->num_clocks; i++) { + priv->clock[i] = of_clk_get(node, i); + if (IS_ERR(priv->clock[i])) { + dev_err(dev, "error getting clock %d\n", i); + return -EINVAL; + } + + ret = clk_prepare_enable(priv->clock[i]); + if (ret) { + dev_err(dev, "error enabling clock %d\n", i); + return ret; + } + } + + priv->num_resets = of_count_phandle_with_args(node, "resets", + "#reset-cells"); + if (priv->num_resets) { + priv->reset = devm_kcalloc(dev, priv->num_resets, + sizeof(struct reset_control *), + GFP_KERNEL); + if (!priv->reset) + return -ENOMEM; + } + for (i = 0; i < priv->num_resets; i++) { + priv->reset[i] = devm_reset_control_get_by_index(dev, i); + if (IS_ERR(priv->reset[i])) { + dev_err(dev, "error getting reset %d\n", i); + return -EINVAL; + } + + ret = reset_control_reset(priv->reset[i]); + if (ret) { + dev_err(dev, "error performing reset %d\n", i); + return ret; + } + } + + spin_lock_init(&priv->rx_lock); + + timer_setup(&priv->rx_timeout, bcm6368_enetsw_refill_rx_timer, 0); + + /* register netdevice */ + ndev->netdev_ops = &bcm6368_enetsw_ops; + ndev->min_mtu = ETH_ZLEN; + ndev->mtu = ETH_DATA_LEN + ENETSW_TAG_SIZE; + ndev->max_mtu = ETH_DATA_LEN + ENETSW_TAG_SIZE; + netif_napi_add(ndev, &priv->napi, bcm6368_enetsw_poll, 16); + SET_NETDEV_DEV(ndev, dev); + + ret = register_netdev(ndev); + if (ret) + goto out_disable_clk; + + netif_carrier_off(ndev); + platform_set_drvdata(pdev, ndev); + priv->pdev = pdev; + priv->net_dev = ndev; + + return 0; + +out_disable_clk: + for (i = 0; i < priv->num_resets; i++) + reset_control_assert(priv->reset[i]); + + for (i = 0; i < priv->num_clocks; i++) + clk_disable_unprepare(priv->clock[i]); + + return ret; +} + +static int bcm6368_enetsw_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev = platform_get_drvdata(pdev); + struct bcm6368_enetsw *priv = netdev_priv(ndev); + unsigned int i; + + unregister_netdev(ndev); + + pm_runtime_put_sync(dev); + for (i = 0; priv->pm && i < priv->num_pms; i++) { + dev_pm_domain_detach(priv->pm[i], true); + device_link_del(priv->link_pm[i]); + } + + for (i = 0; i < priv->num_resets; i++) + reset_control_assert(priv->reset[i]); + + for (i = 0; i < priv->num_clocks; i++) + clk_disable_unprepare(priv->clock[i]); + + free_netdev(ndev); + + return 0; +} + +static const struct of_device_id bcm6368_enetsw_of_match[] = { + { .compatible = "brcm,bcm6318-enetsw", }, + { .compatible = "brcm,bcm6328-enetsw", }, + { .compatible = "brcm,bcm6362-enetsw", }, + { .compatible = "brcm,bcm6368-enetsw", }, + { .compatible = "brcm,bcm63268-enetsw", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm6368_enetsw_of_match); + +static struct platform_driver bcm6368_enetsw_driver = { + .driver = { + .name = "bcm6368-enetsw", + .of_match_table = of_match_ptr(bcm6368_enetsw_of_match), + }, + .probe = bcm6368_enetsw_probe, + .remove = bcm6368_enetsw_remove, +}; +module_platform_driver(bcm6368_enetsw_driver); diff --git a/target/linux/bmips/generic/base-files/etc/board.d/02_network b/target/linux/bmips/generic/base-files/etc/board.d/02_network new file mode 100644 index 0000000000..3e60660331 --- /dev/null +++ b/target/linux/bmips/generic/base-files/etc/board.d/02_network @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +. /lib/functions/uci-defaults.sh + +board_config_update + +case "$(board_name)" in +comtrend,ar-5315u |\ +comtrend,ar-5387un |\ +comtrend,vr-3025u) + ucidef_set_interface_lan "lan1 lan2 lan3 lan4" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/bmips/nand/base-files/etc/board.d/02_network b/target/linux/bmips/nand/base-files/etc/board.d/02_network new file mode 100644 index 0000000000..db62e61287 --- /dev/null +++ b/target/linux/bmips/nand/base-files/etc/board.d/02_network @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +. /lib/functions/uci-defaults.sh + +board_config_update + +case "$(board_name)" in +comtrend,vr-3032u) + ucidef_set_interface_lan "lan1 lan2 lan3 lan4" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/bmips/patches-5.10/040-v5.12-mips-bmips-init-clocks-earlier.patch b/target/linux/bmips/patches-5.10/020-v5.12-mips-bmips-init-clocks-earlier.patch similarity index 100% rename from target/linux/bmips/patches-5.10/040-v5.12-mips-bmips-init-clocks-earlier.patch rename to target/linux/bmips/patches-5.10/020-v5.12-mips-bmips-init-clocks-earlier.patch diff --git a/target/linux/bmips/patches-5.10/041-v5.12-spi-bcm63xx-spi-fix-pm_runtime.patch b/target/linux/bmips/patches-5.10/021-v5.12-spi-bcm63xx-spi-fix-pm_runtime.patch similarity index 100% rename from target/linux/bmips/patches-5.10/041-v5.12-spi-bcm63xx-spi-fix-pm_runtime.patch rename to target/linux/bmips/patches-5.10/021-v5.12-spi-bcm63xx-spi-fix-pm_runtime.patch diff --git a/target/linux/bmips/patches-5.10/042-v5.12-spi-bcm63xx-hsspi-fix-pm_runtime.patch b/target/linux/bmips/patches-5.10/022-v5.12-spi-bcm63xx-hsspi-fix-pm_runtime.patch similarity index 100% rename from target/linux/bmips/patches-5.10/042-v5.12-spi-bcm63xx-hsspi-fix-pm_runtime.patch rename to target/linux/bmips/patches-5.10/022-v5.12-spi-bcm63xx-hsspi-fix-pm_runtime.patch diff --git a/target/linux/bmips/patches-5.10/050-v5.13-mips-smp-bmips-fix-CPU-mappings.patch b/target/linux/bmips/patches-5.10/040-v5.13-mips-smp-bmips-fix-CPU-mappings.patch similarity index 100% rename from target/linux/bmips/patches-5.10/050-v5.13-mips-smp-bmips-fix-CPU-mappings.patch rename to target/linux/bmips/patches-5.10/040-v5.13-mips-smp-bmips-fix-CPU-mappings.patch diff --git a/target/linux/bmips/patches-5.10/051-v5.13-mtd-rawnand-brcmnand-fix-OOB-R-W-with-Hamming-ECC.patch b/target/linux/bmips/patches-5.10/041-v5.13-mtd-rawnand-brcmnand-fix-OOB-R-W-with-Hamming-ECC.patch similarity index 100% rename from target/linux/bmips/patches-5.10/051-v5.13-mtd-rawnand-brcmnand-fix-OOB-R-W-with-Hamming-ECC.patch rename to target/linux/bmips/patches-5.10/041-v5.13-mtd-rawnand-brcmnand-fix-OOB-R-W-with-Hamming-ECC.patch diff --git a/target/linux/bmips/patches-5.10/042-v5.13-dt-bindings-rng-bcm2835-add-clock-constraints.patch b/target/linux/bmips/patches-5.10/042-v5.13-dt-bindings-rng-bcm2835-add-clock-constraints.patch new file mode 100644 index 0000000000..246293e384 --- /dev/null +++ b/target/linux/bmips/patches-5.10/042-v5.13-dt-bindings-rng-bcm2835-add-clock-constraints.patch @@ -0,0 +1,38 @@ +From 0618e07ea3e0981d7765b43d3f7db39e739842eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Fri, 5 Mar 2021 08:01:30 +0100 +Subject: [PATCH 1/3] dt-bindings: rng: bcm2835: add clock constraints +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +brcm,bcm6368-rng controllers require enabling the IPSEC clock in order to get +a functional RNG. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Rob Herring +Acked-by: Florian Fainelli +Signed-off-by: Herbert Xu +--- + .../devicetree/bindings/rng/brcm,bcm2835.yaml | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/Documentation/devicetree/bindings/rng/brcm,bcm2835.yaml ++++ b/Documentation/devicetree/bindings/rng/brcm,bcm2835.yaml +@@ -35,6 +35,16 @@ required: + - compatible + - reg + ++if: ++ properties: ++ compatible: ++ enum: ++ - brcm,bcm6368-rng ++then: ++ required: ++ - clocks ++ - clock-names ++ + additionalProperties: false + + examples: diff --git a/target/linux/bmips/patches-5.10/043-v5.13-dt-bindings-rng-bcm2835-document-reset-support.patch b/target/linux/bmips/patches-5.10/043-v5.13-dt-bindings-rng-bcm2835-document-reset-support.patch new file mode 100644 index 0000000000..ce14a1037c --- /dev/null +++ b/target/linux/bmips/patches-5.10/043-v5.13-dt-bindings-rng-bcm2835-document-reset-support.patch @@ -0,0 +1,51 @@ +From 381345820db55bf8e7289de047c24c00a2e3690d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Fri, 5 Mar 2021 08:01:31 +0100 +Subject: [PATCH 2/3] dt-bindings: rng: bcm2835: document reset support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +brcm,bcm6368-rng controllers require resetting the IPSEC clock in order to get +a functional RNG. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Rob Herring +Acked-by: Florian Fainelli +Signed-off-by: Herbert Xu +--- + .../devicetree/bindings/rng/brcm,bcm2835.yaml | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/Documentation/devicetree/bindings/rng/brcm,bcm2835.yaml ++++ b/Documentation/devicetree/bindings/rng/brcm,bcm2835.yaml +@@ -28,6 +28,12 @@ properties: + clock-names: + const: ipsec + ++ resets: ++ maxItems: 1 ++ ++ reset-names: ++ const: ipsec ++ + interrupts: + maxItems: 1 + +@@ -44,6 +50,8 @@ then: + required: + - clocks + - clock-names ++ - resets ++ - reset-names + + additionalProperties: false + +@@ -68,4 +76,7 @@ examples: + + clocks = <&periph_clk 18>; + clock-names = "ipsec"; ++ ++ resets = <&periph_rst 4>; ++ reset-names = "ipsec"; + }; diff --git a/target/linux/bmips/patches-5.10/207-hwrng-bcm2835-add-reset-support.patch b/target/linux/bmips/patches-5.10/044-v5.13-hwrng-bcm2835-add-reset-support.patch similarity index 84% rename from target/linux/bmips/patches-5.10/207-hwrng-bcm2835-add-reset-support.patch rename to target/linux/bmips/patches-5.10/044-v5.13-hwrng-bcm2835-add-reset-support.patch index 1da0e952a8..24b74817be 100644 --- a/target/linux/bmips/patches-5.10/207-hwrng-bcm2835-add-reset-support.patch +++ b/target/linux/bmips/patches-5.10/044-v5.13-hwrng-bcm2835-add-reset-support.patch @@ -1,12 +1,14 @@ -From 846cd2c9eee7a99de29c743911cc047fdd56db02 Mon Sep 17 00:00:00 2001 +From e5f9f41d5e62004c913bfd4ddf06abe032f5ce1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= -Date: Mon, 22 Feb 2021 20:41:04 +0100 -Subject: [PATCH] hwrng: bcm2835: add reset support +Date: Fri, 5 Mar 2021 08:01:32 +0100 +Subject: [PATCH 3/3] hwrng: bcm2835 - add reset support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -BCM6368 devices need to reset the in order to generate true random numbers. +BCM6368 devices need to reset the IPSEC controller in order to generate true +random numbers. + This is what BCM6368 produces without a reset: root@OpenWrt:/# cat /dev/hwrng | rngtest -c 1000 rngtest 6.10 @@ -25,9 +27,11 @@ rngtest: FIPS 140-2(2001-10-10) Continuous run: 0 rngtest: input channel speed: (min=37.253; avg=320.827; max=635.783)Mibits/s rngtest: FIPS tests speed: (min=12.141; avg=15.034; max=16.428)Mibits/s rngtest: Program run time: 1336176 microseconds -cat: write error: Broken pipe Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Philipp Zabel +Acked-by: Florian Fainelli +Signed-off-by: Herbert Xu --- drivers/char/hw_random/bcm2835-rng.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/linux/bmips/patches-5.10/500-net-dsa-b53-relax-is63xx-condition.patch b/target/linux/bmips/patches-5.10/500-net-dsa-b53-relax-is63xx-condition.patch new file mode 100644 index 0000000000..f5dd4e2c59 --- /dev/null +++ b/target/linux/bmips/patches-5.10/500-net-dsa-b53-relax-is63xx-condition.patch @@ -0,0 +1,29 @@ +From cd6906754bbe3e0665ecaeca2cfb26d927fe9277 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Mon, 1 Mar 2021 07:29:29 +0100 +Subject: [PATCH 1/3] net: dsa: b53: relax is63xx() condition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM63xx switches are present on bcm63xx and bmips devices. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/net/dsa/b53/b53_priv.h | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -185,11 +185,7 @@ static inline int is531x5(struct b53_dev + + static inline int is63xx(struct b53_device *dev) + { +-#ifdef CONFIG_BCM63XX + return dev->chip_id == BCM63XX_DEVICE_ID; +-#else +- return 0; +-#endif + } + + static inline int is5301x(struct b53_device *dev) diff --git a/target/linux/bmips/patches-5.10/501-net-dsa-b53-mmap-Add-device-tree-support.patch b/target/linux/bmips/patches-5.10/501-net-dsa-b53-mmap-Add-device-tree-support.patch new file mode 100644 index 0000000000..e9eb87cb5c --- /dev/null +++ b/target/linux/bmips/patches-5.10/501-net-dsa-b53-mmap-Add-device-tree-support.patch @@ -0,0 +1,82 @@ +From f5419e7f362ae1c462baf28a2da7360267f8e4f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Mon, 1 Mar 2021 07:32:32 +0100 +Subject: [PATCH 2/3] net: dsa: b53: mmap: Add device tree support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add device tree support to b53_mmap.c while keeping platform devices support. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/net/dsa/b53/b53_mmap.c | 36 ++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -228,12 +228,64 @@ static const struct b53_io_ops b53_mmap_ + .write64 = b53_mmap_write64, + }; + ++static int b53_mmap_probe_of(struct platform_device *pdev, ++ struct b53_platform_data **ppdata) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *of_ports, *of_port; ++ struct b53_platform_data *pdata; ++ void __iomem *mem; ++ ++ mem = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mem)) ++ return PTR_ERR(mem); ++ ++ pdata = devm_kzalloc(dev, sizeof(struct b53_platform_data), ++ GFP_KERNEL); ++ if (!pdata) ++ return -ENOMEM; ++ ++ pdata->regs = mem; ++ pdata->chip_id = BCM63XX_DEVICE_ID; ++ pdata->big_endian = of_property_read_bool(np, "big-endian"); ++ ++ of_ports = of_get_child_by_name(np, "ports"); ++ if (!of_ports) { ++ dev_err(dev, "no ports child node found\n"); ++ return -EINVAL; ++ } ++ ++ for_each_available_child_of_node(of_ports, of_port) { ++ u32 reg; ++ ++ if (of_property_read_u32(of_port, "reg", ®)) ++ continue; ++ ++ if (reg < B53_CPU_PORT) ++ pdata->enabled_ports |= BIT(reg); ++ } ++ ++ *ppdata = pdata; ++ ++ return 0; ++} ++ + static int b53_mmap_probe(struct platform_device *pdev) + { ++ struct device_node *np = pdev->dev.of_node; + struct b53_platform_data *pdata = pdev->dev.platform_data; + struct b53_mmap_priv *priv; + struct b53_device *dev; + ++ if (np) { ++ int ret = b53_mmap_probe_of(pdev, &pdata); ++ if (ret) { ++ dev_err(&pdev->dev, "OF probe error\n"); ++ return ret; ++ } ++ } ++ + if (!pdata) + return -EINVAL; + diff --git a/target/linux/bmips/patches-5.10/502-net-dsa-b53-support-tags-for-legacy-switches.patch b/target/linux/bmips/patches-5.10/502-net-dsa-b53-support-tags-for-legacy-switches.patch new file mode 100644 index 0000000000..b8aa8b844d --- /dev/null +++ b/target/linux/bmips/patches-5.10/502-net-dsa-b53-support-tags-for-legacy-switches.patch @@ -0,0 +1,209 @@ +From 3bc3d79efdff6e29b80bf35f7a56baaa36e4d8fe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Fri, 12 Mar 2021 12:35:39 +0100 +Subject: [PATCH] net: dsa: b53: support tags for legacy switches +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/net/dsa/b53/Kconfig | 1 + + drivers/net/dsa/b53/b53_common.c | 6 +++ + include/net/dsa.h | 2 + + net/dsa/Kconfig | 7 +++ + net/dsa/tag_brcm.c | 93 ++++++++++++++++++++++++++++++++ + 5 files changed, 109 insertions(+) + +--- a/drivers/net/dsa/b53/Kconfig ++++ b/drivers/net/dsa/b53/Kconfig +@@ -3,6 +3,7 @@ menuconfig B53 + tristate "Broadcom BCM53xx managed switch support" + depends on NET_DSA + select NET_DSA_TAG_BRCM ++ select NET_DSA_TAG_BRCM_LEGACY + select NET_DSA_TAG_BRCM_PREPEND + help + This driver adds support for Broadcom managed switch chips. It supports +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1992,6 +1992,7 @@ static bool b53_can_enable_brcm_tags(str + + switch (tag_protocol) { + case DSA_TAG_PROTO_BRCM: ++ case DSA_TAG_PROTO_BRCM_LEGACY: + case DSA_TAG_PROTO_BRCM_PREPEND: + dev_warn(ds->dev, + "Port %d is stacked to Broadcom tag switch\n", port); +@@ -2013,12 +2014,16 @@ enum dsa_tag_protocol b53_get_tag_protoc + /* Older models (5325, 5365) support a different tag format that we do + * not support in net/dsa/tag_brcm.c yet. + */ +- if (is5325(dev) || is5365(dev) || +- !b53_can_enable_brcm_tags(ds, port, mprot)) { ++ if (!b53_can_enable_brcm_tags(ds, port, mprot)) { + dev->tag_protocol = DSA_TAG_PROTO_NONE; + goto out; + } + ++ if (is5325(dev) || is5365(dev) || is63xx(dev)) { ++ dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY; ++ goto out; ++ } ++ + /* Broadcom BCM58xx chips have a flow accelerator on Port 8 + * which requires us to use the prepended Broadcom tag type + */ +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -45,10 +45,12 @@ struct phylink_link_state; + #define DSA_TAG_PROTO_OCELOT_VALUE 15 + #define DSA_TAG_PROTO_AR9331_VALUE 16 + #define DSA_TAG_PROTO_RTL4_A_VALUE 17 ++#define DSA_TAG_PROTO_BRCM_LEGACY_VALUE 22 + + enum dsa_tag_protocol { + DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, + DSA_TAG_PROTO_BRCM = DSA_TAG_PROTO_BRCM_VALUE, ++ DSA_TAG_PROTO_BRCM_LEGACY = DSA_TAG_PROTO_BRCM_LEGACY_VALUE, + DSA_TAG_PROTO_BRCM_PREPEND = DSA_TAG_PROTO_BRCM_PREPEND_VALUE, + DSA_TAG_PROTO_DSA = DSA_TAG_PROTO_DSA_VALUE, + DSA_TAG_PROTO_EDSA = DSA_TAG_PROTO_EDSA_VALUE, +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -47,6 +47,13 @@ config NET_DSA_TAG_BRCM + Say Y if you want to enable support for tagging frames for the + Broadcom switches which place the tag after the MAC source address. + ++config NET_DSA_TAG_BRCM_LEGACY ++ tristate "Tag driver for Broadcom legacy switches using in-frame headers" ++ select NET_DSA_TAG_BRCM_COMMON ++ help ++ Say Y if you want to enable support for tagging frames for the ++ Broadcom legacy switches which place the tag after the MAC source ++ address. + + config NET_DSA_TAG_BRCM_PREPEND + tristate "Tag driver for Broadcom switches using prepended headers" +--- a/net/dsa/tag_brcm.c ++++ b/net/dsa/tag_brcm.c +@@ -8,9 +8,23 @@ + #include + #include + #include ++#include + + #include "dsa_priv.h" + ++struct bcm_legacy_tag { ++ uint16_t type; ++#define BRCM_LEG_TYPE 0x8874 ++ ++ uint32_t tag; ++#define BRCM_LEG_TAG_PORT_ID (0xf) ++#define BRCM_LEG_TAG_MULTICAST (1 << 29) ++#define BRCM_LEG_TAG_EGRESS (2 << 29) ++#define BRCM_LEG_TAG_INGRESS (3 << 29) ++} __attribute__((packed)); ++ ++#define BRCM_LEG_TAG_LEN sizeof(struct bcm_legacy_tag) ++ + /* This tag length is 4 bytes, older ones were 6 bytes, we do not + * handle them + */ +@@ -197,6 +211,85 @@ DSA_TAG_DRIVER(brcm_netdev_ops); + MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM); + #endif + ++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) ++static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct dsa_port *dp = dsa_slave_to_port(dev); ++ struct bcm_legacy_tag *brcm_tag; ++ ++ if (skb_cow_head(skb, BRCM_LEG_TAG_LEN) < 0) ++ return NULL; ++ ++ /* The Ethernet switch we are interfaced with needs packets to be at ++ * least 64 bytes (including FCS) otherwise they will be discarded when ++ * they enter the switch port logic. When Broadcom tags are enabled, we ++ * need to make sure that packets are at least 70 bytes ++ * (including FCS and tag) because the length verification is done after ++ * the Broadcom tag is stripped off the ingress packet. ++ * ++ * Let dsa_slave_xmit() free the SKB ++ */ ++ if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false)) ++ return NULL; ++ ++ skb_push(skb, BRCM_LEG_TAG_LEN); ++ ++ memmove(skb->data, skb->data + BRCM_LEG_TAG_LEN, 2 * ETH_ALEN); ++ ++ brcm_tag = (struct bcm_legacy_tag *) (skb->data + 2 * ETH_ALEN); ++ ++ brcm_tag->type = BRCM_LEG_TYPE; ++ brcm_tag->tag = BRCM_LEG_TAG_EGRESS; ++ brcm_tag->tag |= dp->index & BRCM_LEG_TAG_PORT_ID; ++ ++ return skb; ++} ++ ++ ++static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, ++ struct net_device *dev, ++ struct packet_type *pt) ++{ ++ int source_port; ++ struct bcm_legacy_tag *brcm_tag; ++ ++ if (unlikely(!pskb_may_pull(skb, BRCM_LEG_TAG_LEN))) ++ return NULL; ++ ++ brcm_tag = (struct bcm_legacy_tag *) (skb->data - 2); ++ ++ source_port = brcm_tag->tag & BRCM_LEG_TAG_PORT_ID; ++ ++ skb->dev = dsa_master_find_slave(dev, 0, source_port); ++ if (!skb->dev) ++ return NULL; ++ ++ /* Remove Broadcom tag and update checksum */ ++ skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN); ++ ++ skb->offload_fwd_mark = 1; ++ ++ /* Move the Ethernet DA and SA */ ++ memmove(skb->data - ETH_HLEN, ++ skb->data - ETH_HLEN - BRCM_LEG_TAG_LEN, ++ 2 * ETH_ALEN); ++ ++ return skb; ++} ++ ++static const struct dsa_device_ops brcm_legacy_netdev_ops = { ++ .name = "brcm-legacy", ++ .proto = DSA_TAG_PROTO_BRCM_LEGACY, ++ .xmit = brcm_leg_tag_xmit, ++ .rcv = brcm_leg_tag_rcv, ++ .overhead = BRCM_LEG_TAG_LEN, ++}; ++ ++DSA_TAG_DRIVER(brcm_legacy_netdev_ops); ++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY); ++#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY */ ++ + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) + static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb, + struct net_device *dev) +@@ -229,6 +322,9 @@ static struct dsa_tag_driver *dsa_tag_dr + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM) + &DSA_TAG_DRIVER_NAME(brcm_netdev_ops), + #endif ++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) ++ &DSA_TAG_DRIVER_NAME(brcm_legacy_netdev_ops), ++#endif + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) + &DSA_TAG_DRIVER_NAME(brcm_prepend_netdev_ops), + #endif diff --git a/target/linux/bmips/patches-5.10/503-net-broadcom-add-BCM6368-enetsw-controller-driver.patch b/target/linux/bmips/patches-5.10/503-net-broadcom-add-BCM6368-enetsw-controller-driver.patch new file mode 100644 index 0000000000..c2c2e968e9 --- /dev/null +++ b/target/linux/bmips/patches-5.10/503-net-broadcom-add-BCM6368-enetsw-controller-driver.patch @@ -0,0 +1,46 @@ +From 590b60fb08cb1e70fe02d3f407c6b3dbe9ad06ff Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Mon, 1 Mar 2021 07:34:39 +0100 +Subject: [PATCH 3/4] net: broadcom: add BCM6368 enetsw controller driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This controller is present on BCM6318, BCM6328, BCM6362, BCM6368 and BCM63268 +SoCs. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/net/ethernet/broadcom/Kconfig | 8 + + drivers/net/ethernet/broadcom/Makefile | 1 + + .../net/ethernet/broadcom/bcm6368-enetsw.c | 1111 +++++++++++++++++ + 3 files changed, 1120 insertions(+) + create mode 100644 drivers/net/ethernet/broadcom/bcm6368-enetsw.c + +--- a/drivers/net/ethernet/broadcom/Kconfig ++++ b/drivers/net/ethernet/broadcom/Kconfig +@@ -60,6 +60,14 @@ config BCM63XX_ENET + This driver supports the ethernet MACs in the Broadcom 63xx + MIPS chipset family (BCM63XX). + ++config BCM6368_ENETSW ++ tristate "Broadcom BCM6368 internal mac support" ++ depends on BMIPS_GENERIC || COMPILE_TEST ++ default y ++ help ++ This driver supports Ethernet controller integrated into Broadcom ++ BCM6368 family SoCs. ++ + config BCMGENET + tristate "Broadcom GENET internal MAC support" + depends on HAS_IOMEM +--- a/drivers/net/ethernet/broadcom/Makefile ++++ b/drivers/net/ethernet/broadcom/Makefile +@@ -5,6 +5,7 @@ + + obj-$(CONFIG_B44) += b44.o + obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o ++obj-$(CONFIG_BCM6368_ENETSW) += bcm6368-enetsw.o + obj-$(CONFIG_BCMGENET) += genet/ + obj-$(CONFIG_BNX2) += bnx2.o + obj-$(CONFIG_CNIC) += cnic.o diff --git a/target/linux/bmips/patches-5.10/504-net-mdio-Add-BCM6368-MDIO-mux-bus-controller.patch b/target/linux/bmips/patches-5.10/504-net-mdio-Add-BCM6368-MDIO-mux-bus-controller.patch new file mode 100644 index 0000000000..a869681312 --- /dev/null +++ b/target/linux/bmips/patches-5.10/504-net-mdio-Add-BCM6368-MDIO-mux-bus-controller.patch @@ -0,0 +1,231 @@ +From b6ecb2bca2b8ff80ae4b1b15f09dcf071f4ceaf4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Mon, 1 Mar 2021 16:42:12 +0100 +Subject: [PATCH] net: mdio: Add BCM6368 MDIO mux bus controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This controller is present on BCM6318, BCM6328, BCM6362, BCM6368 and BCM63268 +SoCs. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/net/mdio/Kconfig | 11 ++ + drivers/net/mdio/Makefile | 1 + + drivers/net/mdio/mdio-mux-bcm6368.c | 179 ++++++++++++++++++++++++++++ + 3 files changed, 191 insertions(+) + create mode 100644 drivers/net/mdio/mdio-mux-bcm6368.c + +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -200,6 +200,17 @@ config MDIO_BUS_MUX_MESON_G12A + the amlogic g12a SoC. The multiplexers connects either the external + or the internal MDIO bus to the parent bus. + ++config MDIO_BUS_MUX_BCM6368 ++ tristate "Broadcom BCM6368 MDIO bus multiplexers" ++ depends on OF && OF_MDIO && (BMIPS_GENERIC || COMPILE_TEST) ++ select MDIO_BUS_MUX ++ default BMIPS_GENERIC ++ help ++ This module provides a driver for MDIO bus multiplexers found in ++ BCM6368 based Broadcom SoCs. This multiplexer connects one of several ++ child MDIO bus to a parent bus. Buses could be internal as well as ++ external and selection logic lies inside the same multiplexer. ++ + config MDIO_BUS_MUX_BCM_IPROC + tristate "Broadcom iProc based MDIO bus multiplexers" + depends on OF && OF_MDIO && (ARCH_BCM_IPROC || COMPILE_TEST) +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -22,6 +22,7 @@ obj-$(CONFIG_MDIO_THUNDER) += mdio-thun + obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o + + obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o ++obj-$(CONFIG_MDIO_BUS_MUX_BCM6368) += mdio-mux-bcm6368.o + obj-$(CONFIG_MDIO_BUS_MUX_BCM_IPROC) += mdio-mux-bcm-iproc.o + obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o + obj-$(CONFIG_MDIO_BUS_MUX_MESON_G12A) += mdio-mux-meson-g12a.o +--- /dev/null ++++ b/drivers/net/mdio/mdio-mux-bcm6368.c +@@ -0,0 +1,179 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Broadcom BCM6368 mdiomux bus controller driver ++ * ++ * Copyright (C) 2021 Álvaro Fernández Rojas ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MDIOC_REG 0x0 ++#define MDIOC_EXT_MASK BIT(16) ++#define MDIOC_REG_SHIFT 20 ++#define MDIOC_PHYID_SHIFT 25 ++#define MDIOC_RD_MASK BIT(30) ++#define MDIOC_WR_MASK BIT(31) ++ ++#define MDIOD_REG 0x4 ++ ++struct bcm6368_mdiomux_desc { ++ void *mux_handle; ++ void __iomem *base; ++ struct device *dev; ++ struct mii_bus *mii_bus; ++ int ext_phy; ++}; ++ ++static int bcm6368_mdiomux_read(struct mii_bus *bus, int phy_id, int loc) ++{ ++ struct bcm6368_mdiomux_desc *md = bus->priv; ++ uint32_t reg; ++ int ret; ++ ++ __raw_writel(0, md->base + MDIOC_REG); ++ ++ reg = MDIOC_RD_MASK | ++ (phy_id << MDIOC_PHYID_SHIFT) | ++ (loc << MDIOC_REG_SHIFT); ++ if (md->ext_phy) ++ reg |= MDIOC_EXT_MASK; ++ ++ __raw_writel(reg, md->base + MDIOC_REG); ++ udelay(50); ++ ret = __raw_readw(md->base + MDIOD_REG); ++ ++ return ret; ++} ++ ++static int bcm6368_mdiomux_write(struct mii_bus *bus, int phy_id, int loc, ++ uint16_t val) ++{ ++ struct bcm6368_mdiomux_desc *md = bus->priv; ++ uint32_t reg; ++ ++ __raw_writel(0, md->base + MDIOC_REG); ++ ++ reg = MDIOC_WR_MASK | ++ (phy_id << MDIOC_PHYID_SHIFT) | ++ (loc << MDIOC_REG_SHIFT); ++ if (md->ext_phy) ++ reg |= MDIOC_EXT_MASK; ++ reg |= val; ++ ++ __raw_writel(reg, md->base + MDIOC_REG); ++ udelay(50); ++ ++ return 0; ++} ++ ++static int bcm6368_mdiomux_switch_fn(int current_child, int desired_child, ++ void *data) ++{ ++ struct bcm6368_mdiomux_desc *md = data; ++ ++ md->ext_phy = desired_child; ++ ++ return 0; ++} ++ ++static int bcm6368_mdiomux_probe(struct platform_device *pdev) ++{ ++ struct bcm6368_mdiomux_desc *md; ++ struct mii_bus *bus; ++ struct resource *res; ++ int rc; ++ ++ md = devm_kzalloc(&pdev->dev, sizeof(*md), GFP_KERNEL); ++ if (!md) ++ return -ENOMEM; ++ md->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ ++ /* Just ioremap, as this MDIO block is usually integrated into an ++ * Ethernet MAC controller register range ++ */ ++ md->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); ++ if (!md->base) { ++ dev_err(&pdev->dev, "failed to ioremap register\n"); ++ return -ENOMEM; ++ } ++ ++ md->mii_bus = devm_mdiobus_alloc(&pdev->dev); ++ if (!md->mii_bus) { ++ dev_err(&pdev->dev, "mdiomux bus alloc failed\n"); ++ return ENOMEM; ++ } ++ ++ bus = md->mii_bus; ++ bus->priv = md; ++ bus->name = "BCM6368 MDIO mux bus"; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); ++ bus->parent = &pdev->dev; ++ bus->read = bcm6368_mdiomux_read; ++ bus->write = bcm6368_mdiomux_write; ++ bus->phy_mask = 0x3f; ++ bus->dev.of_node = pdev->dev.of_node; ++ ++ rc = mdiobus_register(bus); ++ if (rc) { ++ dev_err(&pdev->dev, "mdiomux registration failed\n"); ++ return rc; ++ } ++ ++ platform_set_drvdata(pdev, md); ++ ++ rc = mdio_mux_init(md->dev, md->dev->of_node, ++ bcm6368_mdiomux_switch_fn, &md->mux_handle, md, ++ md->mii_bus); ++ if (rc) { ++ dev_info(md->dev, "mdiomux initialization failed\n"); ++ goto out_register; ++ } ++ ++ dev_info(&pdev->dev, "Broadcom BCM6368 MDIO mux bus\n"); ++ ++ return 0; ++ ++out_register: ++ mdiobus_unregister(bus); ++ return rc; ++} ++ ++static int bcm6368_mdiomux_remove(struct platform_device *pdev) ++{ ++ struct bcm6368_mdiomux_desc *md = platform_get_drvdata(pdev); ++ ++ mdio_mux_uninit(md->mux_handle); ++ mdiobus_unregister(md->mii_bus); ++ ++ return 0; ++} ++ ++static const struct of_device_id bcm6368_mdiomux_ids[] = { ++ { .compatible = "brcm,bcm6368-mdio-mux", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, bcm6368_mdiomux_ids); ++ ++static struct platform_driver bcm6368_mdiomux_driver = { ++ .driver = { ++ .name = "bcm6368-mdio-mux", ++ .of_match_table = bcm6368_mdiomux_ids, ++ }, ++ .probe = bcm6368_mdiomux_probe, ++ .remove = bcm6368_mdiomux_remove, ++}; ++module_platform_driver(bcm6368_mdiomux_driver); diff --git a/target/linux/generic/backport-5.10/402-v5.12-0001-dt-bindings-mtd-move-partition-binding-to-its-own-fi.patch b/target/linux/generic/backport-5.10/402-v5.12-0001-dt-bindings-mtd-move-partition-binding-to-its-own-fi.patch new file mode 100644 index 0000000000..f3b1179ecd --- /dev/null +++ b/target/linux/generic/backport-5.10/402-v5.12-0001-dt-bindings-mtd-move-partition-binding-to-its-own-fi.patch @@ -0,0 +1,115 @@ +From 6418522022c706fd867b00b2571edba48b8fa8c7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 11 Feb 2021 23:04:25 +0100 +Subject: [PATCH] dt-bindings: mtd: move partition binding to its own file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Single partition binding is quite common and may be: +1. Used by multiple parsers +2. Extended for more specific cases + +Move it to separated file to avoid code duplication. + +Signed-off-by: Rafał Miłecki +Reviewed-by: Rob Herring +Signed-off-by: Richard Weinberger +--- + .../mtd/partitions/fixed-partitions.yaml | 33 +------------ + .../bindings/mtd/partitions/partition.yaml | 47 +++++++++++++++++++ + 2 files changed, 48 insertions(+), 32 deletions(-) + create mode 100644 Documentation/devicetree/bindings/mtd/partitions/partition.yaml + +--- a/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml ++++ b/Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml +@@ -27,38 +27,7 @@ properties: + + patternProperties: + "@[0-9a-f]+$": +- description: node describing a single flash partition +- type: object +- +- properties: +- reg: +- description: partition's offset and size within the flash +- maxItems: 1 +- +- label: +- description: The label / name for this partition. If omitted, the label +- is taken from the node name (excluding the unit address). +- +- read-only: +- description: This parameter, if present, is a hint that this partition +- should only be mounted read-only. This is usually used for flash +- partitions containing early-boot firmware images or data which should +- not be clobbered. +- type: boolean +- +- lock: +- description: Do not unlock the partition at initialization time (not +- supported on all devices) +- type: boolean +- +- slc-mode: +- description: This parameter, if present, allows one to emulate SLC mode +- on a partition attached to an MLC NAND thus making this partition +- immune to paired-pages corruptions +- type: boolean +- +- required: +- - reg ++ $ref: "partition.yaml#" + + required: + - "#address-cells" +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/partitions/partition.yaml +@@ -0,0 +1,47 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/partitions/partition.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Partition ++ ++description: | ++ This binding describes a single flash partition. Each partition must have its ++ relative offset and size specified. Depending on partition function extra ++ properties can be used. ++ ++maintainers: ++ - Rafał Miłecki ++ ++properties: ++ reg: ++ description: partition's offset and size within the flash ++ maxItems: 1 ++ ++ label: ++ description: The label / name for this partition. If omitted, the label ++ is taken from the node name (excluding the unit address). ++ ++ read-only: ++ description: This parameter, if present, is a hint that this partition ++ should only be mounted read-only. This is usually used for flash ++ partitions containing early-boot firmware images or data which should ++ not be clobbered. ++ type: boolean ++ ++ lock: ++ description: Do not unlock the partition at initialization time (not ++ supported on all devices) ++ type: boolean ++ ++ slc-mode: ++ description: This parameter, if present, allows one to emulate SLC mode ++ on a partition attached to an MLC NAND thus making this partition ++ immune to paired-pages corruptions ++ type: boolean ++ ++required: ++ - reg ++ ++additionalProperties: true diff --git a/target/linux/generic/backport-5.10/402-v5.12-0002-dt-bindings-mtd-add-binding-for-BCM4908-partitions.patch b/target/linux/generic/backport-5.10/402-v5.12-0002-dt-bindings-mtd-add-binding-for-BCM4908-partitions.patch new file mode 100644 index 0000000000..8576c7d78d --- /dev/null +++ b/target/linux/generic/backport-5.10/402-v5.12-0002-dt-bindings-mtd-add-binding-for-BCM4908-partitions.patch @@ -0,0 +1,92 @@ +From 6e9dff6fe3fbc452f16566e4a7e293b0decefdba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 11 Feb 2021 23:04:26 +0100 +Subject: [PATCH] dt-bindings: mtd: add binding for BCM4908 partitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM4908 uses fixed partitions layout but function of some partitions may +vary. Some devices use multiple firmware partitions and those partitions +should be marked to let system discover their purpose. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Richard Weinberger +--- + .../partitions/brcm,bcm4908-partitions.yaml | 70 +++++++++++++++++++ + 1 file changed, 70 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/partitions/brcm,bcm4908-partitions.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/partitions/brcm,bcm4908-partitions.yaml +@@ -0,0 +1,70 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/partitions/brcm,bcm4908-partitions.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom BCM4908 partitioning ++ ++description: | ++ Broadcom BCM4908 CFE bootloader supports two firmware partitions. One is used ++ for regular booting, the other is treated as fallback. ++ ++ This binding allows defining all fixed partitions and marking those containing ++ firmware. System can use that information e.g. for booting or flashing ++ purposes. ++ ++maintainers: ++ - Rafał Miłecki ++ ++properties: ++ compatible: ++ const: brcm,bcm4908-partitions ++ ++ "#address-cells": ++ enum: [ 1, 2 ] ++ ++ "#size-cells": ++ enum: [ 1, 2 ] ++ ++patternProperties: ++ "^partition@[0-9a-f]+$": ++ $ref: "partition.yaml#" ++ properties: ++ compatible: ++ const: brcm,bcm4908-firmware ++ unevaluatedProperties: false ++ ++required: ++ - "#address-cells" ++ - "#size-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ partitions { ++ compatible = "brcm,bcm4908-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "cferom"; ++ reg = <0x0 0x100000>; ++ }; ++ ++ partition@100000 { ++ compatible = "brcm,bcm4908-firmware"; ++ reg = <0x100000 0xf00000>; ++ }; ++ ++ partition@1000000 { ++ compatible = "brcm,bcm4908-firmware"; ++ reg = <0x1000000 0xf00000>; ++ }; ++ ++ partition@1f00000 { ++ label = "calibration"; ++ reg = <0x1f00000 0x100000>; ++ }; ++ }; diff --git a/target/linux/generic/pending-5.10/401-v5.13-mtd-parsers-ofpart-support-BCM4908-fixed-partitions.patch b/target/linux/generic/backport-5.10/403-v5.13-mtd-parsers-ofpart-support-BCM4908-fixed-partitions.patch similarity index 100% rename from target/linux/generic/pending-5.10/401-v5.13-mtd-parsers-ofpart-support-BCM4908-fixed-partitions.patch rename to target/linux/generic/backport-5.10/403-v5.13-mtd-parsers-ofpart-support-BCM4908-fixed-partitions.patch diff --git a/target/linux/generic/backport-5.10/404-v5.13-mtd-parsers-ofpart-limit-parsing-of-deprecated-DT-sy.patch b/target/linux/generic/backport-5.10/404-v5.13-mtd-parsers-ofpart-limit-parsing-of-deprecated-DT-sy.patch new file mode 100644 index 0000000000..55a91d7680 --- /dev/null +++ b/target/linux/generic/backport-5.10/404-v5.13-mtd-parsers-ofpart-limit-parsing-of-deprecated-DT-sy.patch @@ -0,0 +1,69 @@ +From 2d751203aacf86a1b301a188d8551c7da91043ab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 2 Mar 2021 20:00:12 +0100 +Subject: [PATCH] mtd: parsers: ofpart: limit parsing of deprecated DT syntax +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For backward compatibility ofpart still supports the old syntax like: +spi-flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0>; + + partition@0 { + label = "bootloader"; + reg = <0x0 0x100000>; + }; +}; +(without "partitions" subnode). + +There is no reason however to support nested partitions without a clear +"compatible" string like: +partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bootloader"; + reg = <0x0 0x100000>; + + partition@0 { + label = "config"; + reg = <0x80000 0x80000>; + }; + }; +}; +(we never officially supported or documented that). + +Make sure ofpart doesn't attempt to parse above. + +Cc: Ansuel Smith +Signed-off-by: Rafał Miłecki +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210302190012.1255-1-zajec5@gmail.com +--- + drivers/mtd/parsers/ofpart_core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/parsers/ofpart_core.c ++++ b/drivers/mtd/parsers/ofpart_core.c +@@ -53,7 +53,7 @@ static int parse_fixed_partitions(struct + return 0; + + ofpart_node = of_get_child_by_name(mtd_node, "partitions"); +- if (!ofpart_node) { ++ if (!ofpart_node && !master->parent) { + /* + * We might get here even when ofpart isn't used at all (e.g., + * when using another parser), so don't be louder than +@@ -64,6 +64,8 @@ static int parse_fixed_partitions(struct + ofpart_node = mtd_node; + dedicated = false; + } ++ if (!ofpart_node) ++ return 0; + + of_id = of_match_node(parse_ofpart_match_table, ofpart_node); + if (dedicated && !of_id) { diff --git a/target/linux/generic/backport-5.10/800-v5.13-0001-firmware-bcm47xx_nvram-rename-finding-function-and-i.patch b/target/linux/generic/backport-5.10/800-v5.13-0001-firmware-bcm47xx_nvram-rename-finding-function-and-i.patch new file mode 100644 index 0000000000..19938704b7 --- /dev/null +++ b/target/linux/generic/backport-5.10/800-v5.13-0001-firmware-bcm47xx_nvram-rename-finding-function-and-i.patch @@ -0,0 +1,80 @@ +From fb009cbdd0693bd633f11e99526617b3d392cfad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:16 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: rename finding function and its + variables +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +1. Use "bcm47xx_" function name prefix for consistency +2. It takes flash start as argument so s/iobase/flash_start/ +3. "off" was used for finding flash end so just call it "flash_size" + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 24 ++++++++++++----------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -48,11 +48,13 @@ static u32 find_nvram_size(void __iomem + return 0; + } + +-/* Probe for NVRAM header */ +-static int nvram_find_and_copy(void __iomem *iobase, u32 lim) ++/** ++ * bcm47xx_nvram_find_and_copy - find NVRAM on flash mapping & copy it ++ */ ++static int bcm47xx_nvram_find_and_copy(void __iomem *flash_start, size_t res_size) + { + struct nvram_header __iomem *header; +- u32 off; ++ size_t flash_size; + u32 size; + + if (nvram_len) { +@@ -61,25 +63,25 @@ static int nvram_find_and_copy(void __io + } + + /* TODO: when nvram is on nand flash check for bad blocks first. */ +- off = FLASH_MIN; +- while (off <= lim) { ++ flash_size = FLASH_MIN; ++ while (flash_size <= res_size) { + /* Windowed flash access */ +- size = find_nvram_size(iobase + off); ++ size = find_nvram_size(flash_start + flash_size); + if (size) { +- header = (struct nvram_header *)(iobase + off - size); ++ header = (struct nvram_header *)(flash_start + flash_size - size); + goto found; + } +- off <<= 1; ++ flash_size <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ +- header = (struct nvram_header *)(iobase + 4096); ++ header = (struct nvram_header *)(flash_start + 4096); + if (header->magic == NVRAM_MAGIC) { + size = NVRAM_SPACE; + goto found; + } + +- header = (struct nvram_header *)(iobase + 1024); ++ header = (struct nvram_header *)(flash_start + 1024); + if (header->magic == NVRAM_MAGIC) { + size = NVRAM_SPACE; + goto found; +@@ -124,7 +126,7 @@ int bcm47xx_nvram_init_from_mem(u32 base + if (!iobase) + return -ENOMEM; + +- err = nvram_find_and_copy(iobase, lim); ++ err = bcm47xx_nvram_find_and_copy(iobase, lim); + + iounmap(iobase); + diff --git a/target/linux/generic/backport-5.10/800-v5.13-0002-firmware-bcm47xx_nvram-add-helper-checking-for-NVRAM.patch b/target/linux/generic/backport-5.10/800-v5.13-0002-firmware-bcm47xx_nvram-add-helper-checking-for-NVRAM.patch new file mode 100644 index 0000000000..6ab072883d --- /dev/null +++ b/target/linux/generic/backport-5.10/800-v5.13-0002-firmware-bcm47xx_nvram-add-helper-checking-for-NVRAM.patch @@ -0,0 +1,90 @@ +From 0a24b51a3264a3f942a75025ea5ff6133c8989b0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:17 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: add helper checking for NVRAM +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This avoids duplicating code doing casting and checking for NVRAM magic. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 30 ++++++++++++++--------- + 1 file changed, 18 insertions(+), 12 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -34,14 +34,20 @@ static char nvram_buf[NVRAM_SPACE]; + static size_t nvram_len; + static const u32 nvram_sizes[] = {0x6000, 0x8000, 0xF000, 0x10000}; + ++/** ++ * bcm47xx_nvram_is_valid - check for a valid NVRAM at specified memory ++ */ ++static bool bcm47xx_nvram_is_valid(void __iomem *nvram) ++{ ++ return ((struct nvram_header *)nvram)->magic == NVRAM_MAGIC; ++} ++ + static u32 find_nvram_size(void __iomem *end) + { +- struct nvram_header __iomem *header; + int i; + + for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { +- header = (struct nvram_header *)(end - nvram_sizes[i]); +- if (header->magic == NVRAM_MAGIC) ++ if (bcm47xx_nvram_is_valid(end - nvram_sizes[i])) + return nvram_sizes[i]; + } + +@@ -55,6 +61,7 @@ static int bcm47xx_nvram_find_and_copy(v + { + struct nvram_header __iomem *header; + size_t flash_size; ++ size_t offset; + u32 size; + + if (nvram_len) { +@@ -68,31 +75,30 @@ static int bcm47xx_nvram_find_and_copy(v + /* Windowed flash access */ + size = find_nvram_size(flash_start + flash_size); + if (size) { +- header = (struct nvram_header *)(flash_start + flash_size - size); ++ offset = flash_size - size; + goto found; + } + flash_size <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ +- header = (struct nvram_header *)(flash_start + 4096); +- if (header->magic == NVRAM_MAGIC) { +- size = NVRAM_SPACE; ++ ++ offset = 4096; ++ if (bcm47xx_nvram_is_valid(flash_start + offset)) + goto found; +- } + +- header = (struct nvram_header *)(flash_start + 1024); +- if (header->magic == NVRAM_MAGIC) { +- size = NVRAM_SPACE; ++ offset = 1024; ++ if (bcm47xx_nvram_is_valid(flash_start + offset)) + goto found; +- } + + pr_err("no nvram found\n"); + return -ENXIO; + + found: ++ header = (struct nvram_header *)(flash_start + offset); + __ioread32_copy(nvram_buf, header, sizeof(*header) / 4); + nvram_len = ((struct nvram_header *)(nvram_buf))->len; ++ size = res_size - offset; + if (nvram_len > size) { + pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n"); + nvram_len = size; diff --git a/target/linux/generic/backport-5.10/800-v5.13-0003-firmware-bcm47xx_nvram-extract-code-copying-NVRAM.patch b/target/linux/generic/backport-5.10/800-v5.13-0003-firmware-bcm47xx_nvram-extract-code-copying-NVRAM.patch new file mode 100644 index 0000000000..a1351f1197 --- /dev/null +++ b/target/linux/generic/backport-5.10/800-v5.13-0003-firmware-bcm47xx_nvram-extract-code-copying-NVRAM.patch @@ -0,0 +1,80 @@ +From 298923cf999cecd2ef06df126f85a3d68da8c4d8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:18 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: extract code copying NVRAM +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This simplifies function finding NVRAM. It doesn't directly deal with +NVRAM structure anymore and is a bit smaller. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 43 +++++++++++++---------- + 1 file changed, 25 insertions(+), 18 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -55,11 +55,34 @@ static u32 find_nvram_size(void __iomem + } + + /** ++ * bcm47xx_nvram_copy - copy NVRAM to internal buffer ++ */ ++static void bcm47xx_nvram_copy(void __iomem *nvram_start, size_t res_size) ++{ ++ struct nvram_header __iomem *header = nvram_start; ++ size_t copy_size; ++ ++ copy_size = header->len; ++ if (copy_size > res_size) { ++ pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n"); ++ copy_size = res_size; ++ } ++ if (copy_size >= NVRAM_SPACE) { ++ pr_err("nvram on flash (%zu bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", ++ copy_size, NVRAM_SPACE - 1); ++ copy_size = NVRAM_SPACE - 1; ++ } ++ ++ __ioread32_copy(nvram_buf, nvram_start, DIV_ROUND_UP(copy_size, 4)); ++ nvram_buf[NVRAM_SPACE - 1] = '\0'; ++ nvram_len = copy_size; ++} ++ ++/** + * bcm47xx_nvram_find_and_copy - find NVRAM on flash mapping & copy it + */ + static int bcm47xx_nvram_find_and_copy(void __iomem *flash_start, size_t res_size) + { +- struct nvram_header __iomem *header; + size_t flash_size; + size_t offset; + u32 size; +@@ -95,23 +118,7 @@ static int bcm47xx_nvram_find_and_copy(v + return -ENXIO; + + found: +- header = (struct nvram_header *)(flash_start + offset); +- __ioread32_copy(nvram_buf, header, sizeof(*header) / 4); +- nvram_len = ((struct nvram_header *)(nvram_buf))->len; +- size = res_size - offset; +- if (nvram_len > size) { +- pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n"); +- nvram_len = size; +- } +- if (nvram_len >= NVRAM_SPACE) { +- pr_err("nvram on flash (%zu bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", +- nvram_len, NVRAM_SPACE - 1); +- nvram_len = NVRAM_SPACE - 1; +- } +- /* proceed reading data after header */ +- __ioread32_copy(nvram_buf + sizeof(*header), header + 1, +- DIV_ROUND_UP(nvram_len, 4)); +- nvram_buf[NVRAM_SPACE - 1] = '\0'; ++ bcm47xx_nvram_copy(flash_start + offset, res_size - offset); + + return 0; + } diff --git a/target/linux/generic/backport-5.10/800-v5.13-0004-firmware-bcm47xx_nvram-look-for-NVRAM-with-for-inste.patch b/target/linux/generic/backport-5.10/800-v5.13-0004-firmware-bcm47xx_nvram-look-for-NVRAM-with-for-inste.patch new file mode 100644 index 0000000000..059a13220b --- /dev/null +++ b/target/linux/generic/backport-5.10/800-v5.13-0004-firmware-bcm47xx_nvram-look-for-NVRAM-with-for-inste.patch @@ -0,0 +1,37 @@ +From 98b68324f67236e8c9152976535dc1f27fb67ba8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:19 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: look for NVRAM with for instead of + while +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This loop requires variable initialization, stop condition and post +iteration increment. It's pretty much a for loop definition. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -93,15 +93,13 @@ static int bcm47xx_nvram_find_and_copy(v + } + + /* TODO: when nvram is on nand flash check for bad blocks first. */ +- flash_size = FLASH_MIN; +- while (flash_size <= res_size) { ++ for (flash_size = FLASH_MIN; flash_size <= res_size; flash_size <<= 1) { + /* Windowed flash access */ + size = find_nvram_size(flash_start + flash_size); + if (size) { + offset = flash_size - size; + goto found; + } +- flash_size <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ diff --git a/target/linux/generic/backport-5.10/800-v5.13-0005-firmware-bcm47xx_nvram-inline-code-checking-NVRAM-si.patch b/target/linux/generic/backport-5.10/800-v5.13-0005-firmware-bcm47xx_nvram-inline-code-checking-NVRAM-si.patch new file mode 100644 index 0000000000..21d250049e --- /dev/null +++ b/target/linux/generic/backport-5.10/800-v5.13-0005-firmware-bcm47xx_nvram-inline-code-checking-NVRAM-si.patch @@ -0,0 +1,70 @@ +From f52da4ccfec9192e17f5c16260dfdd6d3ea76f65 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:20 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: inline code checking NVRAM size +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Separated function was not improving code quality much (or at all). +Moreover it expected possible flash end address as argument and it was +returning NVRAM size. + +The new code always operates on offsets which means less logic and less +calculations. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 25 +++++++---------------- + 1 file changed, 7 insertions(+), 18 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -42,18 +42,6 @@ static bool bcm47xx_nvram_is_valid(void + return ((struct nvram_header *)nvram)->magic == NVRAM_MAGIC; + } + +-static u32 find_nvram_size(void __iomem *end) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { +- if (bcm47xx_nvram_is_valid(end - nvram_sizes[i])) +- return nvram_sizes[i]; +- } +- +- return 0; +-} +- + /** + * bcm47xx_nvram_copy - copy NVRAM to internal buffer + */ +@@ -85,7 +73,7 @@ static int bcm47xx_nvram_find_and_copy(v + { + size_t flash_size; + size_t offset; +- u32 size; ++ int i; + + if (nvram_len) { + pr_warn("nvram already initialized\n"); +@@ -93,12 +81,13 @@ static int bcm47xx_nvram_find_and_copy(v + } + + /* TODO: when nvram is on nand flash check for bad blocks first. */ ++ ++ /* Try every possible flash size and check for NVRAM at its end */ + for (flash_size = FLASH_MIN; flash_size <= res_size; flash_size <<= 1) { +- /* Windowed flash access */ +- size = find_nvram_size(flash_start + flash_size); +- if (size) { +- offset = flash_size - size; +- goto found; ++ for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { ++ offset = flash_size - nvram_sizes[i]; ++ if (bcm47xx_nvram_is_valid(flash_start + offset)) ++ goto found; + } + } + diff --git a/target/linux/generic/backport-5.10/830-v5.12-0001-net-usb-qmi_wwan-support-ZTE-P685M-modem.patch b/target/linux/generic/backport-5.10/830-v5.12-0001-net-usb-qmi_wwan-support-ZTE-P685M-modem.patch deleted file mode 100644 index af3b17cc91..0000000000 --- a/target/linux/generic/backport-5.10/830-v5.12-0001-net-usb-qmi_wwan-support-ZTE-P685M-modem.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 88eee9b7b42e69fb622ddb3ff6f37e8e4347f5b2 Mon Sep 17 00:00:00 2001 -From: Lech Perczak -Date: Tue, 23 Feb 2021 19:34:56 +0100 -Subject: net: usb: qmi_wwan: support ZTE P685M modem -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Now that interface 3 in "option" driver is no longer mapped, add device -ID matching it to qmi_wwan. - -The modem is used inside ZTE MF283+ router and carriers identify it as -such. -Interface mapping is: -0: QCDM, 1: AT (PCUI), 2: AT (Modem), 3: QMI, 4: ADB - -T: Bus=02 Lev=02 Prnt=02 Port=05 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 -D: Ver= 2.01 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 -P: Vendor=19d2 ProdID=1275 Rev=f0.00 -S: Manufacturer=ZTE,Incorporated -S: Product=ZTE Technologies MSM -S: SerialNumber=P685M510ZTED0000CP&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&0 -C:* #Ifs= 5 Cfg#= 1 Atr=a0 MxPwr=500mA -I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option -E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -I:* If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option -E: Ad=83(I) Atr=03(Int.) MxPS= 10 Ivl=32ms -E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option -E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms -E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan -E: Ad=87(I) Atr=03(Int.) MxPS= 8 Ivl=32ms -E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) -E: Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms - -Acked-by: Bjørn Mork -Signed-off-by: Lech Perczak -Link: https://lore.kernel.org/r/20210223183456.6377-1-lech.perczak@gmail.com -Signed-off-by: Jakub Kicinski ---- - drivers/net/usb/qmi_wwan.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/net/usb/qmi_wwan.c -+++ b/drivers/net/usb/qmi_wwan.c -@@ -1258,6 +1258,7 @@ static const struct usb_device_id produc - {QMI_FIXED_INTF(0x19d2, 0x1255, 4)}, - {QMI_FIXED_INTF(0x19d2, 0x1256, 4)}, - {QMI_FIXED_INTF(0x19d2, 0x1270, 5)}, /* ZTE MF667 */ -+ {QMI_FIXED_INTF(0x19d2, 0x1275, 3)}, /* ZTE P685M */ - {QMI_FIXED_INTF(0x19d2, 0x1401, 2)}, - {QMI_FIXED_INTF(0x19d2, 0x1402, 2)}, /* ZTE MF60 */ - {QMI_FIXED_INTF(0x19d2, 0x1424, 2)}, diff --git a/target/linux/generic/backport-5.4/310-v5.6-mips-vdso-fix-jalr-t9-crash-in-vdso-code.patch b/target/linux/generic/backport-5.4/310-v5.6-mips-vdso-fix-jalr-t9-crash-in-vdso-code.patch index 25c87a3263..51eef4b26b 100644 --- a/target/linux/generic/backport-5.4/310-v5.6-mips-vdso-fix-jalr-t9-crash-in-vdso-code.patch +++ b/target/linux/generic/backport-5.4/310-v5.6-mips-vdso-fix-jalr-t9-crash-in-vdso-code.patch @@ -44,7 +44,7 @@ Cc: richard.purdie@linuxfoundation.org --- a/arch/mips/vdso/Makefile +++ b/arch/mips/vdso/Makefile -@@ -29,6 +29,7 @@ endif +@@ -26,6 +26,7 @@ ccflags-vdso := \ cflags-vdso := $(ccflags-vdso) \ $(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \ -O3 -g -fPIC -fno-strict-aliasing -fno-common -fno-builtin -G 0 \ diff --git a/target/linux/generic/pending-5.4/404-v5.13-mtd-parsers-ofpart-support-BCM4908-fixed-partitions.patch b/target/linux/generic/backport-5.4/403-v5.13-mtd-parsers-ofpart-support-BCM4908-fixed-partitions.patch similarity index 100% rename from target/linux/generic/pending-5.4/404-v5.13-mtd-parsers-ofpart-support-BCM4908-fixed-partitions.patch rename to target/linux/generic/backport-5.4/403-v5.13-mtd-parsers-ofpart-support-BCM4908-fixed-partitions.patch diff --git a/target/linux/generic/backport-5.4/404-v5.13-mtd-parsers-ofpart-limit-parsing-of-deprecated-DT-sy.patch b/target/linux/generic/backport-5.4/404-v5.13-mtd-parsers-ofpart-limit-parsing-of-deprecated-DT-sy.patch new file mode 100644 index 0000000000..35058adba7 --- /dev/null +++ b/target/linux/generic/backport-5.4/404-v5.13-mtd-parsers-ofpart-limit-parsing-of-deprecated-DT-sy.patch @@ -0,0 +1,69 @@ +From 2d751203aacf86a1b301a188d8551c7da91043ab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 2 Mar 2021 20:00:12 +0100 +Subject: [PATCH] mtd: parsers: ofpart: limit parsing of deprecated DT syntax +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For backward compatibility ofpart still supports the old syntax like: +spi-flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0>; + + partition@0 { + label = "bootloader"; + reg = <0x0 0x100000>; + }; +}; +(without "partitions" subnode). + +There is no reason however to support nested partitions without a clear +"compatible" string like: +partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bootloader"; + reg = <0x0 0x100000>; + + partition@0 { + label = "config"; + reg = <0x80000 0x80000>; + }; + }; +}; +(we never officially supported or documented that). + +Make sure ofpart doesn't attempt to parse above. + +Cc: Ansuel Smith +Signed-off-by: Rafał Miłecki +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210302190012.1255-1-zajec5@gmail.com +--- + drivers/mtd/parsers/ofpart_core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/parsers/ofpart_core.c ++++ b/drivers/mtd/parsers/ofpart_core.c +@@ -53,7 +53,7 @@ static int parse_fixed_partitions(struct + return 0; + + ofpart_node = of_get_child_by_name(mtd_node, "partitions"); +- if (!ofpart_node) { ++ if (!ofpart_node && !mtd_is_partition(master)) { + /* + * We might get here even when ofpart isn't used at all (e.g., + * when using another parser), so don't be louder than +@@ -64,6 +64,8 @@ static int parse_fixed_partitions(struct + ofpart_node = mtd_node; + dedicated = false; + } ++ if (!ofpart_node) ++ return 0; + + of_id = of_match_node(parse_ofpart_match_table, ofpart_node); + if (dedicated && !of_id) { diff --git a/target/linux/generic/backport-5.4/752-v5.8-net-dsa-provide-an-option-for-drivers-to-always-rece.patch b/target/linux/generic/backport-5.4/752-v5.8-net-dsa-provide-an-option-for-drivers-to-always-rece.patch index 97ac6e2acd..921821a899 100644 --- a/target/linux/generic/backport-5.4/752-v5.8-net-dsa-provide-an-option-for-drivers-to-always-rece.patch +++ b/target/linux/generic/backport-5.4/752-v5.8-net-dsa-provide-an-option-for-drivers-to-always-rece.patch @@ -50,7 +50,7 @@ Signed-off-by: David S. Miller */ --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h -@@ -136,6 +136,7 @@ int dsa_port_bridge_join(struct dsa_port +@@ -139,6 +139,7 @@ int dsa_port_bridge_join(struct dsa_port void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br); int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, struct switchdev_trans *trans); diff --git a/target/linux/generic/backport-5.4/755-v5.8-net-dsa-add-GRO-support-via-gro_cells.patch b/target/linux/generic/backport-5.4/755-v5.8-net-dsa-add-GRO-support-via-gro_cells.patch deleted file mode 100644 index 6d50a40d61..0000000000 --- a/target/linux/generic/backport-5.4/755-v5.8-net-dsa-add-GRO-support-via-gro_cells.patch +++ /dev/null @@ -1,143 +0,0 @@ -From: Alexander Lobakin -Date: Tue, 21 Apr 2020 16:41:08 +0300 -Subject: [PATCH] net: dsa: add GRO support via gro_cells - -gro_cells lib is used by different encapsulating netdevices, such as -geneve, macsec, vxlan etc. to speed up decapsulated traffic processing. -CPU tag is a sort of "encapsulation", and we can use the same mechs to -greatly improve overall DSA performance. -skbs are passed to the GRO layer after removing CPU tags, so we don't -need any new packet offload types as it was firstly proposed by me in -the first GRO-over-DSA variant [1]. - -The size of struct gro_cells is sizeof(void *), so hot struct -dsa_slave_priv becomes only 4/8 bytes bigger, and all critical fields -remain in one 32-byte cacheline. -The other positive side effect is that drivers for network devices -that can be shipped as CPU ports of DSA-driven switches can now use -napi_gro_frags() to pass skbs to kernel. Packets built that way are -completely non-linear and are likely being dropped without GRO. - -This was tested on to-be-mainlined-soon Ethernet driver that uses -napi_gro_frags(), and the overall performance was on par with the -variant from [1], sometimes even better due to minimal overhead. -net.core.gro_normal_batch tuning may help to push it to the limit -on particular setups and platforms. - -iperf3 IPoE VLAN NAT TCP forwarding (port1.218 -> port0) setup -on 1.2 GHz MIPS board: - -5.7-rc2 baseline: - -[ID] Interval Transfer Bitrate Retr -[ 5] 0.00-120.01 sec 9.00 GBytes 644 Mbits/sec 413 sender -[ 5] 0.00-120.00 sec 8.99 GBytes 644 Mbits/sec receiver - -Iface RX packets TX packets -eth0 7097731 7097702 -port0 426050 6671829 -port1 6671681 425862 -port1.218 6671677 425851 - -With this patch: - -[ID] Interval Transfer Bitrate Retr -[ 5] 0.00-120.01 sec 12.2 GBytes 870 Mbits/sec 122 sender -[ 5] 0.00-120.00 sec 12.2 GBytes 870 Mbits/sec receiver - -Iface RX packets TX packets -eth0 9474792 9474777 -port0 455200 353288 -port1 9019592 455035 -port1.218 353144 455024 - -v2: - - Add some performance examples in the commit message; - - No functional changes. - -[1] https://lore.kernel.org/netdev/20191230143028.27313-1-alobakin@dlink.ru/ - -Signed-off-by: Alexander Lobakin -Signed-off-by: David S. Miller ---- - ---- a/net/dsa/Kconfig -+++ b/net/dsa/Kconfig -@@ -9,6 +9,7 @@ menuconfig NET_DSA - tristate "Distributed Switch Architecture" - depends on HAVE_NET_DSA - depends on BRIDGE || BRIDGE=n -+ select GRO_CELLS - select NET_SWITCHDEV - select PHYLINK - select NET_DEVLINK ---- a/net/dsa/dsa.c -+++ b/net/dsa/dsa.c -@@ -238,7 +238,7 @@ static int dsa_switch_rcv(struct sk_buff - if (dsa_skb_defer_rx_timestamp(p, skb)) - return 0; - -- netif_receive_skb(skb); -+ gro_cells_receive(&p->gcells, skb); - - return 0; - } ---- a/net/dsa/dsa_priv.h -+++ b/net/dsa/dsa_priv.h -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - - enum { - DSA_NOTIFIER_AGEING_TIME, -@@ -68,6 +69,8 @@ struct dsa_slave_priv { - - struct pcpu_sw_netstats *stats64; - -+ struct gro_cells gcells; -+ - /* DSA port data, such as switch, port index, etc. */ - struct dsa_port *dp; - ---- a/net/dsa/slave.c -+++ b/net/dsa/slave.c -@@ -1431,6 +1431,11 @@ int dsa_slave_create(struct dsa_port *po - free_netdev(slave_dev); - return -ENOMEM; - } -+ -+ ret = gro_cells_init(&p->gcells, slave_dev); -+ if (ret) -+ goto out_free; -+ - p->dp = port; - INIT_LIST_HEAD(&p->mall_tc_list); - INIT_WORK(&port->xmit_work, dsa_port_xmit_work); -@@ -1443,7 +1448,7 @@ int dsa_slave_create(struct dsa_port *po - ret = dsa_slave_phy_setup(slave_dev); - if (ret) { - netdev_err(master, "error %d setting up slave phy\n", ret); -- goto out_free; -+ goto out_gcells; - } - - dsa_slave_notify(slave_dev, DSA_PORT_REGISTER); -@@ -1462,6 +1467,8 @@ out_phy: - phylink_disconnect_phy(p->dp->pl); - rtnl_unlock(); - phylink_destroy(p->dp->pl); -+out_gcells: -+ gro_cells_destroy(&p->gcells); - out_free: - free_percpu(p->stats64); - free_netdev(slave_dev); -@@ -1482,6 +1489,7 @@ void dsa_slave_destroy(struct net_device - dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER); - unregister_netdev(slave_dev); - phylink_destroy(dp->pl); -+ gro_cells_destroy(&p->gcells); - free_percpu(p->stats64); - free_netdev(slave_dev); - } diff --git a/target/linux/generic/backport-5.4/830-v5.12-0001-net-usb-qmi_wwan-support-ZTE-P685M-modem.patch b/target/linux/generic/backport-5.4/830-v5.12-0001-net-usb-qmi_wwan-support-ZTE-P685M-modem.patch deleted file mode 100644 index 35da12af92..0000000000 --- a/target/linux/generic/backport-5.4/830-v5.12-0001-net-usb-qmi_wwan-support-ZTE-P685M-modem.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 88eee9b7b42e69fb622ddb3ff6f37e8e4347f5b2 Mon Sep 17 00:00:00 2001 -From: Lech Perczak -Date: Tue, 23 Feb 2021 19:34:56 +0100 -Subject: net: usb: qmi_wwan: support ZTE P685M modem -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Now that interface 3 in "option" driver is no longer mapped, add device -ID matching it to qmi_wwan. - -The modem is used inside ZTE MF283+ router and carriers identify it as -such. -Interface mapping is: -0: QCDM, 1: AT (PCUI), 2: AT (Modem), 3: QMI, 4: ADB - -T: Bus=02 Lev=02 Prnt=02 Port=05 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 -D: Ver= 2.01 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 -P: Vendor=19d2 ProdID=1275 Rev=f0.00 -S: Manufacturer=ZTE,Incorporated -S: Product=ZTE Technologies MSM -S: SerialNumber=P685M510ZTED0000CP&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&0 -C:* #Ifs= 5 Cfg#= 1 Atr=a0 MxPwr=500mA -I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option -E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -I:* If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option -E: Ad=83(I) Atr=03(Int.) MxPS= 10 Ivl=32ms -E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option -E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms -E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan -E: Ad=87(I) Atr=03(Int.) MxPS= 8 Ivl=32ms -E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) -E: Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms -E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms - -Acked-by: Bjørn Mork -Signed-off-by: Lech Perczak -Link: https://lore.kernel.org/r/20210223183456.6377-1-lech.perczak@gmail.com -Signed-off-by: Jakub Kicinski ---- - drivers/net/usb/qmi_wwan.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/net/usb/qmi_wwan.c -+++ b/drivers/net/usb/qmi_wwan.c -@@ -1280,6 +1280,7 @@ static const struct usb_device_id produc - {QMI_FIXED_INTF(0x19d2, 0x1255, 4)}, - {QMI_FIXED_INTF(0x19d2, 0x1256, 4)}, - {QMI_FIXED_INTF(0x19d2, 0x1270, 5)}, /* ZTE MF667 */ -+ {QMI_FIXED_INTF(0x19d2, 0x1275, 3)}, /* ZTE P685M */ - {QMI_FIXED_INTF(0x19d2, 0x1401, 2)}, - {QMI_FIXED_INTF(0x19d2, 0x1402, 2)}, /* ZTE MF60 */ - {QMI_FIXED_INTF(0x19d2, 0x1424, 2)}, diff --git a/target/linux/generic/backport-5.4/831-v5.13-0001-firmware-bcm47xx_nvram-rename-finding-function-and-i.patch b/target/linux/generic/backport-5.4/831-v5.13-0001-firmware-bcm47xx_nvram-rename-finding-function-and-i.patch new file mode 100644 index 0000000000..19938704b7 --- /dev/null +++ b/target/linux/generic/backport-5.4/831-v5.13-0001-firmware-bcm47xx_nvram-rename-finding-function-and-i.patch @@ -0,0 +1,80 @@ +From fb009cbdd0693bd633f11e99526617b3d392cfad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:16 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: rename finding function and its + variables +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +1. Use "bcm47xx_" function name prefix for consistency +2. It takes flash start as argument so s/iobase/flash_start/ +3. "off" was used for finding flash end so just call it "flash_size" + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 24 ++++++++++++----------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -48,11 +48,13 @@ static u32 find_nvram_size(void __iomem + return 0; + } + +-/* Probe for NVRAM header */ +-static int nvram_find_and_copy(void __iomem *iobase, u32 lim) ++/** ++ * bcm47xx_nvram_find_and_copy - find NVRAM on flash mapping & copy it ++ */ ++static int bcm47xx_nvram_find_and_copy(void __iomem *flash_start, size_t res_size) + { + struct nvram_header __iomem *header; +- u32 off; ++ size_t flash_size; + u32 size; + + if (nvram_len) { +@@ -61,25 +63,25 @@ static int nvram_find_and_copy(void __io + } + + /* TODO: when nvram is on nand flash check for bad blocks first. */ +- off = FLASH_MIN; +- while (off <= lim) { ++ flash_size = FLASH_MIN; ++ while (flash_size <= res_size) { + /* Windowed flash access */ +- size = find_nvram_size(iobase + off); ++ size = find_nvram_size(flash_start + flash_size); + if (size) { +- header = (struct nvram_header *)(iobase + off - size); ++ header = (struct nvram_header *)(flash_start + flash_size - size); + goto found; + } +- off <<= 1; ++ flash_size <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ +- header = (struct nvram_header *)(iobase + 4096); ++ header = (struct nvram_header *)(flash_start + 4096); + if (header->magic == NVRAM_MAGIC) { + size = NVRAM_SPACE; + goto found; + } + +- header = (struct nvram_header *)(iobase + 1024); ++ header = (struct nvram_header *)(flash_start + 1024); + if (header->magic == NVRAM_MAGIC) { + size = NVRAM_SPACE; + goto found; +@@ -124,7 +126,7 @@ int bcm47xx_nvram_init_from_mem(u32 base + if (!iobase) + return -ENOMEM; + +- err = nvram_find_and_copy(iobase, lim); ++ err = bcm47xx_nvram_find_and_copy(iobase, lim); + + iounmap(iobase); + diff --git a/target/linux/generic/backport-5.4/831-v5.13-0002-firmware-bcm47xx_nvram-add-helper-checking-for-NVRAM.patch b/target/linux/generic/backport-5.4/831-v5.13-0002-firmware-bcm47xx_nvram-add-helper-checking-for-NVRAM.patch new file mode 100644 index 0000000000..6ab072883d --- /dev/null +++ b/target/linux/generic/backport-5.4/831-v5.13-0002-firmware-bcm47xx_nvram-add-helper-checking-for-NVRAM.patch @@ -0,0 +1,90 @@ +From 0a24b51a3264a3f942a75025ea5ff6133c8989b0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:17 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: add helper checking for NVRAM +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This avoids duplicating code doing casting and checking for NVRAM magic. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 30 ++++++++++++++--------- + 1 file changed, 18 insertions(+), 12 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -34,14 +34,20 @@ static char nvram_buf[NVRAM_SPACE]; + static size_t nvram_len; + static const u32 nvram_sizes[] = {0x6000, 0x8000, 0xF000, 0x10000}; + ++/** ++ * bcm47xx_nvram_is_valid - check for a valid NVRAM at specified memory ++ */ ++static bool bcm47xx_nvram_is_valid(void __iomem *nvram) ++{ ++ return ((struct nvram_header *)nvram)->magic == NVRAM_MAGIC; ++} ++ + static u32 find_nvram_size(void __iomem *end) + { +- struct nvram_header __iomem *header; + int i; + + for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { +- header = (struct nvram_header *)(end - nvram_sizes[i]); +- if (header->magic == NVRAM_MAGIC) ++ if (bcm47xx_nvram_is_valid(end - nvram_sizes[i])) + return nvram_sizes[i]; + } + +@@ -55,6 +61,7 @@ static int bcm47xx_nvram_find_and_copy(v + { + struct nvram_header __iomem *header; + size_t flash_size; ++ size_t offset; + u32 size; + + if (nvram_len) { +@@ -68,31 +75,30 @@ static int bcm47xx_nvram_find_and_copy(v + /* Windowed flash access */ + size = find_nvram_size(flash_start + flash_size); + if (size) { +- header = (struct nvram_header *)(flash_start + flash_size - size); ++ offset = flash_size - size; + goto found; + } + flash_size <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ +- header = (struct nvram_header *)(flash_start + 4096); +- if (header->magic == NVRAM_MAGIC) { +- size = NVRAM_SPACE; ++ ++ offset = 4096; ++ if (bcm47xx_nvram_is_valid(flash_start + offset)) + goto found; +- } + +- header = (struct nvram_header *)(flash_start + 1024); +- if (header->magic == NVRAM_MAGIC) { +- size = NVRAM_SPACE; ++ offset = 1024; ++ if (bcm47xx_nvram_is_valid(flash_start + offset)) + goto found; +- } + + pr_err("no nvram found\n"); + return -ENXIO; + + found: ++ header = (struct nvram_header *)(flash_start + offset); + __ioread32_copy(nvram_buf, header, sizeof(*header) / 4); + nvram_len = ((struct nvram_header *)(nvram_buf))->len; ++ size = res_size - offset; + if (nvram_len > size) { + pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n"); + nvram_len = size; diff --git a/target/linux/generic/backport-5.4/831-v5.13-0003-firmware-bcm47xx_nvram-extract-code-copying-NVRAM.patch b/target/linux/generic/backport-5.4/831-v5.13-0003-firmware-bcm47xx_nvram-extract-code-copying-NVRAM.patch new file mode 100644 index 0000000000..a1351f1197 --- /dev/null +++ b/target/linux/generic/backport-5.4/831-v5.13-0003-firmware-bcm47xx_nvram-extract-code-copying-NVRAM.patch @@ -0,0 +1,80 @@ +From 298923cf999cecd2ef06df126f85a3d68da8c4d8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:18 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: extract code copying NVRAM +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This simplifies function finding NVRAM. It doesn't directly deal with +NVRAM structure anymore and is a bit smaller. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 43 +++++++++++++---------- + 1 file changed, 25 insertions(+), 18 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -55,11 +55,34 @@ static u32 find_nvram_size(void __iomem + } + + /** ++ * bcm47xx_nvram_copy - copy NVRAM to internal buffer ++ */ ++static void bcm47xx_nvram_copy(void __iomem *nvram_start, size_t res_size) ++{ ++ struct nvram_header __iomem *header = nvram_start; ++ size_t copy_size; ++ ++ copy_size = header->len; ++ if (copy_size > res_size) { ++ pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n"); ++ copy_size = res_size; ++ } ++ if (copy_size >= NVRAM_SPACE) { ++ pr_err("nvram on flash (%zu bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", ++ copy_size, NVRAM_SPACE - 1); ++ copy_size = NVRAM_SPACE - 1; ++ } ++ ++ __ioread32_copy(nvram_buf, nvram_start, DIV_ROUND_UP(copy_size, 4)); ++ nvram_buf[NVRAM_SPACE - 1] = '\0'; ++ nvram_len = copy_size; ++} ++ ++/** + * bcm47xx_nvram_find_and_copy - find NVRAM on flash mapping & copy it + */ + static int bcm47xx_nvram_find_and_copy(void __iomem *flash_start, size_t res_size) + { +- struct nvram_header __iomem *header; + size_t flash_size; + size_t offset; + u32 size; +@@ -95,23 +118,7 @@ static int bcm47xx_nvram_find_and_copy(v + return -ENXIO; + + found: +- header = (struct nvram_header *)(flash_start + offset); +- __ioread32_copy(nvram_buf, header, sizeof(*header) / 4); +- nvram_len = ((struct nvram_header *)(nvram_buf))->len; +- size = res_size - offset; +- if (nvram_len > size) { +- pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n"); +- nvram_len = size; +- } +- if (nvram_len >= NVRAM_SPACE) { +- pr_err("nvram on flash (%zu bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", +- nvram_len, NVRAM_SPACE - 1); +- nvram_len = NVRAM_SPACE - 1; +- } +- /* proceed reading data after header */ +- __ioread32_copy(nvram_buf + sizeof(*header), header + 1, +- DIV_ROUND_UP(nvram_len, 4)); +- nvram_buf[NVRAM_SPACE - 1] = '\0'; ++ bcm47xx_nvram_copy(flash_start + offset, res_size - offset); + + return 0; + } diff --git a/target/linux/generic/backport-5.4/831-v5.13-0004-firmware-bcm47xx_nvram-look-for-NVRAM-with-for-inste.patch b/target/linux/generic/backport-5.4/831-v5.13-0004-firmware-bcm47xx_nvram-look-for-NVRAM-with-for-inste.patch new file mode 100644 index 0000000000..059a13220b --- /dev/null +++ b/target/linux/generic/backport-5.4/831-v5.13-0004-firmware-bcm47xx_nvram-look-for-NVRAM-with-for-inste.patch @@ -0,0 +1,37 @@ +From 98b68324f67236e8c9152976535dc1f27fb67ba8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:19 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: look for NVRAM with for instead of + while +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This loop requires variable initialization, stop condition and post +iteration increment. It's pretty much a for loop definition. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -93,15 +93,13 @@ static int bcm47xx_nvram_find_and_copy(v + } + + /* TODO: when nvram is on nand flash check for bad blocks first. */ +- flash_size = FLASH_MIN; +- while (flash_size <= res_size) { ++ for (flash_size = FLASH_MIN; flash_size <= res_size; flash_size <<= 1) { + /* Windowed flash access */ + size = find_nvram_size(flash_start + flash_size); + if (size) { + offset = flash_size - size; + goto found; + } +- flash_size <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ diff --git a/target/linux/generic/backport-5.4/831-v5.13-0005-firmware-bcm47xx_nvram-inline-code-checking-NVRAM-si.patch b/target/linux/generic/backport-5.4/831-v5.13-0005-firmware-bcm47xx_nvram-inline-code-checking-NVRAM-si.patch new file mode 100644 index 0000000000..21d250049e --- /dev/null +++ b/target/linux/generic/backport-5.4/831-v5.13-0005-firmware-bcm47xx_nvram-inline-code-checking-NVRAM-si.patch @@ -0,0 +1,70 @@ +From f52da4ccfec9192e17f5c16260dfdd6d3ea76f65 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 8 Mar 2021 10:03:20 +0100 +Subject: [PATCH] firmware: bcm47xx_nvram: inline code checking NVRAM size +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Separated function was not improving code quality much (or at all). +Moreover it expected possible flash end address as argument and it was +returning NVRAM size. + +The new code always operates on offsets which means less logic and less +calculations. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Thomas Bogendoerfer +--- + drivers/firmware/broadcom/bcm47xx_nvram.c | 25 +++++++---------------- + 1 file changed, 7 insertions(+), 18 deletions(-) + +--- a/drivers/firmware/broadcom/bcm47xx_nvram.c ++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c +@@ -42,18 +42,6 @@ static bool bcm47xx_nvram_is_valid(void + return ((struct nvram_header *)nvram)->magic == NVRAM_MAGIC; + } + +-static u32 find_nvram_size(void __iomem *end) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { +- if (bcm47xx_nvram_is_valid(end - nvram_sizes[i])) +- return nvram_sizes[i]; +- } +- +- return 0; +-} +- + /** + * bcm47xx_nvram_copy - copy NVRAM to internal buffer + */ +@@ -85,7 +73,7 @@ static int bcm47xx_nvram_find_and_copy(v + { + size_t flash_size; + size_t offset; +- u32 size; ++ int i; + + if (nvram_len) { + pr_warn("nvram already initialized\n"); +@@ -93,12 +81,13 @@ static int bcm47xx_nvram_find_and_copy(v + } + + /* TODO: when nvram is on nand flash check for bad blocks first. */ ++ ++ /* Try every possible flash size and check for NVRAM at its end */ + for (flash_size = FLASH_MIN; flash_size <= res_size; flash_size <<= 1) { +- /* Windowed flash access */ +- size = find_nvram_size(flash_start + flash_size); +- if (size) { +- offset = flash_size - size; +- goto found; ++ for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { ++ offset = flash_size - nvram_sizes[i]; ++ if (bcm47xx_nvram_is_valid(flash_start + offset)) ++ goto found; + } + } + diff --git a/target/linux/generic/backport-5.4/831-v5.9-usbip-tools-fix-build-error-for-multiple-definition.patch b/target/linux/generic/backport-5.4/831-v5.9-usbip-tools-fix-build-error-for-multiple-definition.patch deleted file mode 100644 index 03f27fb528..0000000000 --- a/target/linux/generic/backport-5.4/831-v5.9-usbip-tools-fix-build-error-for-multiple-definition.patch +++ /dev/null @@ -1,33 +0,0 @@ -From d5efc2e6b98fe661dbd8dd0d5d5bfb961728e57a Mon Sep 17 00:00:00 2001 -From: Antonio Borneo -Date: Thu, 18 Jun 2020 02:08:44 +0200 -Subject: usbip: tools: fix build error for multiple definition - -With GCC 10, building usbip triggers error for multiple definition -of 'udev_context', in: -- libsrc/vhci_driver.c:18 and -- libsrc/usbip_host_common.c:27. - -Declare as extern the definition in libsrc/usbip_host_common.c. - -Signed-off-by: Antonio Borneo -Acked-by: Shuah Khan -Link: https://lore.kernel.org/r/20200618000844.1048309-1-borneo.antonio@gmail.com -Signed-off-by: Greg Kroah-Hartman ---- - tools/usb/usbip/libsrc/usbip_host_common.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -(limited to 'tools/usb/usbip') - ---- a/tools/usb/usbip/libsrc/usbip_host_common.c -+++ b/tools/usb/usbip/libsrc/usbip_host_common.c -@@ -23,7 +23,7 @@ - #include "list.h" - #include "sysfs_utils.h" - --struct udev *udev_context; -+extern struct udev *udev_context; - - static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) - { diff --git a/target/linux/generic/hack-5.10/650-netfilter-add-xt_OFFLOAD-target.patch b/target/linux/generic/hack-5.10/650-netfilter-add-xt_FLOWOFFLOAD-target.patch similarity index 93% rename from target/linux/generic/hack-5.10/650-netfilter-add-xt_OFFLOAD-target.patch rename to target/linux/generic/hack-5.10/650-netfilter-add-xt_FLOWOFFLOAD-target.patch index eb540acc85..97aa7a673b 100644 --- a/target/linux/generic/hack-5.10/650-netfilter-add-xt_OFFLOAD-target.patch +++ b/target/linux/generic/hack-5.10/650-netfilter-add-xt_FLOWOFFLOAD-target.patch @@ -1,6 +1,6 @@ From: Felix Fietkau Date: Tue, 20 Feb 2018 15:56:02 +0100 -Subject: [PATCH] netfilter: add xt_OFFLOAD target +Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target Signed-off-by: Felix Fietkau --- @@ -98,7 +98,7 @@ Signed-off-by: Felix Fietkau obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o --- /dev/null +++ b/net/netfilter/xt_FLOWOFFLOAD.c -@@ -0,0 +1,660 @@ +@@ -0,0 +1,658 @@ +/* + * Copyright (C) 2018-2021 Felix Fietkau + * @@ -265,20 +265,14 @@ Signed-off-by: Felix Fietkau +xt_flowoffload_check_hook(struct flow_offload *flow, void *data) +{ + struct xt_flowoffload_table *table = data; -+ struct flow_offload_tuple *tuple = &flow->tuplehash[0].tuple; ++ struct flow_offload_tuple *tuple0 = &flow->tuplehash[0].tuple; ++ struct flow_offload_tuple *tuple1 = &flow->tuplehash[1].tuple; + struct xt_flowoffload_hook *hook; + + spin_lock_bh(&hooks_lock); + hlist_for_each_entry(hook, &table->hooks, list) { -+ int ifindex; -+ -+ if (tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) -+ ifindex = tuple->out.ifidx; -+ else -+ ifindex = tuple->dst_cache->dev->ifindex; -+ -+ if (hook->ops.dev->ifindex != tuple->iifidx && -+ hook->ops.dev->ifindex != ifindex) ++ if (hook->ops.dev->ifindex != tuple0->iifidx && ++ hook->ops.dev->ifindex != tuple1->iifidx) + continue; + + hook->used = true; @@ -357,6 +351,7 @@ Signed-off-by: Felix Fietkau + int i; + + route->tuple[!dir].in.ifindex = dev->ifindex; ++ route->tuple[dir].out.ifindex = dev->ifindex; + + if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_XFRM) + return; @@ -386,52 +381,54 @@ Signed-off-by: Felix Fietkau + prev_type = DEV_PATH_ETHERNET; + for (i = 0; i <= stack.num_paths; i++) { + const struct net_device_path *path = &stack.path[i]; -+ int n_vlans = route->tuple[!dir].in.num_vlans; ++ int n_encaps = route->tuple[!dir].in.num_encaps; + + dev = (struct net_device *)path->dev; + if (flow_is_valid_ether_device(dev)) { -+ if (route->tuple[dir].xmit_type != FLOW_OFFLOAD_XMIT_DIRECT) ++ if (route->tuple[dir].xmit_type != FLOW_OFFLOAD_XMIT_DIRECT) { + memcpy(route->tuple[dir].out.h_source, + dev->dev_addr, ETH_ALEN); ++ route->tuple[dir].out.ifindex = dev->ifindex; ++ } + route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; -+ route->tuple[dir].out.ifindex = dev->ifindex; + } + + switch (path->type) { ++ case DEV_PATH_PPPOE: + case DEV_PATH_VLAN: -+ if (n_vlans >= NF_FLOW_TABLE_VLAN_MAX || ++ if (n_encaps >= NF_FLOW_TABLE_ENCAP_MAX || + i == stack.num_paths) { + last = true; + break; + } + -+ route->tuple[!dir].in.num_vlans++; -+ route->tuple[!dir].in.vid[n_vlans] = path->vlan.id; -+ route->tuple[!dir].in.vproto[n_vlans] = path->vlan.proto; ++ route->tuple[!dir].in.num_encaps++; ++ route->tuple[!dir].in.encap[n_encaps].id = path->encap.id; ++ route->tuple[!dir].in.encap[n_encaps].proto = path->encap.proto; ++ if (path->type == DEV_PATH_PPPOE) ++ memcpy(route->tuple[dir].out.h_dest, ++ path->encap.h_dest, ETH_ALEN); + break; + case DEV_PATH_BRIDGE: + switch (path->bridge.vlan_mode) { + case DEV_PATH_BR_VLAN_TAG: -+ if (n_vlans >= NF_FLOW_TABLE_VLAN_MAX || ++ if (n_encaps >= NF_FLOW_TABLE_ENCAP_MAX || + i == stack.num_paths) { + last = true; + break; + } + -+ route->tuple[!dir].in.num_vlans++; -+ route->tuple[!dir].in.vid[n_vlans] = ++ route->tuple[!dir].in.num_encaps++; ++ route->tuple[!dir].in.encap[n_encaps].id = + path->bridge.vlan_id; -+ route->tuple[!dir].in.vproto[n_vlans] = ++ route->tuple[!dir].in.encap[n_encaps].proto = + path->bridge.vlan_proto; + break; -+ case DEV_PATH_BR_VLAN_UNTAG_HW: -+ route->tuple[!dir].in.pvid.id = -+ route->tuple[!dir].in.vid[n_vlans - 1]; -+ route->tuple[!dir].in.pvid.proto = -+ route->tuple[!dir].in.vproto[n_vlans - 1]; -+ fallthrough; + case DEV_PATH_BR_VLAN_UNTAG: -+ route->tuple[!dir].in.num_vlans--; ++ route->tuple[!dir].in.num_encaps--; ++ break; ++ case DEV_PATH_BR_VLAN_UNTAG_HW: ++ route->tuple[!dir].in.ingress_vlans |= BIT(n_encaps - 1); + break; + case DEV_PATH_BR_VLAN_KEEP: + break; @@ -447,6 +444,7 @@ Signed-off-by: Felix Fietkau + } + + *out_dev = dev; ++ route->tuple[dir].out.hw_ifindex = dev->ifindex; + route->tuple[!dir].in.ifindex = dev->ifindex; +} + @@ -769,7 +767,7 @@ Signed-off-by: Felix Fietkau #include #include #include -@@ -355,8 +354,7 @@ flow_offload_lookup(struct nf_flowtable +@@ -356,8 +355,7 @@ flow_offload_lookup(struct nf_flowtable } EXPORT_SYMBOL_GPL(flow_offload_lookup); @@ -779,7 +777,7 @@ Signed-off-by: Felix Fietkau void (*iter)(struct flow_offload *flow, void *data), void *data) { -@@ -388,6 +386,7 @@ nf_flow_table_iterate(struct nf_flowtabl +@@ -389,6 +387,7 @@ nf_flow_table_iterate(struct nf_flowtabl return err; } @@ -809,7 +807,7 @@ Signed-off-by: Felix Fietkau +#endif /* _XT_FLOWOFFLOAD_H */ --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h -@@ -265,6 +265,10 @@ void nf_flow_table_free(struct nf_flowta +@@ -266,6 +266,10 @@ void nf_flow_table_free(struct nf_flowta void flow_offload_teardown(struct flow_offload *flow); diff --git a/target/linux/generic/hack-5.10/904-debloat_dma_buf.patch b/target/linux/generic/hack-5.10/904-debloat_dma_buf.patch index 3c5af9d666..2f5f685063 100644 --- a/target/linux/generic/hack-5.10/904-debloat_dma_buf.patch +++ b/target/linux/generic/hack-5.10/904-debloat_dma_buf.patch @@ -62,7 +62,7 @@ Signed-off-by: Felix Fietkau +MODULE_LICENSE("GPL"); --- a/kernel/sched/core.c +++ b/kernel/sched/core.c -@@ -3054,6 +3054,7 @@ int wake_up_state(struct task_struct *p, +@@ -3051,6 +3051,7 @@ int wake_up_state(struct task_struct *p, { return try_to_wake_up(p, state, 0); } diff --git a/target/linux/generic/hack-5.4/904-debloat_dma_buf.patch b/target/linux/generic/hack-5.4/904-debloat_dma_buf.patch index 7c67478fc0..92c230c511 100644 --- a/target/linux/generic/hack-5.4/904-debloat_dma_buf.patch +++ b/target/linux/generic/hack-5.4/904-debloat_dma_buf.patch @@ -54,7 +54,7 @@ Signed-off-by: Felix Fietkau +MODULE_LICENSE("GPL"); --- a/kernel/sched/core.c +++ b/kernel/sched/core.c -@@ -2756,6 +2756,7 @@ int wake_up_state(struct task_struct *p, +@@ -2754,6 +2754,7 @@ int wake_up_state(struct task_struct *p, { return try_to_wake_up(p, state, 0); } diff --git a/target/linux/generic/pending-5.10/401-0001-mtd-core-add-nvmem-cells-compatible-to-parse-mtd-as-.patch b/target/linux/generic/pending-5.10/401-0001-mtd-core-add-nvmem-cells-compatible-to-parse-mtd-as-.patch new file mode 100644 index 0000000000..28335cb71f --- /dev/null +++ b/target/linux/generic/pending-5.10/401-0001-mtd-core-add-nvmem-cells-compatible-to-parse-mtd-as-.patch @@ -0,0 +1,38 @@ +From a5d83d6e2bc747b13f347962d4b335d70b23559b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 12 Mar 2021 07:28:19 +0100 +Subject: [PATCH] mtd: core: add nvmem-cells compatible to parse mtd as nvmem + cells +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Partitions that contains the nvmem-cells compatible will register +their direct subonodes as nvmem cells and the node will be treated as a +nvmem provider. + +Signed-off-by: Ansuel Smith +Tested-by: Rafał Miłecki +--- + drivers/mtd/mtdcore.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -531,6 +531,7 @@ static int mtd_nvmem_reg_read(void *priv + + static int mtd_nvmem_add(struct mtd_info *mtd) + { ++ struct device_node *node = mtd_get_of_node(mtd); + struct nvmem_config config = {}; + + config.id = -1; +@@ -543,7 +544,7 @@ static int mtd_nvmem_add(struct mtd_info + config.stride = 1; + config.read_only = true; + config.root_only = true; +- config.no_of_node = true; ++ config.no_of_node = !of_device_is_compatible(node, "nvmem-cells"); + config.priv = mtd; + + mtd->nvmem = nvmem_register(&config); diff --git a/target/linux/generic/pending-5.10/401-0002-devicetree-nvmem-nvmem-drop-nodename-restriction.patch b/target/linux/generic/pending-5.10/401-0002-devicetree-nvmem-nvmem-drop-nodename-restriction.patch new file mode 100644 index 0000000000..14ea3f6b8c --- /dev/null +++ b/target/linux/generic/pending-5.10/401-0002-devicetree-nvmem-nvmem-drop-nodename-restriction.patch @@ -0,0 +1,25 @@ +From 42645976c3289b03a12f1bd2bc131fd98fc27170 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 12 Mar 2021 07:28:20 +0100 +Subject: [PATCH] devicetree: nvmem: nvmem: drop $nodename restriction + +Drop $nodename restriction as now mtd partition can also be used as +nvmem provider. + +Signed-off-by: Ansuel Smith +--- + Documentation/devicetree/bindings/nvmem/nvmem.yaml | 3 --- + 1 file changed, 3 deletions(-) + +--- a/Documentation/devicetree/bindings/nvmem/nvmem.yaml ++++ b/Documentation/devicetree/bindings/nvmem/nvmem.yaml +@@ -20,9 +20,6 @@ description: | + storage device. + + properties: +- $nodename: +- pattern: "^(eeprom|efuse|nvram)(@.*|-[0-9a-f])*$" +- + "#address-cells": + const: 1 + diff --git a/target/linux/generic/pending-5.10/401-0003-dt-bindings-mtd-Document-use-of-nvmem-cells-compatib.patch b/target/linux/generic/pending-5.10/401-0003-dt-bindings-mtd-Document-use-of-nvmem-cells-compatib.patch new file mode 100644 index 0000000000..0eb4c637cf --- /dev/null +++ b/target/linux/generic/pending-5.10/401-0003-dt-bindings-mtd-Document-use-of-nvmem-cells-compatib.patch @@ -0,0 +1,117 @@ +From 377aa0135dc8489312edd3184d143ce3a89ff7ee Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 12 Mar 2021 07:28:21 +0100 +Subject: [PATCH] dt-bindings: mtd: Document use of nvmem-cells compatible + +Document nvmem-cells compatible used to treat mtd partitions as a +nvmem provider. + +Signed-off-by: Ansuel Smith +Reviewed-by: Rob Herring +--- + .../bindings/mtd/partitions/nvmem-cells.yaml | 99 +++++++++++++++++++ + 1 file changed, 99 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/partitions/nvmem-cells.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/partitions/nvmem-cells.yaml +@@ -0,0 +1,99 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/partitions/nvmem-cells.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Nvmem cells ++ ++description: | ++ Any partition containing the compatible "nvmem-cells" will register as a ++ nvmem provider. ++ Each direct subnodes represents a nvmem cell following the nvmem binding. ++ Nvmem binding to declare nvmem-cells can be found in: ++ Documentation/devicetree/bindings/nvmem/nvmem.yaml ++ ++maintainers: ++ - Ansuel Smith ++ ++allOf: ++ - $ref: /schemas/nvmem/nvmem.yaml# ++ ++properties: ++ compatible: ++ const: nvmem-cells ++ ++required: ++ - compatible ++ ++additionalProperties: true ++ ++examples: ++ - | ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ /* ... */ ++ ++ }; ++ art: art@1200000 { ++ compatible = "nvmem-cells"; ++ reg = <0x1200000 0x0140000>; ++ label = "art"; ++ read-only; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ macaddr_gmac1: macaddr_gmac1@0 { ++ reg = <0x0 0x6>; ++ }; ++ ++ macaddr_gmac2: macaddr_gmac2@6 { ++ reg = <0x6 0x6>; ++ }; ++ ++ pre_cal_24g: pre_cal_24g@1000 { ++ reg = <0x1000 0x2f20>; ++ }; ++ ++ pre_cal_5g: pre_cal_5g@5000{ ++ reg = <0x5000 0x2f20>; ++ }; ++ }; ++ - | ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "bootloader"; ++ reg = <0x000000 0x100000>; ++ read-only; ++ }; ++ ++ firmware@100000 { ++ compatible = "brcm,trx"; ++ label = "firmware"; ++ reg = <0x100000 0xe00000>; ++ }; ++ ++ calibration@f00000 { ++ compatible = "nvmem-cells"; ++ label = "calibration"; ++ reg = <0xf00000 0x100000>; ++ ranges = <0 0xf00000 0x100000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ wifi0@0 { ++ reg = <0x000000 0x080000>; ++ }; ++ ++ wifi1@80000 { ++ reg = <0x080000 0x080000>; ++ }; ++ }; ++ }; diff --git a/target/linux/generic/pending-5.10/480-mtd-set-rootfs-to-be-root-dev.patch b/target/linux/generic/pending-5.10/480-mtd-set-rootfs-to-be-root-dev.patch index 11cdc179d2..e01b991942 100644 --- a/target/linux/generic/pending-5.10/480-mtd-set-rootfs-to-be-root-dev.patch +++ b/target/linux/generic/pending-5.10/480-mtd-set-rootfs-to-be-root-dev.patch @@ -20,7 +20,7 @@ Signed-off-by: Gabor Juhos #include #include -@@ -692,6 +693,15 @@ int add_mtd_device(struct mtd_info *mtd) +@@ -693,6 +694,15 @@ int add_mtd_device(struct mtd_info *mtd) of this try_ nonsense, and no bitching about it either. :) */ __module_get(THIS_MODULE); diff --git a/target/linux/generic/pending-5.10/495-mtd-core-add-get_mtd_device_by_node.patch b/target/linux/generic/pending-5.10/495-mtd-core-add-get_mtd_device_by_node.patch index 98c5c2ef19..7a7ad6b263 100644 --- a/target/linux/generic/pending-5.10/495-mtd-core-add-get_mtd_device_by_node.patch +++ b/target/linux/generic/pending-5.10/495-mtd-core-add-get_mtd_device_by_node.patch @@ -17,7 +17,7 @@ Reviewed-by: Miquel Raynal --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c -@@ -1052,6 +1052,44 @@ out_unlock: +@@ -1053,6 +1053,44 @@ out_unlock: } EXPORT_SYMBOL_GPL(get_mtd_device_nm); diff --git a/target/linux/generic/pending-5.10/640-02-net-resolve-forwarding-path-from-virtual-netdevice-a.patch b/target/linux/generic/pending-5.10/640-02-net-resolve-forwarding-path-from-virtual-netdevice-a.patch index 953446baab..603dddfc36 100644 --- a/target/linux/generic/pending-5.10/640-02-net-resolve-forwarding-path-from-virtual-netdevice-a.patch +++ b/target/linux/generic/pending-5.10/640-02-net-resolve-forwarding-path-from-virtual-netdevice-a.patch @@ -1,5 +1,5 @@ From: Pablo Neira Ayuso -Date: Sun, 10 Jan 2021 15:53:58 +0100 +Date: Thu, 4 Mar 2021 23:18:11 +0100 Subject: [PATCH] net: resolve forwarding path from virtual netdevice and HW destination address @@ -91,7 +91,7 @@ Signed-off-by: Pablo Neira Ayuso * If a device is paired with a peer device, return the peer instance. * The caller must be under RCU read context. + * int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, struct net_device_path *path); -+ * Get the forwarding path to reach the real device from the HW destination address ++ * Get the forwarding path to reach the real device from the HW destination address */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -99,8 +99,8 @@ Signed-off-by: Pablo Neira Ayuso int (*ndo_tunnel_ctl)(struct net_device *dev, struct ip_tunnel_parm *p, int cmd); struct net_device * (*ndo_get_peer_dev)(struct net_device *dev); -+ int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, -+ struct net_device_path *path); ++ int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, ++ struct net_device_path *path); }; /** diff --git a/target/linux/generic/pending-5.10/640-03-net-8021q-resolve-forwarding-path-for-vlan-devices.patch b/target/linux/generic/pending-5.10/640-03-net-8021q-resolve-forwarding-path-for-vlan-devices.patch index ddc0de0d3b..26fb64509c 100644 --- a/target/linux/generic/pending-5.10/640-03-net-8021q-resolve-forwarding-path-for-vlan-devices.patch +++ b/target/linux/generic/pending-5.10/640-03-net-8021q-resolve-forwarding-path-for-vlan-devices.patch @@ -42,7 +42,7 @@ Signed-off-by: Pablo Neira Ayuso + struct { + u16 id; + __be16 proto; -+ } vlan; ++ } encap; + }; }; @@ -59,8 +59,8 @@ Signed-off-by: Pablo Neira Ayuso + struct vlan_dev_priv *vlan = vlan_dev_priv(ctx->dev); + + path->type = DEV_PATH_VLAN; -+ path->vlan.id = vlan->vlan_id; -+ path->vlan.proto = vlan->vlan_proto; ++ path->encap.id = vlan->vlan_id; ++ path->encap.proto = vlan->vlan_proto; + path->dev = ctx->dev; + ctx->dev = vlan->real_dev; + diff --git a/target/linux/generic/pending-5.10/640-05-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch b/target/linux/generic/pending-5.10/640-05-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch index 34724a5696..1f61cff09d 100644 --- a/target/linux/generic/pending-5.10/640-05-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch +++ b/target/linux/generic/pending-5.10/640-05-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch @@ -89,7 +89,7 @@ Signed-off-by: Pablo Neira Ayuso +} + +struct nft_forward_info { -+ const struct net_device *dev; ++ const struct net_device *indev; +}; + +static void nft_dev_path_info(const struct net_device_path_stack *stack, @@ -102,12 +102,12 @@ Signed-off-by: Pablo Neira Ayuso + path = &stack->path[i]; + switch (path->type) { + case DEV_PATH_ETHERNET: -+ info->dev = path->dev; ++ info->indev = path->dev; + break; + case DEV_PATH_VLAN: + case DEV_PATH_BRIDGE: + default: -+ info->dev = NULL; ++ info->indev = NULL; + break; + } + } @@ -142,10 +142,10 @@ Signed-off-by: Pablo Neira Ayuso + if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0) + nft_dev_path_info(&stack, &info); + -+ if (!info.dev || !nft_flowtable_find_dev(info.dev, ft)) ++ if (!info.indev || !nft_flowtable_find_dev(info.indev, ft)) + return; + -+ route->tuple[!dir].in.ifindex = info.dev->ifindex; ++ route->tuple[!dir].in.ifindex = info.indev->ifindex; +} + static int nft_flow_route(const struct nft_pktinfo *pkt, diff --git a/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch b/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch index d4bf68a690..ada14e6c7a 100644 --- a/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch +++ b/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch @@ -1,5 +1,5 @@ From: Pablo Neira Ayuso -Date: Fri, 20 Nov 2020 13:49:19 +0100 +Date: Thu, 4 Mar 2021 03:26:35 +0100 Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to obtain egress device @@ -271,10 +271,11 @@ Signed-off-by: Pablo Neira Ayuso struct neighbour *n; u8 nud_state; -@@ -66,22 +65,35 @@ static int nft_dev_fill_forward_path(con +@@ -66,27 +65,43 @@ static int nft_dev_fill_forward_path(con struct nft_forward_info { - const struct net_device *dev; + const struct net_device *indev; ++ const struct net_device *outdev; + u8 h_source[ETH_ALEN]; + u8 h_dest[ETH_ALEN]; + enum flow_offload_xmit_type xmit_type; @@ -294,7 +295,7 @@ Signed-off-by: Pablo Neira Ayuso path = &stack->path[i]; switch (path->type) { case DEV_PATH_ETHERNET: - info->dev = path->dev; + info->indev = path->dev; + if (is_zero_ether_addr(info->h_source)) + memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); break; @@ -307,9 +308,16 @@ Signed-off-by: Pablo Neira Ayuso + break; + case DEV_PATH_VLAN: default: - info->dev = NULL; + info->indev = NULL; break; -@@ -114,14 +126,22 @@ static void nft_dev_forward_path(struct + } + } ++ if (!info->outdev) ++ info->outdev = info->indev; + } + + static bool nft_flowtable_find_dev(const struct net_device *dev, +@@ -114,14 +129,22 @@ static void nft_dev_forward_path(struct const struct dst_entry *dst = route->tuple[dir].dst; struct net_device_path_stack stack; struct nft_forward_info info = {}; @@ -320,15 +328,15 @@ Signed-off-by: Pablo Neira Ayuso + if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) + nft_dev_path_info(&stack, &info, ha); - if (!info.dev || !nft_flowtable_find_dev(info.dev, ft)) + if (!info.indev || !nft_flowtable_find_dev(info.indev, ft)) return; - route->tuple[!dir].in.ifindex = info.dev->ifindex; + route->tuple[!dir].in.ifindex = info.indev->ifindex; + + if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { + memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); + memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); -+ route->tuple[dir].out.ifindex = info.dev->ifindex; ++ route->tuple[dir].out.ifindex = info.outdev->ifindex; + route->tuple[dir].xmit_type = info.xmit_type; + } } diff --git a/target/linux/generic/pending-5.10/640-07-netfilter-flowtable-add-vlan-support.patch b/target/linux/generic/pending-5.10/640-07-netfilter-flowtable-add-vlan-support.patch index 77aeaccbce..fea1b59dc3 100644 --- a/target/linux/generic/pending-5.10/640-07-netfilter-flowtable-add-vlan-support.patch +++ b/target/linux/generic/pending-5.10/640-07-netfilter-flowtable-add-vlan-support.patch @@ -16,7 +16,7 @@ Signed-off-by: Pablo Neira Ayuso FLOW_OFFLOAD_XMIT_DIRECT, }; -+#define NF_FLOW_TABLE_VLAN_MAX 2 ++#define NF_FLOW_TABLE_ENCAP_MAX 2 + struct flow_offload_tuple { union { @@ -28,7 +28,7 @@ Signed-off-by: Pablo Neira Ayuso + struct { + u16 id; + __be16 proto; -+ } in_vlan[NF_FLOW_TABLE_VLAN_MAX]; ++ } encap[NF_FLOW_TABLE_ENCAP_MAX]; /* All members above are keys for lookups, see flow_offload_hash(). */ struct { } __hash; @@ -38,17 +38,19 @@ Signed-off-by: Pablo Neira Ayuso - + u8 dir:4, + xmit_type:2, -+ in_vlan_num:2; ++ encap_num:2; u16 mtu; union { struct dst_entry *dst_cache; -@@ -174,6 +180,9 @@ struct nf_flow_route { +@@ -174,6 +180,11 @@ struct nf_flow_route { struct dst_entry *dst; struct { u32 ifindex; -+ u16 vid[NF_FLOW_TABLE_VLAN_MAX]; -+ __be16 vproto[NF_FLOW_TABLE_VLAN_MAX]; -+ u8 num_vlans; ++ struct { ++ u16 id; ++ __be16 proto; ++ } encap[NF_FLOW_TABLE_ENCAP_MAX]; ++ u8 num_encaps; } in; struct { u32 ifindex; @@ -66,33 +68,36 @@ Signed-off-by: Pablo Neira Ayuso } flow_tuple->iifidx = route->tuple[dir].in.ifindex; -+ for (i = route->tuple[dir].in.num_vlans - 1; i >= 0; i--) { -+ flow_tuple->in_vlan[j].id = route->tuple[dir].in.vid[i]; -+ flow_tuple->in_vlan[j].proto = route->tuple[dir].in.vproto[i]; ++ for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) { ++ flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id; ++ flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto; + j++; + } -+ flow_tuple->in_vlan_num = route->tuple[dir].in.num_vlans; ++ flow_tuple->encap_num = route->tuple[dir].in.num_encaps; switch (route->tuple[dir].xmit_type) { case FLOW_OFFLOAD_XMIT_DIRECT: --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c -@@ -159,17 +159,35 @@ static bool ip_has_options(unsigned int +@@ -159,17 +159,38 @@ static bool ip_has_options(unsigned int return thoff != sizeof(struct iphdr); } -+static void nf_flow_tuple_vlan(struct sk_buff *skb, -+ struct flow_offload_tuple *tuple) ++static void nf_flow_tuple_encap(struct sk_buff *skb, ++ struct flow_offload_tuple *tuple) +{ ++ int i = 0; ++ + if (skb_vlan_tag_present(skb)) { -+ tuple->in_vlan[0].id = skb_vlan_tag_get(skb); -+ tuple->in_vlan[0].proto = skb->vlan_proto; ++ tuple->encap[i].id = skb_vlan_tag_get(skb); ++ tuple->encap[i].proto = skb->vlan_proto; ++ i++; + } + if (skb->protocol == htons(ETH_P_8021Q)) { + struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb); + -+ tuple->in_vlan[1].id = ntohs(veth->h_vlan_TCI); -+ tuple->in_vlan[1].proto = skb->protocol; ++ tuple->encap[i].id = ntohs(veth->h_vlan_TCI); ++ tuple->encap[i].proto = skb->protocol; + } +} + @@ -116,7 +121,7 @@ Signed-off-by: Pablo Neira Ayuso thoff = iph->ihl * 4; if (ip_is_fragment(iph) || -@@ -191,11 +209,11 @@ static int nf_flow_tuple_ip(struct sk_bu +@@ -191,11 +212,11 @@ static int nf_flow_tuple_ip(struct sk_bu return -1; thoff = iph->ihl * 4; @@ -131,19 +136,19 @@ Signed-off-by: Pablo Neira Ayuso tuple->src_v4.s_addr = iph->saddr; tuple->dst_v4.s_addr = iph->daddr; -@@ -204,6 +222,7 @@ static int nf_flow_tuple_ip(struct sk_bu +@@ -204,6 +225,7 @@ static int nf_flow_tuple_ip(struct sk_bu tuple->l3proto = AF_INET; tuple->l4proto = iph->protocol; tuple->iifidx = dev->ifindex; -+ nf_flow_tuple_vlan(skb, tuple); ++ nf_flow_tuple_encap(skb, tuple); return 0; } -@@ -248,6 +267,37 @@ static unsigned int nf_flow_xmit_xfrm(st +@@ -248,6 +270,40 @@ static unsigned int nf_flow_xmit_xfrm(st return NF_STOLEN; } -+static bool nf_flow_skb_vlan_protocol(const struct sk_buff *skb, __be16 proto) ++static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto) +{ + if (skb->protocol == htons(ETH_P_8021Q)) { + struct vlan_ethhdr *veth; @@ -156,29 +161,37 @@ Signed-off-by: Pablo Neira Ayuso + return false; +} + -+static void nf_flow_vlan_pop(struct sk_buff *skb, -+ struct flow_offload_tuple_rhash *tuplehash) ++static void nf_flow_encap_pop(struct sk_buff *skb, ++ struct flow_offload_tuple_rhash *tuplehash) +{ + struct vlan_hdr *vlan_hdr; + int i; + -+ for (i = 0; i < tuplehash->tuple.in_vlan_num; i++) { ++ for (i = 0; i < tuplehash->tuple.encap_num; i++) { + if (skb_vlan_tag_present(skb)) { + __vlan_hwaccel_clear_tag(skb); + continue; + } -+ vlan_hdr = (struct vlan_hdr *)skb->data; -+ __skb_pull(skb, VLAN_HLEN); -+ vlan_set_encap_proto(skb, vlan_hdr); -+ skb_reset_network_header(skb); ++ if (skb->protocol == htons(ETH_P_8021Q)) { ++ vlan_hdr = (struct vlan_hdr *)skb->data; ++ __skb_pull(skb, VLAN_HLEN); ++ vlan_set_encap_proto(skb, vlan_hdr); ++ skb_reset_network_header(skb); ++ break; ++ } + } +} + static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb, const struct flow_offload_tuple_rhash *tuplehash, unsigned short type) -@@ -280,9 +330,11 @@ nf_flow_offload_ip_hook(void *priv, stru - unsigned int thoff; +@@ -276,13 +332,15 @@ nf_flow_offload_ip_hook(void *priv, stru + enum flow_offload_tuple_dir dir; + struct flow_offload *flow; + struct net_device *outdev; ++ unsigned int thoff, mtu; + struct rtable *rt; +- unsigned int thoff; struct iphdr *iph; __be32 nexthop; + u32 offset = 0; @@ -186,12 +199,17 @@ Signed-off-by: Pablo Neira Ayuso - if (skb->protocol != htons(ETH_P_IP)) + if (skb->protocol != htons(ETH_P_IP) && -+ !nf_flow_skb_vlan_protocol(skb, htons(ETH_P_IP))) ++ !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IP))) return NF_ACCEPT; if (nf_flow_tuple_ip(skb, state->in, &tuple) < 0) -@@ -298,11 +350,15 @@ nf_flow_offload_ip_hook(void *priv, stru - if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu))) +@@ -295,14 +353,19 @@ nf_flow_offload_ip_hook(void *priv, stru + dir = tuplehash->tuple.dir; + flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); + +- if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu))) ++ mtu = flow->tuplehash[dir].tuple.mtu + offset; ++ if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) return NF_ACCEPT; - if (skb_try_make_writable(skb, sizeof(*iph))) @@ -209,17 +227,17 @@ Signed-off-by: Pablo Neira Ayuso return NF_ACCEPT; flow_offload_refresh(flow_table, flow); -@@ -312,6 +368,9 @@ nf_flow_offload_ip_hook(void *priv, stru +@@ -312,6 +375,9 @@ nf_flow_offload_ip_hook(void *priv, stru return NF_ACCEPT; } -+ nf_flow_vlan_pop(skb, tuplehash); ++ nf_flow_encap_pop(skb, tuplehash); + thoff -= offset; + if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0) return NF_DROP; -@@ -479,14 +538,17 @@ static int nf_flow_nat_ipv6(const struct +@@ -479,14 +545,17 @@ static int nf_flow_nat_ipv6(const struct static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev, struct flow_offload_tuple *tuple) { @@ -240,7 +258,7 @@ Signed-off-by: Pablo Neira Ayuso switch (ip6h->nexthdr) { case IPPROTO_TCP: -@@ -503,11 +565,11 @@ static int nf_flow_tuple_ipv6(struct sk_ +@@ -503,11 +572,11 @@ static int nf_flow_tuple_ipv6(struct sk_ return -1; thoff = sizeof(*ip6h); @@ -255,29 +273,35 @@ Signed-off-by: Pablo Neira Ayuso tuple->src_v6 = ip6h->saddr; tuple->dst_v6 = ip6h->daddr; -@@ -516,6 +578,7 @@ static int nf_flow_tuple_ipv6(struct sk_ +@@ -516,6 +585,7 @@ static int nf_flow_tuple_ipv6(struct sk_ tuple->l3proto = AF_INET6; tuple->l4proto = ip6h->nexthdr; tuple->iifidx = dev->ifindex; -+ nf_flow_tuple_vlan(skb, tuple); ++ nf_flow_tuple_encap(skb, tuple); return 0; } -@@ -533,9 +596,11 @@ nf_flow_offload_ipv6_hook(void *priv, st +@@ -533,9 +603,12 @@ nf_flow_offload_ipv6_hook(void *priv, st struct net_device *outdev; struct ipv6hdr *ip6h; struct rt6_info *rt; ++ unsigned int mtu; + u32 offset = 0; int ret; - if (skb->protocol != htons(ETH_P_IPV6)) + if (skb->protocol != htons(ETH_P_IPV6) && -+ !nf_flow_skb_vlan_protocol(skb, htons(ETH_P_IPV6))) ++ !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IPV6))) return NF_ACCEPT; if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0) -@@ -551,8 +616,11 @@ nf_flow_offload_ipv6_hook(void *priv, st - if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu))) +@@ -548,11 +621,15 @@ nf_flow_offload_ipv6_hook(void *priv, st + dir = tuplehash->tuple.dir; + flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); + +- if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu))) ++ mtu = flow->tuplehash[dir].tuple.mtu + offset; ++ if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) return NF_ACCEPT; - if (nf_flow_state_check(flow, ipv6_hdr(skb)->nexthdr, skb, @@ -290,33 +314,35 @@ Signed-off-by: Pablo Neira Ayuso return NF_ACCEPT; flow_offload_refresh(flow_table, flow); -@@ -562,6 +630,8 @@ nf_flow_offload_ipv6_hook(void *priv, st +@@ -562,6 +639,8 @@ nf_flow_offload_ipv6_hook(void *priv, st return NF_ACCEPT; } -+ nf_flow_vlan_pop(skb, tuplehash); ++ nf_flow_encap_pop(skb, tuplehash); + if (skb_try_make_writable(skb, sizeof(*ip6h))) return NF_DROP; --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c -@@ -65,6 +65,9 @@ static int nft_dev_fill_forward_path(con - +@@ -66,6 +66,11 @@ static int nft_dev_fill_forward_path(con struct nft_forward_info { - const struct net_device *dev; -+ __u16 vid[NF_FLOW_TABLE_VLAN_MAX]; -+ __be16 vproto[NF_FLOW_TABLE_VLAN_MAX]; -+ u8 num_vlans; + const struct net_device *indev; + const struct net_device *outdev; ++ struct id { ++ __u16 id; ++ __be16 proto; ++ } encap[NF_FLOW_TABLE_ENCAP_MAX]; ++ u8 num_encaps; u8 h_source[ETH_ALEN]; u8 h_dest[ETH_ALEN]; enum flow_offload_xmit_type xmit_type; -@@ -83,9 +86,22 @@ static void nft_dev_path_info(const stru +@@ -84,9 +89,23 @@ static void nft_dev_path_info(const stru path = &stack->path[i]; switch (path->type) { case DEV_PATH_ETHERNET: + case DEV_PATH_VLAN: - info->dev = path->dev; + info->indev = path->dev; if (is_zero_ether_addr(info->h_source)) memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); + @@ -324,25 +350,26 @@ Signed-off-by: Pablo Neira Ayuso + break; + + /* DEV_PATH_VLAN */ -+ if (info->num_vlans >= NF_FLOW_TABLE_VLAN_MAX) { -+ info->dev = NULL; ++ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { ++ info->indev = NULL; + break; + } -+ info->vid[info->num_vlans] = path->vlan.id; -+ info->vproto[info->num_vlans] = path->vlan.proto; -+ info->num_vlans++; ++ info->outdev = path->dev; ++ info->encap[info->num_encaps].id = path->encap.id; ++ info->encap[info->num_encaps].proto = path->encap.proto; ++ info->num_encaps++; break; case DEV_PATH_BRIDGE: if (is_zero_ether_addr(info->h_source)) -@@ -93,7 +109,6 @@ static void nft_dev_path_info(const stru +@@ -94,7 +113,6 @@ static void nft_dev_path_info(const stru info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; break; - case DEV_PATH_VLAN: default: - info->dev = NULL; + info->indev = NULL; break; -@@ -127,6 +142,7 @@ static void nft_dev_forward_path(struct +@@ -130,6 +148,7 @@ static void nft_dev_forward_path(struct struct net_device_path_stack stack; struct nft_forward_info info = {}; unsigned char ha[ETH_ALEN]; @@ -350,15 +377,15 @@ Signed-off-by: Pablo Neira Ayuso if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) nft_dev_path_info(&stack, &info, ha); -@@ -135,6 +151,11 @@ static void nft_dev_forward_path(struct +@@ -138,6 +157,11 @@ static void nft_dev_forward_path(struct return; - route->tuple[!dir].in.ifindex = info.dev->ifindex; -+ for (i = 0; i < info.num_vlans; i++) { -+ route->tuple[!dir].in.vid[i] = info.vid[i]; -+ route->tuple[!dir].in.vproto[i] = info.vproto[i]; + route->tuple[!dir].in.ifindex = info.indev->ifindex; ++ for (i = 0; i < info.num_encaps; i++) { ++ route->tuple[!dir].in.encap[i].id = info.encap[i].id; ++ route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; + } -+ route->tuple[!dir].in.num_vlans = info.num_vlans; ++ route->tuple[!dir].in.num_encaps = info.num_encaps; if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); diff --git a/target/linux/generic/pending-5.10/640-09-net-bridge-resolve-VLAN-tag-actions-in-forwarding-pa.patch b/target/linux/generic/pending-5.10/640-09-net-bridge-resolve-VLAN-tag-actions-in-forwarding-pa.patch index fb5f736648..9bd1f6b4c6 100644 --- a/target/linux/generic/pending-5.10/640-09-net-bridge-resolve-VLAN-tag-actions-in-forwarding-pa.patch +++ b/target/linux/generic/pending-5.10/640-09-net-bridge-resolve-VLAN-tag-actions-in-forwarding-pa.patch @@ -18,7 +18,7 @@ Signed-off-by: Pablo Neira Ayuso @@ -847,10 +847,20 @@ struct net_device_path { u16 id; __be16 proto; - } vlan; + } encap; + struct { + enum { + DEV_PATH_BR_VLAN_KEEP, @@ -52,7 +52,7 @@ Signed-off-by: Pablo Neira Ayuso --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -777,6 +777,12 @@ static int vlan_dev_fill_forward_path(st - path->vlan.proto = vlan->vlan_proto; + path->encap.proto = vlan->vlan_proto; path->dev = ctx->dev; ctx->dev = vlan->real_dev; + if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan)) diff --git a/target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch b/target/linux/generic/pending-5.10/640-10-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch similarity index 74% rename from target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch rename to target/linux/generic/pending-5.10/640-10-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch index 2e833117db..86fd6bf77b 100644 --- a/target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch +++ b/target/linux/generic/pending-5.10/640-10-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch @@ -10,18 +10,18 @@ Signed-off-by: Pablo Neira Ayuso --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c -@@ -112,6 +112,18 @@ static void nft_dev_path_info(const stru +@@ -111,6 +111,18 @@ static void nft_dev_path_info(const stru if (is_zero_ether_addr(info->h_source)) memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); + switch (path->bridge.vlan_mode) { + case DEV_PATH_BR_VLAN_TAG: -+ info->vid[info->num_vlans] = path->vlan.id; -+ info->vproto[info->num_vlans] = path->vlan.proto; -+ info->num_vlans++; ++ info->encap[info->num_encaps].id = path->bridge.vlan_id; ++ info->encap[info->num_encaps].proto = path->bridge.vlan_proto; ++ info->num_encaps++; + break; + case DEV_PATH_BR_VLAN_UNTAG: -+ info->num_vlans--; ++ info->num_encaps--; + break; + case DEV_PATH_BR_VLAN_KEEP: + break; diff --git a/target/linux/generic/pending-5.10/640-11-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch b/target/linux/generic/pending-5.10/640-11-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch new file mode 100644 index 0000000000..34d918206f --- /dev/null +++ b/target/linux/generic/pending-5.10/640-11-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch @@ -0,0 +1,100 @@ +From: Pablo Neira Ayuso +Date: Tue, 2 Mar 2021 21:45:16 +0100 +Subject: [PATCH] net: ppp: resolve forwarding path for bridge pppoe + devices + +Pass on the PPPoE session ID and the real device. +--- + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -1450,12 +1450,34 @@ static void ppp_dev_priv_destructor(stru + ppp_destroy_interface(ppp); + } + ++static int ppp_fill_forward_path(struct net_device_path_ctx *ctx, ++ struct net_device_path *path) ++{ ++ struct ppp *ppp = netdev_priv(path->dev); ++ struct ppp_channel *chan; ++ struct channel *pch; ++ ++ if (ppp->flags & SC_MULTILINK) ++ return -EOPNOTSUPP; ++ ++ if (list_empty(&ppp->channels)) ++ return -ENODEV; ++ ++ pch = list_first_entry(&ppp->channels, struct channel, clist); ++ chan = pch->chan; ++ if (!chan->ops->fill_forward_path) ++ return -EOPNOTSUPP; ++ ++ return chan->ops->fill_forward_path(ctx, path, chan); ++} ++ + static const struct net_device_ops ppp_netdev_ops = { + .ndo_init = ppp_dev_init, + .ndo_uninit = ppp_dev_uninit, + .ndo_start_xmit = ppp_start_xmit, + .ndo_do_ioctl = ppp_net_ioctl, + .ndo_get_stats64 = ppp_get_stats64, ++ .ndo_fill_forward_path = ppp_fill_forward_path, + }; + + static struct device_type ppp_type = { +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -972,8 +972,30 @@ static int pppoe_xmit(struct ppp_channel + return __pppoe_xmit(sk, skb); + } + ++static int pppoe_fill_forward_path(struct net_device_path_ctx *ctx, ++ struct net_device_path *path, ++ const struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ struct pppox_sock *po = pppox_sk(sk); ++ struct net_device *dev = po->pppoe_dev; ++ ++ if (sock_flag(sk, SOCK_DEAD) || ++ !(sk->sk_state & PPPOX_CONNECTED) || !dev) ++ return -1; ++ ++ path->type = DEV_PATH_PPPOE; ++ path->encap.proto = htons(ETH_P_PPP_SES); ++ path->encap.id = be16_to_cpu(po->num); ++ path->dev = ctx->dev; ++ ctx->dev = dev; ++ ++ return 0; ++} ++ + static const struct ppp_channel_ops pppoe_chan_ops = { + .start_xmit = pppoe_xmit, ++ .fill_forward_path = pppoe_fill_forward_path, + }; + + static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -837,6 +837,7 @@ enum net_device_path_type { + DEV_PATH_ETHERNET = 0, + DEV_PATH_VLAN, + DEV_PATH_BRIDGE, ++ DEV_PATH_PPPOE, + }; + + struct net_device_path { +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -28,6 +28,9 @@ struct ppp_channel_ops { + int (*start_xmit)(struct ppp_channel *, struct sk_buff *); + /* Handle an ioctl call that has come in via /dev/ppp. */ + int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long); ++ int (*fill_forward_path)(struct net_device_path_ctx *, ++ struct net_device_path *, ++ const struct ppp_channel *); + }; + + struct ppp_channel { diff --git a/target/linux/generic/pending-5.10/640-10-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch b/target/linux/generic/pending-5.10/640-12-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch similarity index 87% rename from target/linux/generic/pending-5.10/640-10-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch rename to target/linux/generic/pending-5.10/640-12-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch index 64a2b14af5..3e7a9eaef9 100644 --- a/target/linux/generic/pending-5.10/640-10-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch +++ b/target/linux/generic/pending-5.10/640-12-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch @@ -1,23 +1,21 @@ From: Felix Fietkau -Date: Mon, 7 Dec 2020 20:31:48 +0100 +Date: Thu, 4 Mar 2021 23:19:06 +0100 Subject: [PATCH] net: dsa: resolve forwarding path for dsa slave ports Add .ndo_fill_forward_path for dsa slave port devices - -Signed-off-by: Felix Fietkau --- --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -837,6 +837,7 @@ enum net_device_path_type { - DEV_PATH_ETHERNET = 0, +@@ -838,6 +838,7 @@ enum net_device_path_type { DEV_PATH_VLAN, DEV_PATH_BRIDGE, + DEV_PATH_PPPOE, + DEV_PATH_DSA, }; struct net_device_path { -@@ -856,6 +857,10 @@ struct net_device_path { +@@ -857,6 +858,10 @@ struct net_device_path { u16 vlan_id; __be16 vlan_proto; } bridge; diff --git a/target/linux/generic/pending-5.10/640-13-netfilter-flowtable-add-pppoe-support.patch b/target/linux/generic/pending-5.10/640-13-netfilter-flowtable-add-pppoe-support.patch new file mode 100644 index 0000000000..6a14514565 --- /dev/null +++ b/target/linux/generic/pending-5.10/640-13-netfilter-flowtable-add-pppoe-support.patch @@ -0,0 +1,263 @@ +From: Pablo Neira Ayuso +Date: Mon, 1 Mar 2021 23:52:49 +0100 +Subject: [PATCH] netfilter: flowtable: add pppoe support + +--- + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -1453,7 +1453,7 @@ static void ppp_dev_priv_destructor(stru + static int ppp_fill_forward_path(struct net_device_path_ctx *ctx, + struct net_device_path *path) + { +- struct ppp *ppp = netdev_priv(path->dev); ++ struct ppp *ppp = netdev_priv(ctx->dev); + struct ppp_channel *chan; + struct channel *pch; + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -987,6 +987,7 @@ static int pppoe_fill_forward_path(struc + path->type = DEV_PATH_PPPOE; + path->encap.proto = htons(ETH_P_PPP_SES); + path->encap.id = be16_to_cpu(po->num); ++ memcpy(path->encap.h_dest, po->pppoe_pa.remote, ETH_ALEN); + path->dev = ctx->dev; + ctx->dev = dev; + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -848,6 +848,7 @@ struct net_device_path { + struct { + u16 id; + __be16 proto; ++ u8 h_dest[ETH_ALEN]; + } encap; + struct { + enum { +--- a/net/netfilter/nf_flow_table_ip.c ++++ b/net/netfilter/nf_flow_table_ip.c +@@ -7,6 +7,9 @@ + #include + #include + #include ++#include ++#include ++#include + #include + #include + #include +@@ -162,6 +165,8 @@ static bool ip_has_options(unsigned int + static void nf_flow_tuple_encap(struct sk_buff *skb, + struct flow_offload_tuple *tuple) + { ++ struct vlan_ethhdr *veth; ++ struct pppoe_hdr *phdr; + int i = 0; + + if (skb_vlan_tag_present(skb)) { +@@ -169,23 +174,35 @@ static void nf_flow_tuple_encap(struct s + tuple->encap[i].proto = skb->vlan_proto; + i++; + } +- if (skb->protocol == htons(ETH_P_8021Q)) { +- struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb); +- ++ switch (skb->protocol) { ++ case htons(ETH_P_8021Q): ++ veth = (struct vlan_ethhdr *)skb_mac_header(skb); + tuple->encap[i].id = ntohs(veth->h_vlan_TCI); + tuple->encap[i].proto = skb->protocol; ++ break; ++ case htons(ETH_P_PPP_SES): ++ phdr = (struct pppoe_hdr *)skb_mac_header(skb); ++ tuple->encap[i].id = ntohs(phdr->sid); ++ tuple->encap[i].proto = skb->protocol; ++ break; + } + } + + static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev, +- struct flow_offload_tuple *tuple) ++ struct flow_offload_tuple *tuple, u32 *nhoff) + { + unsigned int thoff, hdrsize, offset = 0; + struct flow_ports *ports; + struct iphdr *iph; + +- if (skb->protocol == htons(ETH_P_8021Q)) ++ switch (skb->protocol) { ++ case htons(ETH_P_8021Q): + offset += VLAN_HLEN; ++ break; ++ case htons(ETH_P_PPP_SES): ++ offset += PPPOE_SES_HLEN; ++ break; ++ } + + if (!pskb_may_pull(skb, sizeof(*iph) + offset)) + return -1; +@@ -226,6 +243,7 @@ static int nf_flow_tuple_ip(struct sk_bu + tuple->l4proto = iph->protocol; + tuple->iifidx = dev->ifindex; + nf_flow_tuple_encap(skb, tuple); ++ *nhoff = offset; + + return 0; + } +@@ -270,14 +288,36 @@ static unsigned int nf_flow_xmit_xfrm(st + return NF_STOLEN; + } + ++static inline __be16 nf_flow_pppoe_proto(const struct sk_buff *skb) ++{ ++ __be16 proto; ++ ++ proto = *((__be16 *)(skb_mac_header(skb) + ETH_HLEN + ++ sizeof(struct pppoe_hdr))); ++ switch (proto) { ++ case htons(PPP_IP): ++ return htons(ETH_P_IP); ++ case htons(PPP_IPV6): ++ return htons(ETH_P_IPV6); ++ } ++ ++ return 0; ++} ++ + static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto) + { +- if (skb->protocol == htons(ETH_P_8021Q)) { +- struct vlan_ethhdr *veth; ++ struct vlan_ethhdr *veth; + ++ switch (skb->protocol) { ++ case htons(ETH_P_8021Q): + veth = (struct vlan_ethhdr *)skb_mac_header(skb); + if (veth->h_vlan_encapsulated_proto == proto) + return true; ++ break; ++ case htons(ETH_P_PPP_SES): ++ if (nf_flow_pppoe_proto(skb) == proto) ++ return true; ++ break; + } + + return false; +@@ -294,12 +334,18 @@ static void nf_flow_encap_pop(struct sk_ + __vlan_hwaccel_clear_tag(skb); + continue; + } +- if (skb->protocol == htons(ETH_P_8021Q)) { ++ switch (skb->protocol) { ++ case htons(ETH_P_8021Q): + vlan_hdr = (struct vlan_hdr *)skb->data; + __skb_pull(skb, VLAN_HLEN); + vlan_set_encap_proto(skb, vlan_hdr); + skb_reset_network_header(skb); + break; ++ case htons(ETH_P_PPP_SES): ++ skb->protocol = nf_flow_pppoe_proto(skb); ++ skb_pull(skb, PPPOE_SES_HLEN); ++ skb_reset_network_header(skb); ++ break; + } + } + } +@@ -343,7 +389,7 @@ nf_flow_offload_ip_hook(void *priv, stru + !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IP))) + return NF_ACCEPT; + +- if (nf_flow_tuple_ip(skb, state->in, &tuple) < 0) ++ if (nf_flow_tuple_ip(skb, state->in, &tuple, &offset) < 0) + return NF_ACCEPT; + + tuplehash = flow_offload_lookup(flow_table, &tuple); +@@ -357,9 +403,6 @@ nf_flow_offload_ip_hook(void *priv, stru + if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) + return NF_ACCEPT; + +- if (skb->protocol == htons(ETH_P_8021Q)) +- offset += VLAN_HLEN; +- + if (skb_try_make_writable(skb, sizeof(*iph) + offset)) + return NF_DROP; + +@@ -543,14 +586,20 @@ static int nf_flow_nat_ipv6(const struct + } + + static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev, +- struct flow_offload_tuple *tuple) ++ struct flow_offload_tuple *tuple, u32 *nhoff) + { + unsigned int thoff, hdrsize, offset = 0; + struct flow_ports *ports; + struct ipv6hdr *ip6h; + +- if (skb->protocol == htons(ETH_P_8021Q)) ++ switch (skb->protocol) { ++ case htons(ETH_P_8021Q): + offset += VLAN_HLEN; ++ break; ++ case htons(ETH_P_PPP_SES): ++ offset += PPPOE_SES_HLEN; ++ break; ++ } + + if (!pskb_may_pull(skb, sizeof(*ip6h) + offset)) + return -1; +@@ -586,6 +635,7 @@ static int nf_flow_tuple_ipv6(struct sk_ + tuple->l4proto = ip6h->nexthdr; + tuple->iifidx = dev->ifindex; + nf_flow_tuple_encap(skb, tuple); ++ *nhoff = offset; + + return 0; + } +@@ -611,7 +661,7 @@ nf_flow_offload_ipv6_hook(void *priv, st + !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IPV6))) + return NF_ACCEPT; + +- if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0) ++ if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &offset) < 0) + return NF_ACCEPT; + + tuplehash = flow_offload_lookup(flow_table, &tuple); +@@ -625,9 +675,6 @@ nf_flow_offload_ipv6_hook(void *priv, st + if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) + return NF_ACCEPT; + +- if (skb->protocol == htons(ETH_P_8021Q)) +- offset += VLAN_HLEN; +- + ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset); + if (nf_flow_state_check(flow, ip6h->nexthdr, skb, sizeof(*ip6h))) + return NF_ACCEPT; +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -90,6 +90,7 @@ static void nft_dev_path_info(const stru + switch (path->type) { + case DEV_PATH_ETHERNET: + case DEV_PATH_VLAN: ++ case DEV_PATH_PPPOE: + info->indev = path->dev; + if (is_zero_ether_addr(info->h_source)) + memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); +@@ -97,7 +98,7 @@ static void nft_dev_path_info(const stru + if (path->type == DEV_PATH_ETHERNET) + break; + +- /* DEV_PATH_VLAN */ ++ /* DEV_PATH_VLAN and DEV_PATH_PPPOE */ + if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { + info->indev = NULL; + break; +@@ -106,6 +107,8 @@ static void nft_dev_path_info(const stru + info->encap[info->num_encaps].id = path->encap.id; + info->encap[info->num_encaps].proto = path->encap.proto; + info->num_encaps++; ++ if (path->type == DEV_PATH_PPPOE) ++ memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN); + break; + case DEV_PATH_BRIDGE: + if (is_zero_ether_addr(info->h_source)) diff --git a/target/linux/generic/pending-5.10/640-12-netfilter-nft_flow_offload-add-dsa-support.patch b/target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-dsa-support.patch similarity index 64% rename from target/linux/generic/pending-5.10/640-12-netfilter-nft_flow_offload-add-dsa-support.patch rename to target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-dsa-support.patch index 3689a476b3..7ff5dad1f7 100644 --- a/target/linux/generic/pending-5.10/640-12-netfilter-nft_flow_offload-add-dsa-support.patch +++ b/target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-dsa-support.patch @@ -1,5 +1,5 @@ From: Pablo Neira Ayuso -Date: Mon, 18 Jan 2021 22:27:45 +0100 +Date: Thu, 4 Mar 2021 19:22:55 +0100 Subject: [PATCH] netfilter: nft_flow_offload: add dsa support Replace the master ethernet device by the dsa slave port. @@ -9,15 +9,15 @@ Signed-off-by: Pablo Neira Ayuso --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c -@@ -86,6 +86,7 @@ static void nft_dev_path_info(const stru +@@ -89,6 +89,7 @@ static void nft_dev_path_info(const stru path = &stack->path[i]; switch (path->type) { case DEV_PATH_ETHERNET: + case DEV_PATH_DSA: case DEV_PATH_VLAN: - info->dev = path->dev; - if (is_zero_ether_addr(info->h_source)) -@@ -93,6 +94,10 @@ static void nft_dev_path_info(const stru + case DEV_PATH_PPPOE: + info->indev = path->dev; +@@ -97,6 +98,10 @@ static void nft_dev_path_info(const stru if (path->type == DEV_PATH_ETHERNET) break; @@ -26,5 +26,5 @@ Signed-off-by: Pablo Neira Ayuso + break; + } - /* DEV_PATH_VLAN */ - if (info->num_vlans >= NF_FLOW_TABLE_VLAN_MAX) { + /* DEV_PATH_VLAN and DEV_PATH_PPPOE */ + if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { diff --git a/target/linux/generic/pending-5.10/640-11-netfilter-flowtable-add-offload-support-for-xmit-pat.patch b/target/linux/generic/pending-5.10/640-15-netfilter-flowtable-add-offload-support-for-xmit-pat.patch similarity index 98% rename from target/linux/generic/pending-5.10/640-11-netfilter-flowtable-add-offload-support-for-xmit-pat.patch rename to target/linux/generic/pending-5.10/640-15-netfilter-flowtable-add-offload-support-for-xmit-pat.patch index 508dc90e14..696ea03332 100644 --- a/target/linux/generic/pending-5.10/640-11-netfilter-flowtable-add-offload-support-for-xmit-pat.patch +++ b/target/linux/generic/pending-5.10/640-15-netfilter-flowtable-add-offload-support-for-xmit-pat.patch @@ -229,12 +229,12 @@ tag to the driver. + other_tuple = &flow->tuplehash[!dir].tuple; + -+ for (i = 0; i < other_tuple->in_vlan_num; i++) { ++ for (i = 0; i < other_tuple->encap_num; i++) { + struct flow_action_entry *entry = flow_action_entry_next(flow_rule); + + entry->id = FLOW_ACTION_VLAN_PUSH; -+ entry->vlan.vid = other_tuple->in_vlan[i].id; -+ entry->vlan.proto = other_tuple->in_vlan[i].proto; ++ entry->vlan.vid = other_tuple->encap[i].id; ++ entry->vlan.proto = other_tuple->encap[i].proto; + } + + return 0; diff --git a/target/linux/generic/pending-5.10/640-15-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch b/target/linux/generic/pending-5.10/640-15-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch deleted file mode 100644 index 1c65a5e218..0000000000 --- a/target/linux/generic/pending-5.10/640-15-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch +++ /dev/null @@ -1,51 +0,0 @@ -From: Pablo Neira Ayuso -Date: Tue, 2 Feb 2021 17:10:07 +0100 -Subject: [PATCH] netfilter: nft_flow_offload: use direct xmit if - hardware offload is enabled - -If there is a forward path to reach an ethernet device and hardware -offload is enabled, then use the direct xmit path. ---- - ---- a/net/netfilter/nft_flow_offload.c -+++ b/net/netfilter/nft_flow_offload.c -@@ -73,9 +73,18 @@ struct nft_forward_info { - enum flow_offload_xmit_type xmit_type; - }; - -+static bool nft_is_valid_ether_device(const struct net_device *dev) -+{ -+ if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || -+ dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr)) -+ return false; -+ -+ return true; -+} -+ - static void nft_dev_path_info(const struct net_device_path_stack *stack, - struct nft_forward_info *info, -- unsigned char *ha) -+ unsigned char *ha, struct nf_flowtable *flowtable) - { - const struct net_device_path *path; - int i; -@@ -131,6 +140,10 @@ static void nft_dev_path_info(const stru - break; - } - } -+ -+ if (nf_flowtable_hw_offload(flowtable) && -+ nft_is_valid_ether_device(info->dev)) -+ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; - } - - static bool nft_flowtable_find_dev(const struct net_device *dev, -@@ -162,7 +175,7 @@ static void nft_dev_forward_path(struct - int i; - - if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) -- nft_dev_path_info(&stack, &info, ha); -+ nft_dev_path_info(&stack, &info, ha, &ft->data); - - if (!info.dev || !nft_flowtable_find_dev(info.dev, ft)) - return; diff --git a/target/linux/generic/pending-5.10/640-13-dsa-slave-add-support-for-TC_SETUP_FT.patch b/target/linux/generic/pending-5.10/640-16-dsa-slave-add-support-for-TC_SETUP_FT.patch similarity index 100% rename from target/linux/generic/pending-5.10/640-13-dsa-slave-add-support-for-TC_SETUP_FT.patch rename to target/linux/generic/pending-5.10/640-16-dsa-slave-add-support-for-TC_SETUP_FT.patch diff --git a/target/linux/generic/pending-5.10/640-16-netfilter-nft_flow_offload-fix-bridge-vlan-tag-handl.patch b/target/linux/generic/pending-5.10/640-16-netfilter-nft_flow_offload-fix-bridge-vlan-tag-handl.patch deleted file mode 100644 index 5877e91e78..0000000000 --- a/target/linux/generic/pending-5.10/640-16-netfilter-nft_flow_offload-fix-bridge-vlan-tag-handl.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: Felix Fietkau -Date: Wed, 10 Feb 2021 10:23:38 +0100 -Subject: [PATCH] netfilter: nft_flow_offload: fix bridge vlan tag handling - -the brigde type uses the path->bridge.vlan_* fields - -Signed-off-by: Felix Fietkau ---- - ---- a/net/netfilter/nft_flow_offload.c -+++ b/net/netfilter/nft_flow_offload.c -@@ -123,8 +123,8 @@ static void nft_dev_path_info(const stru - - switch (path->bridge.vlan_mode) { - case DEV_PATH_BR_VLAN_TAG: -- info->vid[info->num_vlans] = path->vlan.id; -- info->vproto[info->num_vlans] = path->vlan.proto; -+ info->vid[info->num_vlans] = path->bridge.vlan_id; -+ info->vproto[info->num_vlans] = path->bridge.vlan_proto; - info->num_vlans++; - break; - case DEV_PATH_BR_VLAN_UNTAG: diff --git a/target/linux/generic/pending-5.10/640-17-netfilter-flowtable-rework-ingress-vlan-matching.patch b/target/linux/generic/pending-5.10/640-17-netfilter-flowtable-rework-ingress-vlan-matching.patch deleted file mode 100644 index 67ec263f51..0000000000 --- a/target/linux/generic/pending-5.10/640-17-netfilter-flowtable-rework-ingress-vlan-matching.patch +++ /dev/null @@ -1,143 +0,0 @@ -From: Felix Fietkau -Date: Wed, 10 Feb 2021 19:39:23 +0100 -Subject: [PATCH] netfilter: flowtable: rework ingress vlan matching - -When dealing with bridges with VLAN filtering and DSA/switchdev offload, -the hardware could offload adding a VLAN tag configured in the bridge. -Since there doesn't seem to be an easy way to detect that, this patch -reworks the code to optionally match the last VLAN tag that would otherwise -be inserted by the bridge. -This matters when bypassing the bridge and attaching an ingress hook on -a DSA port below it. - -Signed-off-by: Felix Fietkau ---- - ---- a/include/net/netfilter/nf_flow_table.h -+++ b/include/net/netfilter/nf_flow_table.h -@@ -115,14 +115,15 @@ struct flow_offload_tuple { - - u8 l3proto; - u8 l4proto; -- struct { -- u16 id; -- __be16 proto; -- } in_vlan[NF_FLOW_TABLE_VLAN_MAX]; - - /* All members above are keys for lookups, see flow_offload_hash(). */ - struct { } __hash; - -+ struct { -+ u16 id; -+ __be16 proto; -+ } in_vlan[NF_FLOW_TABLE_VLAN_MAX], in_pvid; -+ - u8 dir:4, - xmit_type:2, - in_vlan_num:2; ---- a/net/netfilter/nf_flow_table_ip.c -+++ b/net/netfilter/nf_flow_table_ip.c -@@ -281,12 +281,13 @@ static bool nf_flow_skb_vlan_protocol(co - } - - static void nf_flow_vlan_pop(struct sk_buff *skb, -- struct flow_offload_tuple_rhash *tuplehash) -+ struct flow_offload_tuple_rhash *tuplehash, -+ bool strip_pvid) - { - struct vlan_hdr *vlan_hdr; - int i; - -- for (i = 0; i < tuplehash->tuple.in_vlan_num; i++) { -+ for (i = 0; i < tuplehash->tuple.in_vlan_num + strip_pvid; i++) { - if (skb_vlan_tag_present(skb)) { - __vlan_hwaccel_clear_tag(skb); - continue; -@@ -316,6 +317,31 @@ static unsigned int nf_flow_queue_xmit(s - return NF_STOLEN; - } - -+static bool -+nf_flow_offload_check_vlan(struct flow_offload_tuple *tuple, -+ struct flow_offload_tuple *flow_tuple, -+ bool *strip_pvid) -+{ -+ int i, cur = 0; -+ -+ if (flow_tuple->in_pvid.proto && -+ !memcmp(&tuple->in_vlan[0], &flow_tuple->in_pvid, -+ sizeof(tuple->in_vlan[0]))) -+ cur++; -+ -+ *strip_pvid = cur; -+ -+ for (i = 0; i < flow_tuple->in_vlan_num; i++, cur++) { -+ if (!memcmp(&tuple->in_vlan[cur], &flow_tuple->in_vlan[i], -+ sizeof(tuple->in_vlan[0]))) -+ continue; -+ -+ return false; -+ } -+ -+ return true; -+} -+ - unsigned int - nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, - const struct nf_hook_state *state) -@@ -329,6 +355,7 @@ nf_flow_offload_ip_hook(void *priv, stru - struct rtable *rt; - unsigned int thoff; - struct iphdr *iph; -+ bool strip_pvid; - __be32 nexthop; - u32 offset = 0; - int ret; -@@ -344,6 +371,10 @@ nf_flow_offload_ip_hook(void *priv, stru - if (tuplehash == NULL) - return NF_ACCEPT; - -+ if (!nf_flow_offload_check_vlan(&tuple, &tuplehash->tuple, -+ &strip_pvid)) -+ return NF_ACCEPT; -+ - dir = tuplehash->tuple.dir; - flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); - -@@ -368,7 +399,7 @@ nf_flow_offload_ip_hook(void *priv, stru - return NF_ACCEPT; - } - -- nf_flow_vlan_pop(skb, tuplehash); -+ nf_flow_vlan_pop(skb, tuplehash, strip_pvid); - thoff -= offset; - - if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0) -@@ -596,6 +627,7 @@ nf_flow_offload_ipv6_hook(void *priv, st - struct net_device *outdev; - struct ipv6hdr *ip6h; - struct rt6_info *rt; -+ bool strip_pvid; - u32 offset = 0; - int ret; - -@@ -610,6 +642,10 @@ nf_flow_offload_ipv6_hook(void *priv, st - if (tuplehash == NULL) - return NF_ACCEPT; - -+ if (!nf_flow_offload_check_vlan(&tuple, &tuplehash->tuple, -+ &strip_pvid)) -+ return NF_ACCEPT; -+ - dir = tuplehash->tuple.dir; - flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); - -@@ -630,7 +666,7 @@ nf_flow_offload_ipv6_hook(void *priv, st - return NF_ACCEPT; - } - -- nf_flow_vlan_pop(skb, tuplehash); -+ nf_flow_vlan_pop(skb, tuplehash, strip_pvid); - - if (skb_try_make_writable(skb, sizeof(*ip6h))) - return NF_DROP; diff --git a/target/linux/generic/pending-5.10/640-17-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch b/target/linux/generic/pending-5.10/640-17-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch new file mode 100644 index 0000000000..26d172bcf7 --- /dev/null +++ b/target/linux/generic/pending-5.10/640-17-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch @@ -0,0 +1,108 @@ +From: Pablo Neira Ayuso +Date: Thu, 4 Mar 2021 19:24:11 +0100 +Subject: [PATCH] netfilter: nft_flow_offload: use direct xmit if + hardware offload is enabled + +If there is a forward path to reach an ethernet device and hardware +offload is enabled, then use the direct xmit path. +--- + +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -131,6 +131,7 @@ struct flow_offload_tuple { + struct dst_entry *dst_cache; + struct { + u32 ifidx; ++ u32 hw_ifidx; + u8 h_source[ETH_ALEN]; + u8 h_dest[ETH_ALEN]; + } out; +@@ -188,6 +189,7 @@ struct nf_flow_route { + } in; + struct { + u32 ifindex; ++ u32 hw_ifindex; + u8 h_source[ETH_ALEN]; + u8 h_dest[ETH_ALEN]; + } out; +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -106,6 +106,7 @@ static int flow_offload_fill_route(struc + memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source, + ETH_ALEN); + flow_tuple->out.ifidx = route->tuple[dir].out.ifindex; ++ flow_tuple->out.hw_ifidx = route->tuple[dir].out.hw_ifindex; + break; + case FLOW_OFFLOAD_XMIT_XFRM: + case FLOW_OFFLOAD_XMIT_NEIGH: +--- a/net/netfilter/nf_flow_table_offload.c ++++ b/net/netfilter/nf_flow_table_offload.c +@@ -506,7 +506,7 @@ static void flow_offload_redirect(struct + switch (this_tuple->xmit_type) { + case FLOW_OFFLOAD_XMIT_DIRECT: + this_tuple = &flow->tuplehash[dir].tuple; +- ifindex = this_tuple->out.ifidx; ++ ifindex = this_tuple->out.hw_ifidx; + break; + case FLOW_OFFLOAD_XMIT_NEIGH: + other_tuple = &flow->tuplehash[!dir].tuple; +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -66,6 +66,7 @@ static int nft_dev_fill_forward_path(con + struct nft_forward_info { + const struct net_device *indev; + const struct net_device *outdev; ++ const struct net_device *hw_outdev; + struct id { + __u16 id; + __be16 proto; +@@ -76,9 +77,18 @@ struct nft_forward_info { + enum flow_offload_xmit_type xmit_type; + }; + ++static bool nft_is_valid_ether_device(const struct net_device *dev) ++{ ++ if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || ++ dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr)) ++ return false; ++ ++ return true; ++} ++ + static void nft_dev_path_info(const struct net_device_path_stack *stack, + struct nft_forward_info *info, +- unsigned char *ha) ++ unsigned char *ha, struct nf_flowtable *flowtable) + { + const struct net_device_path *path; + int i; +@@ -140,6 +150,12 @@ static void nft_dev_path_info(const stru + } + if (!info->outdev) + info->outdev = info->indev; ++ ++ info->hw_outdev = info->indev; ++ ++ if (nf_flowtable_hw_offload(flowtable) && ++ nft_is_valid_ether_device(info->indev)) ++ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; + } + + static bool nft_flowtable_find_dev(const struct net_device *dev, +@@ -171,7 +187,7 @@ static void nft_dev_forward_path(struct + int i; + + if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) +- nft_dev_path_info(&stack, &info, ha); ++ nft_dev_path_info(&stack, &info, ha, &ft->data); + + if (!info.indev || !nft_flowtable_find_dev(info.indev, ft)) + return; +@@ -187,6 +203,7 @@ static void nft_dev_forward_path(struct + memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); + memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); + route->tuple[dir].out.ifindex = info.outdev->ifindex; ++ route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex; + route->tuple[dir].xmit_type = info.xmit_type; + } + } diff --git a/target/linux/generic/pending-5.10/640-18-netfilter-flowtable-handle-bridge-vlan-filter-offloa.patch b/target/linux/generic/pending-5.10/640-18-netfilter-flowtable-handle-bridge-vlan-filter-offloa.patch deleted file mode 100644 index ba480f3520..0000000000 --- a/target/linux/generic/pending-5.10/640-18-netfilter-flowtable-handle-bridge-vlan-filter-offloa.patch +++ /dev/null @@ -1,106 +0,0 @@ -From: Felix Fietkau -Date: Wed, 10 Feb 2021 19:44:33 +0100 -Subject: [PATCH] netfilter: flowtable: handle bridge vlan filter offload - tags from DSA/switchdev - -When a switchdev/DSA port is an untagged member of a bridge vlan, ingress -packets could potentially be tagged with the id of the VLAN. -When the VLAN port group has been uploaded to switchdev, report the bridge -tag mode as DEV_PATH_BR_VLAN_UNTAG_HW instead of DEV_PATH_BR_VLAN_UNTAG -and handle it in netfilter flow offloading by storing the tag in the tuple -in_pvid field. -This allows the ingress hook to detect the optional tag and remove it for -software offload. This tag processing is for ingress only, egress needs to be -fully untagged - -Signed-off-by: Felix Fietkau ---- - ---- a/include/linux/netdevice.h -+++ b/include/linux/netdevice.h -@@ -853,6 +853,7 @@ struct net_device_path { - DEV_PATH_BR_VLAN_KEEP, - DEV_PATH_BR_VLAN_TAG, - DEV_PATH_BR_VLAN_UNTAG, -+ DEV_PATH_BR_VLAN_UNTAG_HW, - } vlan_mode; - u16 vlan_id; - __be16 vlan_proto; ---- a/include/net/netfilter/nf_flow_table.h -+++ b/include/net/netfilter/nf_flow_table.h -@@ -183,6 +183,10 @@ struct nf_flow_route { - u32 ifindex; - u16 vid[NF_FLOW_TABLE_VLAN_MAX]; - __be16 vproto[NF_FLOW_TABLE_VLAN_MAX]; -+ struct { -+ u16 id; -+ __be16 proto; -+ } pvid; - u8 num_vlans; - } in; - struct { ---- a/net/bridge/br_device.c -+++ b/net/bridge/br_device.c -@@ -435,6 +435,7 @@ static int br_fill_forward_path(struct n - ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto; - ctx->num_vlans++; - break; -+ case DEV_PATH_BR_VLAN_UNTAG_HW: - case DEV_PATH_BR_VLAN_UNTAG: - ctx->num_vlans--; - break; ---- a/net/bridge/br_vlan.c -+++ b/net/bridge/br_vlan.c -@@ -1374,6 +1374,8 @@ int br_vlan_fill_forward_path_mode(struc - - if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG) - path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP; -+ else if (v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV) -+ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG_HW; - else - path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG; - ---- a/net/netfilter/nf_flow_table_core.c -+++ b/net/netfilter/nf_flow_table_core.c -@@ -98,6 +98,8 @@ static int flow_offload_fill_route(struc - j++; - } - flow_tuple->in_vlan_num = route->tuple[dir].in.num_vlans; -+ flow_tuple->in_pvid.id = route->tuple[dir].in.pvid.id; -+ flow_tuple->in_pvid.proto = route->tuple[dir].in.pvid.proto; - - switch (route->tuple[dir].xmit_type) { - case FLOW_OFFLOAD_XMIT_DIRECT: ---- a/net/netfilter/nft_flow_offload.c -+++ b/net/netfilter/nft_flow_offload.c -@@ -67,6 +67,10 @@ struct nft_forward_info { - const struct net_device *dev; - __u16 vid[NF_FLOW_TABLE_VLAN_MAX]; - __be16 vproto[NF_FLOW_TABLE_VLAN_MAX]; -+ struct { -+ __u16 id; -+ __be16 proto; -+ } pvid; - u8 num_vlans; - u8 h_source[ETH_ALEN]; - u8 h_dest[ETH_ALEN]; -@@ -127,6 +131,10 @@ static void nft_dev_path_info(const stru - info->vproto[info->num_vlans] = path->bridge.vlan_proto; - info->num_vlans++; - break; -+ case DEV_PATH_BR_VLAN_UNTAG_HW: -+ info->pvid.id = info->vid[info->num_vlans - 1]; -+ info->pvid.proto = info->vproto[info->num_vlans - 1]; -+ fallthrough; - case DEV_PATH_BR_VLAN_UNTAG: - info->num_vlans--; - break; -@@ -185,6 +193,8 @@ static void nft_dev_forward_path(struct - route->tuple[!dir].in.vid[i] = info.vid[i]; - route->tuple[!dir].in.vproto[i] = info.vproto[i]; - } -+ route->tuple[!dir].in.pvid.id = info.pvid.id; -+ route->tuple[!dir].in.pvid.proto = info.pvid.proto; - route->tuple[!dir].in.num_vlans = info.num_vlans; - - if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { diff --git a/target/linux/generic/pending-5.10/640-18-netfilter-nf_flow_table-fix-untagging-with-hardware-.patch b/target/linux/generic/pending-5.10/640-18-netfilter-nf_flow_table-fix-untagging-with-hardware-.patch new file mode 100644 index 0000000000..33600a862e --- /dev/null +++ b/target/linux/generic/pending-5.10/640-18-netfilter-nf_flow_table-fix-untagging-with-hardware-.patch @@ -0,0 +1,122 @@ +From: Felix Fietkau +Date: Mon, 8 Mar 2021 12:06:44 +0100 +Subject: [PATCH] netfilter: nf_flow_table: fix untagging with + hardware-offloaded bridge vlan_filtering + +When switchdev offloading is enabled, treat an untagged VLAN as tagged for +ingress only + +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -855,6 +855,7 @@ struct net_device_path { + DEV_PATH_BR_VLAN_KEEP, + DEV_PATH_BR_VLAN_TAG, + DEV_PATH_BR_VLAN_UNTAG, ++ DEV_PATH_BR_VLAN_UNTAG_HW, + } vlan_mode; + u16 vlan_id; + __be16 vlan_proto; +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -123,9 +123,10 @@ struct flow_offload_tuple { + /* All members above are keys for lookups, see flow_offload_hash(). */ + struct { } __hash; + +- u8 dir:4, ++ u8 dir:2, + xmit_type:2, +- encap_num:2; ++ encap_num:2, ++ in_vlan_ingress:2; + u16 mtu; + union { + struct dst_entry *dst_cache; +@@ -185,7 +186,8 @@ struct nf_flow_route { + u16 id; + __be16 proto; + } encap[NF_FLOW_TABLE_ENCAP_MAX]; +- u8 num_encaps; ++ u8 num_encaps:2, ++ ingress_vlans:2; + } in; + struct { + u32 ifindex; +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -435,6 +435,7 @@ static int br_fill_forward_path(struct n + ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto; + ctx->num_vlans++; + break; ++ case DEV_PATH_BR_VLAN_UNTAG_HW: + case DEV_PATH_BR_VLAN_UNTAG: + ctx->num_vlans--; + break; +--- a/net/bridge/br_vlan.c ++++ b/net/bridge/br_vlan.c +@@ -1374,6 +1374,8 @@ int br_vlan_fill_forward_path_mode(struc + + if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG) + path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP; ++ else if (v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV) ++ path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG_HW; + else + path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG; + +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -95,6 +95,8 @@ static int flow_offload_fill_route(struc + for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) { + flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id; + flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto; ++ if (route->tuple[dir].in.ingress_vlans & BIT(i)) ++ flow_tuple->in_vlan_ingress |= BIT(j); + j++; + } + flow_tuple->encap_num = route->tuple[dir].in.num_encaps; +--- a/net/netfilter/nf_flow_table_offload.c ++++ b/net/netfilter/nf_flow_table_offload.c +@@ -592,8 +592,12 @@ nf_flow_rule_route_common(struct net *ne + other_tuple = &flow->tuplehash[!dir].tuple; + + for (i = 0; i < other_tuple->encap_num; i++) { +- struct flow_action_entry *entry = flow_action_entry_next(flow_rule); ++ struct flow_action_entry *entry; + ++ if (other_tuple->in_vlan_ingress & BIT(i)) ++ continue; ++ ++ entry = flow_action_entry_next(flow_rule); + entry->id = FLOW_ACTION_VLAN_PUSH; + entry->vlan.vid = other_tuple->encap[i].id; + entry->vlan.proto = other_tuple->encap[i].proto; +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -72,6 +72,7 @@ struct nft_forward_info { + __be16 proto; + } encap[NF_FLOW_TABLE_ENCAP_MAX]; + u8 num_encaps; ++ u8 ingress_vlans; + u8 h_source[ETH_ALEN]; + u8 h_dest[ETH_ALEN]; + enum flow_offload_xmit_type xmit_type; +@@ -130,6 +131,9 @@ static void nft_dev_path_info(const stru + memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); + + switch (path->bridge.vlan_mode) { ++ case DEV_PATH_BR_VLAN_UNTAG_HW: ++ info->ingress_vlans |= BIT(info->num_encaps - 1); ++ break; + case DEV_PATH_BR_VLAN_TAG: + info->encap[info->num_encaps].id = path->bridge.vlan_id; + info->encap[info->num_encaps].proto = path->bridge.vlan_proto; +@@ -198,6 +202,7 @@ static void nft_dev_forward_path(struct + route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; + } + route->tuple[!dir].in.num_encaps = info.num_encaps; ++ route->tuple[!dir].in.ingress_vlans = info.ingress_vlans; + + if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { + memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); diff --git a/target/linux/generic/pending-5.10/640-19-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch b/target/linux/generic/pending-5.10/640-19-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch new file mode 100644 index 0000000000..3eaad27cdf --- /dev/null +++ b/target/linux/generic/pending-5.10/640-19-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch @@ -0,0 +1,26 @@ +From: Pablo Neira Ayuso +Date: Tue, 2 Mar 2021 00:51:31 +0100 +Subject: [PATCH] net: flow_offload: add FLOW_ACTION_PPPOE_PUSH + +--- + +--- a/include/net/flow_offload.h ++++ b/include/net/flow_offload.h +@@ -147,6 +147,7 @@ enum flow_action_id { + FLOW_ACTION_MPLS_POP, + FLOW_ACTION_MPLS_MANGLE, + FLOW_ACTION_GATE, ++ FLOW_ACTION_PPPOE_PUSH, + NUM_FLOW_ACTIONS, + }; + +@@ -271,6 +272,9 @@ struct flow_action_entry { + u32 num_entries; + struct action_gate_entry *entries; + } gate; ++ struct { /* FLOW_ACTION_PPPOE_PUSH */ ++ u16 sid; ++ } pppoe; + }; + struct flow_action_cookie *cookie; /* user defined action cookie */ + }; diff --git a/target/linux/generic/pending-5.10/640-20-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch b/target/linux/generic/pending-5.10/640-20-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch new file mode 100644 index 0000000000..fcafff1fff --- /dev/null +++ b/target/linux/generic/pending-5.10/640-20-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch @@ -0,0 +1,31 @@ +From: Pablo Neira Ayuso +Date: Tue, 2 Mar 2021 01:01:50 +0100 +Subject: [PATCH] netfilter: flowtable: support for + FLOW_ACTION_PPPOE_PUSH + +--- + +--- a/net/netfilter/nf_flow_table_offload.c ++++ b/net/netfilter/nf_flow_table_offload.c +@@ -598,9 +598,18 @@ nf_flow_rule_route_common(struct net *ne + continue; + + entry = flow_action_entry_next(flow_rule); +- entry->id = FLOW_ACTION_VLAN_PUSH; +- entry->vlan.vid = other_tuple->encap[i].id; +- entry->vlan.proto = other_tuple->encap[i].proto; ++ ++ switch (other_tuple->encap[i].proto) { ++ case htons(ETH_P_PPP_SES): ++ entry->id = FLOW_ACTION_PPPOE_PUSH; ++ entry->pppoe.sid = other_tuple->encap[i].id; ++ break; ++ case htons(ETH_P_8021Q): ++ entry->id = FLOW_ACTION_VLAN_PUSH; ++ entry->vlan.vid = other_tuple->encap[i].id; ++ entry->vlan.proto = other_tuple->encap[i].proto; ++ break; ++ } + } + + return 0; diff --git a/target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch index 6d0c84c494..0422221468 100644 --- a/target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch +++ b/target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -2031,6 +2031,8 @@ struct net_device { +@@ -2033,6 +2033,8 @@ struct net_device { struct netdev_hw_addr_list mc; struct netdev_hw_addr_list dev_addrs; diff --git a/target/linux/generic/pending-5.10/690-net-add-support-for-threaded-NAPI-polling.patch b/target/linux/generic/pending-5.10/690-net-add-support-for-threaded-NAPI-polling.patch index b2c2d00ec0..9b1bd6e976 100644 --- a/target/linux/generic/pending-5.10/690-net-add-support-for-threaded-NAPI-polling.patch +++ b/target/linux/generic/pending-5.10/690-net-add-support-for-threaded-NAPI-polling.patch @@ -50,7 +50,15 @@ Signed-off-by: Felix Fietkau }; enum gro_result { -@@ -2411,6 +2414,26 @@ void netif_napi_add(struct net_device *d +@@ -2211,6 +2214,7 @@ struct net_device { + struct lock_class_key *qdisc_running_key; + bool proto_down; + unsigned wol_enabled:1; ++ unsigned threaded:1; + + struct list_head net_notifier_list; + +@@ -2413,6 +2417,26 @@ void netif_napi_add(struct net_device *d int (*poll)(struct napi_struct *, int), int weight); /** @@ -111,7 +119,7 @@ Signed-off-by: Felix Fietkau ____napi_schedule(this_cpu_ptr(&softnet_data), n); } EXPORT_SYMBOL(__napi_schedule_irqoff); -@@ -6715,6 +6726,86 @@ static void init_gro_hash(struct napi_st +@@ -6715,12 +6726,94 @@ static void init_gro_hash(struct napi_st napi->gro_bitmask = 0; } @@ -198,7 +206,15 @@ Signed-off-by: Felix Fietkau void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight) { -@@ -6738,6 +6829,7 @@ void netif_napi_add(struct net_device *d + if (WARN_ON(test_and_set_bit(NAPI_STATE_LISTED, &napi->state))) + return; + ++ if (dev->threaded) ++ set_bit(NAPI_STATE_THREADED, &napi->state); + INIT_LIST_HEAD(&napi->poll_list); + INIT_HLIST_NODE(&napi->napi_hash_node); + hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); +@@ -6738,6 +6831,7 @@ void netif_napi_add(struct net_device *d #ifdef CONFIG_NETPOLL napi->poll_owner = -1; #endif @@ -206,7 +222,7 @@ Signed-off-by: Felix Fietkau set_bit(NAPI_STATE_SCHED, &napi->state); set_bit(NAPI_STATE_NPSVC, &napi->state); list_add_rcu(&napi->dev_list, &dev->napi_list); -@@ -6780,6 +6872,7 @@ void __netif_napi_del(struct napi_struct +@@ -6780,6 +6874,7 @@ void __netif_napi_del(struct napi_struct if (!test_and_clear_bit(NAPI_STATE_LISTED, &napi->state)) return; @@ -214,7 +230,7 @@ Signed-off-by: Felix Fietkau napi_hash_del(napi); list_del_rcu(&napi->dev_list); napi_free_frags(napi); -@@ -6791,53 +6884,19 @@ EXPORT_SYMBOL(__netif_napi_del); +@@ -6791,53 +6886,19 @@ EXPORT_SYMBOL(__netif_napi_del); static int napi_poll(struct napi_struct *n, struct list_head *repoll) { @@ -272,7 +288,7 @@ Signed-off-by: Felix Fietkau /* Some drivers may have called napi_schedule * prior to exhausting their budget. */ -@@ -11291,6 +11350,10 @@ static int __init net_dev_init(void) +@@ -11333,6 +11394,10 @@ static int __init net_dev_init(void) sd->backlog.weight = weight_p; } diff --git a/target/linux/generic/pending-5.10/770-16-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch b/target/linux/generic/pending-5.10/770-16-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch index fa9cbcafc8..2f1ae6cde6 100644 --- a/target/linux/generic/pending-5.10/770-16-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch +++ b/target/linux/generic/pending-5.10/770-16-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch @@ -80,7 +80,7 @@ Signed-off-by: Pablo Neira Ayuso #endif /* MTK_ETH_H */ --- /dev/null +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c -@@ -0,0 +1,478 @@ +@@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Felix Fietkau @@ -113,6 +113,10 @@ Signed-off-by: Pablo Neira Ayuso + __be16 proto; + u8 num; + } vlan; ++ struct { ++ u16 sid; ++ u8 num; ++ } pppoe; +}; + +struct mtk_flow_entry { @@ -311,13 +315,20 @@ Signed-off-by: Pablo Neira Ayuso + break; + case FLOW_ACTION_VLAN_PUSH: + if (data.vlan.num == 1 || -+ data.vlan.proto != ETH_P_8021Q) ++ act->vlan.proto != htons(ETH_P_8021Q)) + return -EOPNOTSUPP; + + data.vlan.id = act->vlan.vid; + data.vlan.proto = act->vlan.proto; + data.vlan.num++; + break; ++ case FLOW_ACTION_PPPOE_PUSH: ++ if (data.pppoe.num == 1) ++ return -EOPNOTSUPP; ++ ++ data.pppoe.sid = act->pppoe.sid; ++ data.pppoe.num++; ++ break; + default: + return -EOPNOTSUPP; + } @@ -392,11 +403,13 @@ Signed-off-by: Pablo Neira Ayuso + } + + if (data.vlan.num == 1) { -+ if (data.vlan.proto != ETH_P_8021Q) ++ if (data.vlan.proto != htons(ETH_P_8021Q)) + return -EOPNOTSUPP; + + mtk_foe_entry_set_vlan(&foe, data.vlan.id); + } ++ if (data.pppoe.num == 1) ++ mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid); + + err = mtk_flow_set_output_device(eth, &foe, odev); + if (err) diff --git a/target/linux/generic/pending-5.4/306-mips_mem_functions_performance.patch b/target/linux/generic/pending-5.4/306-mips_mem_functions_performance.patch index 4e6b7b5cd3..611aa0314f 100644 --- a/target/linux/generic/pending-5.4/306-mips_mem_functions_performance.patch +++ b/target/linux/generic/pending-5.4/306-mips_mem_functions_performance.patch @@ -25,7 +25,7 @@ Signed-off-by: Felix Fietkau -DBOOT_HEAP_SIZE=$(BOOT_HEAP_SIZE) \ --- a/arch/mips/include/asm/string.h +++ b/arch/mips/include/asm/string.h -@@ -140,4 +140,42 @@ extern void *memcpy(void *__to, __const_ +@@ -19,4 +19,42 @@ extern void *memcpy(void *__to, __const_ #define __HAVE_ARCH_MEMMOVE extern void *memmove(void *__dest, __const__ void *__src, size_t __n); diff --git a/target/linux/generic/pending-5.4/405-0001-mtd-core-add-nvmem-cells-compatible-to-parse-mtd-as-.patch b/target/linux/generic/pending-5.4/405-0001-mtd-core-add-nvmem-cells-compatible-to-parse-mtd-as-.patch new file mode 100644 index 0000000000..25801e4686 --- /dev/null +++ b/target/linux/generic/pending-5.4/405-0001-mtd-core-add-nvmem-cells-compatible-to-parse-mtd-as-.patch @@ -0,0 +1,38 @@ +From a5d83d6e2bc747b13f347962d4b335d70b23559b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 12 Mar 2021 07:28:19 +0100 +Subject: [PATCH] mtd: core: add nvmem-cells compatible to parse mtd as nvmem + cells +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Partitions that contains the nvmem-cells compatible will register +their direct subonodes as nvmem cells and the node will be treated as a +nvmem provider. + +Signed-off-by: Ansuel Smith +Tested-by: Rafał Miłecki +--- + drivers/mtd/mtdcore.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -559,6 +559,7 @@ static int mtd_nvmem_reg_read(void *priv + + static int mtd_nvmem_add(struct mtd_info *mtd) + { ++ struct device_node *node = mtd_get_of_node(mtd); + struct nvmem_config config = {}; + + config.id = -1; +@@ -571,7 +572,7 @@ static int mtd_nvmem_add(struct mtd_info + config.stride = 1; + config.read_only = true; + config.root_only = true; +- config.no_of_node = true; ++ config.no_of_node = !of_device_is_compatible(node, "nvmem-cells"); + config.priv = mtd; + + mtd->nvmem = nvmem_register(&config); diff --git a/target/linux/generic/pending-5.4/405-0002-devicetree-nvmem-nvmem-drop-nodename-restriction.patch b/target/linux/generic/pending-5.4/405-0002-devicetree-nvmem-nvmem-drop-nodename-restriction.patch new file mode 100644 index 0000000000..14ea3f6b8c --- /dev/null +++ b/target/linux/generic/pending-5.4/405-0002-devicetree-nvmem-nvmem-drop-nodename-restriction.patch @@ -0,0 +1,25 @@ +From 42645976c3289b03a12f1bd2bc131fd98fc27170 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 12 Mar 2021 07:28:20 +0100 +Subject: [PATCH] devicetree: nvmem: nvmem: drop $nodename restriction + +Drop $nodename restriction as now mtd partition can also be used as +nvmem provider. + +Signed-off-by: Ansuel Smith +--- + Documentation/devicetree/bindings/nvmem/nvmem.yaml | 3 --- + 1 file changed, 3 deletions(-) + +--- a/Documentation/devicetree/bindings/nvmem/nvmem.yaml ++++ b/Documentation/devicetree/bindings/nvmem/nvmem.yaml +@@ -20,9 +20,6 @@ description: | + storage device. + + properties: +- $nodename: +- pattern: "^(eeprom|efuse|nvram)(@.*|-[0-9a-f])*$" +- + "#address-cells": + const: 1 + diff --git a/target/linux/generic/pending-5.4/405-0003-dt-bindings-mtd-Document-use-of-nvmem-cells-compatib.patch b/target/linux/generic/pending-5.4/405-0003-dt-bindings-mtd-Document-use-of-nvmem-cells-compatib.patch new file mode 100644 index 0000000000..0eb4c637cf --- /dev/null +++ b/target/linux/generic/pending-5.4/405-0003-dt-bindings-mtd-Document-use-of-nvmem-cells-compatib.patch @@ -0,0 +1,117 @@ +From 377aa0135dc8489312edd3184d143ce3a89ff7ee Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 12 Mar 2021 07:28:21 +0100 +Subject: [PATCH] dt-bindings: mtd: Document use of nvmem-cells compatible + +Document nvmem-cells compatible used to treat mtd partitions as a +nvmem provider. + +Signed-off-by: Ansuel Smith +Reviewed-by: Rob Herring +--- + .../bindings/mtd/partitions/nvmem-cells.yaml | 99 +++++++++++++++++++ + 1 file changed, 99 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/partitions/nvmem-cells.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/partitions/nvmem-cells.yaml +@@ -0,0 +1,99 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/partitions/nvmem-cells.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Nvmem cells ++ ++description: | ++ Any partition containing the compatible "nvmem-cells" will register as a ++ nvmem provider. ++ Each direct subnodes represents a nvmem cell following the nvmem binding. ++ Nvmem binding to declare nvmem-cells can be found in: ++ Documentation/devicetree/bindings/nvmem/nvmem.yaml ++ ++maintainers: ++ - Ansuel Smith ++ ++allOf: ++ - $ref: /schemas/nvmem/nvmem.yaml# ++ ++properties: ++ compatible: ++ const: nvmem-cells ++ ++required: ++ - compatible ++ ++additionalProperties: true ++ ++examples: ++ - | ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ /* ... */ ++ ++ }; ++ art: art@1200000 { ++ compatible = "nvmem-cells"; ++ reg = <0x1200000 0x0140000>; ++ label = "art"; ++ read-only; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ macaddr_gmac1: macaddr_gmac1@0 { ++ reg = <0x0 0x6>; ++ }; ++ ++ macaddr_gmac2: macaddr_gmac2@6 { ++ reg = <0x6 0x6>; ++ }; ++ ++ pre_cal_24g: pre_cal_24g@1000 { ++ reg = <0x1000 0x2f20>; ++ }; ++ ++ pre_cal_5g: pre_cal_5g@5000{ ++ reg = <0x5000 0x2f20>; ++ }; ++ }; ++ - | ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "bootloader"; ++ reg = <0x000000 0x100000>; ++ read-only; ++ }; ++ ++ firmware@100000 { ++ compatible = "brcm,trx"; ++ label = "firmware"; ++ reg = <0x100000 0xe00000>; ++ }; ++ ++ calibration@f00000 { ++ compatible = "nvmem-cells"; ++ label = "calibration"; ++ reg = <0xf00000 0x100000>; ++ ranges = <0 0xf00000 0x100000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ wifi0@0 { ++ reg = <0x000000 0x080000>; ++ }; ++ ++ wifi1@80000 { ++ reg = <0x080000 0x080000>; ++ }; ++ }; ++ }; diff --git a/target/linux/generic/pending-5.4/480-mtd-set-rootfs-to-be-root-dev.patch b/target/linux/generic/pending-5.4/480-mtd-set-rootfs-to-be-root-dev.patch index 3907ac2368..95863d6edb 100644 --- a/target/linux/generic/pending-5.4/480-mtd-set-rootfs-to-be-root-dev.patch +++ b/target/linux/generic/pending-5.4/480-mtd-set-rootfs-to-be-root-dev.patch @@ -20,7 +20,7 @@ Signed-off-by: Gabor Juhos #include #include -@@ -698,6 +699,15 @@ int add_mtd_device(struct mtd_info *mtd) +@@ -699,6 +700,15 @@ int add_mtd_device(struct mtd_info *mtd) of this try_ nonsense, and no bitching about it either. :) */ __module_get(THIS_MODULE); diff --git a/target/linux/generic/pending-5.4/495-mtd-core-add-get_mtd_device_by_node.patch b/target/linux/generic/pending-5.4/495-mtd-core-add-get_mtd_device_by_node.patch index 3a118e38fe..b6e36410e7 100644 --- a/target/linux/generic/pending-5.4/495-mtd-core-add-get_mtd_device_by_node.patch +++ b/target/linux/generic/pending-5.4/495-mtd-core-add-get_mtd_device_by_node.patch @@ -17,7 +17,7 @@ Reviewed-by: Miquel Raynal --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c -@@ -1049,6 +1049,44 @@ out_unlock: +@@ -1050,6 +1050,44 @@ out_unlock: } EXPORT_SYMBOL_GPL(get_mtd_device_nm); diff --git a/target/linux/generic/pending-5.4/690-net-add-support-for-threaded-NAPI-polling.patch b/target/linux/generic/pending-5.4/690-net-add-support-for-threaded-NAPI-polling.patch index 6d24007b6a..6392b9d756 100644 --- a/target/linux/generic/pending-5.4/690-net-add-support-for-threaded-NAPI-polling.patch +++ b/target/linux/generic/pending-5.4/690-net-add-support-for-threaded-NAPI-polling.patch @@ -50,7 +50,15 @@ Signed-off-by: Felix Fietkau }; enum gro_result { -@@ -2249,6 +2252,26 @@ void netif_napi_add(struct net_device *d +@@ -2092,6 +2095,7 @@ struct net_device { + struct lock_class_key addr_list_lock_key; + bool proto_down; + unsigned wol_enabled:1; ++ unsigned threaded:1; + }; + #define to_net_dev(d) container_of(d, struct net_device, dev) + +@@ -2249,6 +2253,26 @@ void netif_napi_add(struct net_device *d int (*poll)(struct napi_struct *, int), int weight); /** @@ -111,7 +119,7 @@ Signed-off-by: Felix Fietkau ____napi_schedule(this_cpu_ptr(&softnet_data), n); } EXPORT_SYMBOL(__napi_schedule_irqoff); -@@ -6219,6 +6230,84 @@ static void init_gro_hash(struct napi_st +@@ -6219,9 +6230,89 @@ static void init_gro_hash(struct napi_st napi->gro_bitmask = 0; } @@ -196,7 +204,12 @@ Signed-off-by: Felix Fietkau void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight) { -@@ -6238,6 +6327,7 @@ void netif_napi_add(struct net_device *d ++ if (dev->threaded) ++ set_bit(NAPI_STATE_THREADED, &napi->state); + INIT_LIST_HEAD(&napi->poll_list); + hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + napi->timer.function = napi_watchdog; +@@ -6238,6 +6329,7 @@ void netif_napi_add(struct net_device *d #ifdef CONFIG_NETPOLL napi->poll_owner = -1; #endif @@ -204,7 +217,7 @@ Signed-off-by: Felix Fietkau set_bit(NAPI_STATE_SCHED, &napi->state); set_bit(NAPI_STATE_NPSVC, &napi->state); list_add_rcu(&napi->dev_list, &dev->napi_list); -@@ -6278,6 +6368,7 @@ static void flush_gro_hash(struct napi_s +@@ -6278,6 +6370,7 @@ static void flush_gro_hash(struct napi_s void netif_napi_del(struct napi_struct *napi) { might_sleep(); @@ -212,7 +225,7 @@ Signed-off-by: Felix Fietkau if (napi_hash_del(napi)) synchronize_net(); list_del_init(&napi->dev_list); -@@ -6290,50 +6381,18 @@ EXPORT_SYMBOL(netif_napi_del); +@@ -6290,50 +6383,18 @@ EXPORT_SYMBOL(netif_napi_del); static int napi_poll(struct napi_struct *n, struct list_head *repoll) { @@ -267,7 +280,7 @@ Signed-off-by: Felix Fietkau /* Some drivers may have called napi_schedule * prior to exhausting their budget. -@@ -10271,6 +10330,10 @@ static int __init net_dev_init(void) +@@ -10313,6 +10374,10 @@ static int __init net_dev_init(void) sd->backlog.weight = weight_p; } diff --git a/target/linux/kirkwood/patches-5.10/202-linksys-find-active-root.patch b/target/linux/kirkwood/patches-5.10/202-linksys-find-active-root.patch index 2d1f769e96..214a0cd649 100644 --- a/target/linux/kirkwood/patches-5.10/202-linksys-find-active-root.patch +++ b/target/linux/kirkwood/patches-5.10/202-linksys-find-active-root.patch @@ -22,7 +22,7 @@ Signed-off-by: Imre Kaloz const char *partname; struct device_node *pp; int nr_parts, i, ret = 0; -@@ -124,9 +127,15 @@ static int parse_fixed_partitions(struct +@@ -126,9 +129,15 @@ static int parse_fixed_partitions(struct parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; @@ -41,7 +41,7 @@ Signed-off-by: Imre Kaloz parts[i].name = partname; if (of_get_property(pp, "read-only", &len)) -@@ -242,6 +251,18 @@ static int __init ofpart_parser_init(voi +@@ -244,6 +253,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/kirkwood/patches-5.4/202-linksys-find-active-root.patch b/target/linux/kirkwood/patches-5.4/202-linksys-find-active-root.patch index cc35583b98..3077e718c3 100644 --- a/target/linux/kirkwood/patches-5.4/202-linksys-find-active-root.patch +++ b/target/linux/kirkwood/patches-5.4/202-linksys-find-active-root.patch @@ -22,7 +22,7 @@ Signed-off-by: Imre Kaloz const char *partname; struct device_node *pp; int nr_parts, i, ret = 0; -@@ -124,9 +127,15 @@ static int parse_fixed_partitions(struct +@@ -126,9 +129,15 @@ static int parse_fixed_partitions(struct parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; @@ -41,7 +41,7 @@ Signed-off-by: Imre Kaloz parts[i].name = partname; if (of_get_property(pp, "read-only", &len)) -@@ -239,6 +248,18 @@ static int __init ofpart_parser_init(voi +@@ -241,6 +250,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/lantiq/patches-5.4/0101-find_active_root.patch b/target/linux/lantiq/patches-5.4/0101-find_active_root.patch index d541cc247c..201cdd3f87 100644 --- a/target/linux/lantiq/patches-5.4/0101-find_active_root.patch +++ b/target/linux/lantiq/patches-5.4/0101-find_active_root.patch @@ -48,7 +48,7 @@ /* Pull of_node from the master device node */ mtd_node = mtd_get_of_node(master); -@@ -86,7 +120,9 @@ static int parse_fixed_partitions(struct +@@ -88,7 +122,9 @@ static int parse_fixed_partitions(struct return 0; parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); @@ -59,7 +59,7 @@ return -ENOMEM; i = 0; -@@ -135,6 +171,11 @@ static int parse_fixed_partitions(struct +@@ -137,6 +173,11 @@ static int parse_fixed_partitions(struct if (of_get_property(pp, "lock", &len)) parts[i].mask_flags |= MTD_POWERUP_LOCK; @@ -71,7 +71,7 @@ i++; } -@@ -144,6 +185,11 @@ static int parse_fixed_partitions(struct +@@ -146,6 +187,11 @@ static int parse_fixed_partitions(struct if (quirks && quirks->post_parse) quirks->post_parse(master, parts, nr_parts); @@ -83,7 +83,7 @@ *pparts = parts; return nr_parts; -@@ -154,6 +200,7 @@ ofpart_fail: +@@ -156,6 +202,7 @@ ofpart_fail: ofpart_none: of_node_put(pp); kfree(parts); diff --git a/target/linux/mediatek/image/mt7622.mk b/target/linux/mediatek/image/mt7622.mk index 0de1c375b2..9f54021524 100644 --- a/target/linux/mediatek/image/mt7622.mk +++ b/target/linux/mediatek/image/mt7622.mk @@ -48,8 +48,8 @@ define Device/bananapi_bpi-r64 IMAGES := sysupgrade.itb KERNEL_INITRAMFS_SUFFIX := -recovery.itb ARTIFACT/sdcard.img := mt7622-gpt sdmmc | pad-to 128k | mt7622-gpt emmc | pad-to 256k |\ - bl2 emmc-2ddr | pad-to 512k | bl2 sdmmc-2ddr | pad-to 1M | bl31-uboot bananapi_bpi-r64-emmc | pad-to 2M |\ - bl31-uboot bananapi_bpi-r64-sdmmc | pad-to 6M + bl2 emmc-2ddr | pad-to 512k | bl2 sdmmc-2ddr | pad-to 1024k | bl31-uboot bananapi_bpi-r64-emmc | pad-to 2048k |\ + bl31-uboot bananapi_bpi-r64-sdmmc | pad-to 6144k KERNEL := kernel-bin | gzip KERNEL_INITRAMFS := kernel-bin | lzma | fit lzma $$(DTS_DIR)/$$(DEVICE_DTS).dtb with-initrd | pad-to 128k IMAGE/sysupgrade.itb := append-kernel | fit gzip $$(DTS_DIR)/$$(DEVICE_DTS).dtb external-static-with-rootfs | append-metadata diff --git a/target/linux/mvebu/files/arch/arm/boot/dts/armada-370-buffalo-ls421de.dts b/target/linux/mvebu/files/arch/arm/boot/dts/armada-370-buffalo-ls421de.dts index 57f1acf5f1..4e176554c6 100644 --- a/target/linux/mvebu/files/arch/arm/boot/dts/armada-370-buffalo-ls421de.dts +++ b/target/linux/mvebu/files/arch/arm/boot/dts/armada-370-buffalo-ls421de.dts @@ -25,7 +25,7 @@ }; chosen { - bootargs = "console=ttyS0,115200 earlyprintk noinitrd rootfstype=squashfs"; + bootargs = "earlycon"; stdout-path = "serial0:115200n8"; append-rootblock = "nullparameter="; /* override the bootloader args */ }; @@ -127,8 +127,6 @@ gpio_keys { compatible = "gpio-keys"; - #address-cells = <1>; - #size-cells = <0>; pinctrl-0 = <&pmx_buttons>; pinctrl-names = "default"; @@ -361,8 +359,6 @@ pinctrl-names = "default"; spi-flash@0 { - #address-cells = <1>; - #size-cells = <1>; compatible = "mxicy,mx25l8005", "jedec,spi-nor"; reg = <0>; /* Chip select 0 */ spi-max-frequency = <50000000>; @@ -390,42 +386,52 @@ marvell,pins = "mpp4"; marvell,function = "vdd"; }; + pmx_power_usb: pmx-power-usb { marvell,pins = "mpp5"; marvell,function = "gpo"; }; + pmx_power_hdd1: pmx-power-hdd1 { marvell,pins = "mpp8"; marvell,function = "gpio"; }; + pmx_power_hdd2: pmx-power-hdd2 { marvell,pins = "mpp9"; marvell,function = "gpo"; }; + pmx_fan_lock: pmx-fan-lock { marvell,pins = "mpp10"; marvell,function = "gpio"; }; + pmx_hdd_present: pmx-hdd-present { marvell,pins = "mpp11", "mpp12"; marvell,function = "gpio"; }; + pmx_fan_high: pmx-fan-high { marvell,pins = "mpp13"; marvell,function = "gpio"; }; + pmx_fan_low: pmx-fan-low { marvell,pins = "mpp14"; marvell,function = "gpio"; }; + pmx_buttons: pmx-buttons { marvell,pins = "mpp15", "mpp16"; marvell,function = "gpio"; }; + pmx_leds1: pmx-leds { marvell,pins = "mpp7", "mpp54", "mpp59", "mpp61"; marvell,function = "gpo"; }; + pmx_leds2: pmx-leds { marvell,pins = "mpp55", "mpp57", "mpp62"; marvell,function = "gpio"; diff --git a/target/linux/mvebu/patches-5.10/400-find_active_root.patch b/target/linux/mvebu/patches-5.10/400-find_active_root.patch index 9df99d69ae..6146c8e038 100644 --- a/target/linux/mvebu/patches-5.10/400-find_active_root.patch +++ b/target/linux/mvebu/patches-5.10/400-find_active_root.patch @@ -22,7 +22,7 @@ Signed-off-by: Imre Kaloz struct device_node *pp; int nr_parts, i, ret = 0; bool dedicated = true; -@@ -124,9 +127,13 @@ static int parse_fixed_partitions(struct +@@ -126,9 +129,13 @@ static int parse_fixed_partitions(struct parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; @@ -39,7 +39,7 @@ Signed-off-by: Imre Kaloz parts[i].name = partname; if (of_get_property(pp, "read-only", &len)) -@@ -242,6 +249,18 @@ static int __init ofpart_parser_init(voi +@@ -244,6 +251,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/mvebu/patches-5.4/400-find_active_root.patch b/target/linux/mvebu/patches-5.4/400-find_active_root.patch index 8475bec925..6ac777a1d2 100644 --- a/target/linux/mvebu/patches-5.4/400-find_active_root.patch +++ b/target/linux/mvebu/patches-5.4/400-find_active_root.patch @@ -22,7 +22,7 @@ Signed-off-by: Imre Kaloz struct device_node *pp; int nr_parts, i, ret = 0; bool dedicated = true; -@@ -124,9 +127,13 @@ static int parse_fixed_partitions(struct +@@ -126,9 +129,13 @@ static int parse_fixed_partitions(struct parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; @@ -39,7 +39,7 @@ Signed-off-by: Imre Kaloz parts[i].name = partname; if (of_get_property(pp, "read-only", &len)) -@@ -239,6 +246,18 @@ static int __init ofpart_parser_init(voi +@@ -241,6 +248,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/octeontx/patches-5.4/0004-PCI-add-quirk-for-Gateworks-PLX-PEX860x-switch-with-.patch b/target/linux/octeontx/patches-5.4/0004-PCI-add-quirk-for-Gateworks-PLX-PEX860x-switch-with-.patch index 833604ae55..e33a28e953 100644 --- a/target/linux/octeontx/patches-5.4/0004-PCI-add-quirk-for-Gateworks-PLX-PEX860x-switch-with-.patch +++ b/target/linux/octeontx/patches-5.4/0004-PCI-add-quirk-for-Gateworks-PLX-PEX860x-switch-with-.patch @@ -22,7 +22,7 @@ Signed-off-by: Tim Harvey #include #include #include -@@ -5622,3 +5623,34 @@ static void apex_pci_fixup_class(struct +@@ -5625,3 +5626,34 @@ static void apex_pci_fixup_class(struct } DECLARE_PCI_FIXUP_CLASS_HEADER(0x1ac1, 0x089a, PCI_CLASS_NOT_DEFINED, 8, apex_pci_fixup_class); diff --git a/target/linux/pistachio/patches-5.4/401-mtd-nor-support-mtd-name-from-device-tree.patch b/target/linux/pistachio/patches-5.4/401-mtd-nor-support-mtd-name-from-device-tree.patch index 2d0b1cc634..69a8303d2b 100644 --- a/target/linux/pistachio/patches-5.4/401-mtd-nor-support-mtd-name-from-device-tree.patch +++ b/target/linux/pistachio/patches-5.4/401-mtd-nor-support-mtd-name-from-device-tree.patch @@ -34,7 +34,7 @@ Signed-off-by: Abhimanyu Vishwakarma mtd->type = MTD_NORFLASH; --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c -@@ -778,6 +778,17 @@ out_error: +@@ -779,6 +779,17 @@ out_error: */ static void mtd_set_dev_defaults(struct mtd_info *mtd) { diff --git a/target/linux/ramips/patches-5.10/810-uvc-add-iPassion-iP2970-support.patch b/target/linux/ramips/patches-5.10/810-uvc-add-iPassion-iP2970-support.patch index 410f369f70..e0703db1bc 100644 --- a/target/linux/ramips/patches-5.10/810-uvc-add-iPassion-iP2970-support.patch +++ b/target/linux/ramips/patches-5.10/810-uvc-add-iPassion-iP2970-support.patch @@ -13,7 +13,7 @@ Signed-off-by: John Crispin --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c -@@ -2969,6 +2969,18 @@ static const struct usb_device_id uvc_id +@@ -2972,6 +2972,18 @@ static const struct usb_device_id uvc_id .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, diff --git a/target/linux/ramips/patches-5.4/0031-uvc-add-iPassion-iP2970-support.patch b/target/linux/ramips/patches-5.4/0031-uvc-add-iPassion-iP2970-support.patch index 084b655483..dbeee8a78b 100644 --- a/target/linux/ramips/patches-5.4/0031-uvc-add-iPassion-iP2970-support.patch +++ b/target/linux/ramips/patches-5.4/0031-uvc-add-iPassion-iP2970-support.patch @@ -13,7 +13,7 @@ Signed-off-by: John Crispin --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c -@@ -2908,6 +2908,18 @@ static const struct usb_device_id uvc_id +@@ -2911,6 +2911,18 @@ static const struct usb_device_id uvc_id .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, diff --git a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-8.dts b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-8.dts new file mode 100644 index 0000000000..e9c5efe603 --- /dev/null +++ b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-8.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-8", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-8 Switch"; +}; + +&gpio1 { + /delete-node/ poe_enable; +}; diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c index e0832c42b8..af6d92cbc2 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c @@ -310,7 +310,7 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port, link = priv->r->get_port_reg_le(priv->r->mac_link_sts); if (link & BIT_ULL(port)) state->link = 1; - pr_info("%s: link state: %llx\n", __func__, link & BIT_ULL(port)); + pr_debug("%s: link state: %llx\n", __func__, link & BIT_ULL(port)); state->duplex = 0; if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port)) diff --git a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c index 7931daff07..2f678b7384 100644 --- a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c @@ -855,7 +855,7 @@ static int rtl838x_eth_open(struct net_device *ndev) struct ring_b *ring = priv->membase; int i, err; - pr_info("%s called: RX rings %d(length %d), TX rings %d(length %d)\n", + pr_debug("%s called: RX rings %d(length %d), TX rings %d(length %d)\n", __func__, priv->rxrings, priv->rxringlen, TXRINGS, TXRINGLEN); spin_lock_irqsave(&priv->lock, flags); @@ -1342,7 +1342,7 @@ static void rtl838x_validate(struct phylink_config *config, { __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - pr_info("In %s\n", __func__); + pr_debug("In %s\n", __func__); if (!phy_interface_mode_is_rgmii(state->interface) && state->interface != PHY_INTERFACE_MODE_1000BASEX && @@ -1404,7 +1404,7 @@ static void rtl838x_mac_an_restart(struct phylink_config *config) if (priv->family_id != RTL8380_FAMILY_ID) return; - pr_info("In %s\n", __func__); + pr_debug("In %s\n", __func__); /* Restart by disabling and re-enabling link */ sw_w32(0x6192D, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); mdelay(20); @@ -1419,7 +1419,7 @@ static int rtl838x_mac_pcs_get_state(struct phylink_config *config, struct rtl838x_eth_priv *priv = netdev_priv(dev); int port = priv->cpu_port; - pr_info("In %s\n", __func__); + pr_debug("In %s\n", __func__); state->link = priv->r->get_mac_link_sts(port) ? 1 : 0; state->duplex = priv->r->get_mac_link_dup_sts(port) ? 1 : 0; @@ -1456,7 +1456,7 @@ static void rtl838x_mac_link_down(struct phylink_config *config, struct net_device *dev = container_of(config->dev, struct net_device, dev); struct rtl838x_eth_priv *priv = netdev_priv(dev); - pr_info("In %s\n", __func__); + pr_debug("In %s\n", __func__); /* Stop TX/RX to port */ sw_w32_mask(0x03, 0, priv->r->mac_port_ctrl(priv->cpu_port)); } @@ -1468,7 +1468,7 @@ static void rtl838x_mac_link_up(struct phylink_config *config, unsigned int mode struct net_device *dev = container_of(config->dev, struct net_device, dev); struct rtl838x_eth_priv *priv = netdev_priv(dev); - pr_info("In %s\n", __func__); + pr_debug("In %s\n", __func__); /* Restart TX/RX to port */ sw_w32_mask(0, 0x03, priv->r->mac_port_ctrl(priv->cpu_port)); } @@ -1479,7 +1479,7 @@ static void rtl838x_set_mac_hw(struct net_device *dev, u8 *mac) unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - pr_info("In %s\n", __func__); + pr_debug("In %s\n", __func__); sw_w32((mac[0] << 8) | mac[1], priv->r->mac); sw_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], priv->r->mac + 4); @@ -1547,7 +1547,7 @@ static int rtl838x_get_link_ksettings(struct net_device *ndev, { struct rtl838x_eth_priv *priv = netdev_priv(ndev); - pr_info("%s called\n", __func__); + pr_debug("%s called\n", __func__); return phylink_ethtool_ksettings_get(priv->phylink, cmd); } @@ -1556,7 +1556,7 @@ static int rtl838x_set_link_ksettings(struct net_device *ndev, { struct rtl838x_eth_priv *priv = netdev_priv(ndev); - pr_info("%s called\n", __func__); + pr_debug("%s called\n", __func__); return phylink_ethtool_ksettings_set(priv->phylink, cmd); } @@ -1678,7 +1678,7 @@ static int rtl931x_mdio_write(struct mii_bus *bus, int mii_id, static int rtl838x_mdio_reset(struct mii_bus *bus) { - pr_info("%s called\n", __func__); + pr_debug("%s called\n", __func__); /* Disable MAC polling the PHY so that we can start configuration */ sw_w32(0x00000000, RTL838X_SMI_POLL_CTRL); @@ -1693,7 +1693,7 @@ static int rtl839x_mdio_reset(struct mii_bus *bus) { return 0; - pr_info("%s called\n", __func__); + pr_debug("%s called\n", __func__); /* BUG: The following does not work, but should! */ /* Disable MAC polling the PHY so that we can start configuration */ sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL); @@ -1710,7 +1710,7 @@ static int rtl931x_mdio_reset(struct mii_bus *bus) sw_w32(0x00000000, RTL931X_SMI_PORT_POLLING_CTRL); sw_w32(0x00000000, RTL931X_SMI_PORT_POLLING_CTRL + 4); - pr_info("%s called\n", __func__); + pr_debug("%s called\n", __func__); return 0; } @@ -1767,7 +1767,7 @@ static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) struct device_node *mii_np; int ret; - pr_info("%s called\n", __func__); + pr_debug("%s called\n", __func__); mii_np = of_get_child_by_name(priv->pdev->dev.of_node, "mdio-bus"); if (!mii_np) { @@ -1827,7 +1827,7 @@ err_put_node: static int rtl838x_mdio_remove(struct rtl838x_eth_priv *priv) { - pr_info("%s called\n", __func__); + pr_debug("%s called\n", __func__); if (!priv->mii_bus) return 0; diff --git a/target/linux/realtek/image/Makefile b/target/linux/realtek/image/Makefile index 424726c8a9..d867d2a3d9 100644 --- a/target/linux/realtek/image/Makefile +++ b/target/linux/realtek/image/Makefile @@ -84,37 +84,44 @@ define Device/netgear_gs110tpp-v1 endef TARGET_DEVICES += netgear_gs110tpp-v1 -define Device/zyxel_gs1900-10hp +define Device/zyxel_gs1900 SOC := rtl8380 IMAGE_SIZE := 6976k DEVICE_VENDOR := ZyXEL - DEVICE_MODEL := GS1900-10HP UIMAGE_MAGIC := 0x83800000 - KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | zyxel-vers AAZI | uImage gzip + KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | zyxel-vers $$$$(ZYXEL_VERS) | \ + uImage gzip +endef + +define Device/zyxel_gs1900-10hp + $(Device/zyxel_gs1900) + DEVICE_MODEL := GS1900-10HP + ZYXEL_VERS := AAZI endef TARGET_DEVICES += zyxel_gs1900-10hp +define Device/zyxel_gs1900-8 + $(Device/zyxel_gs1900) + DEVICE_MODEL := GS1900-8 + ZYXEL_VERS := AAHH +endef +TARGET_DEVICES += zyxel_gs1900-8 + define Device/zyxel_gs1900-8hp-v1 - SOC := rtl8380 - IMAGE_SIZE := 6976k - DEVICE_VENDOR := ZyXEL + $(Device/zyxel_gs1900) DEVICE_MODEL := GS1900-8HP DEVICE_VARIANT := v1 + ZYXEL_VERS := AAHI DEVICE_PACKAGES += lua-rs232 - UIMAGE_MAGIC := 0x83800000 - KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | zyxel-vers AAHI | uImage gzip endef TARGET_DEVICES += zyxel_gs1900-8hp-v1 define Device/zyxel_gs1900-8hp-v2 - SOC := rtl8380 - IMAGE_SIZE := 6976k - DEVICE_VENDOR := ZyXEL + $(Device/zyxel_gs1900) DEVICE_MODEL := GS1900-8HP DEVICE_VARIANT := v2 + ZYXEL_VERS := AAHI DEVICE_PACKAGES += lua-rs232 - UIMAGE_MAGIC := 0x83800000 - KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | zyxel-vers AAHI | uImage gzip endef TARGET_DEVICES += zyxel_gs1900-8hp-v2 diff --git a/target/linux/rockchip/patches-5.4/803-PM-devfreq-rockchip-add-devfreq-driver-for-rk3328-dmc.patch b/target/linux/rockchip/patches-5.4/803-PM-devfreq-rockchip-add-devfreq-driver-for-rk3328-dmc.patch index 8d67a11f90..3018bd30d6 100644 --- a/target/linux/rockchip/patches-5.4/803-PM-devfreq-rockchip-add-devfreq-driver-for-rk3328-dmc.patch +++ b/target/linux/rockchip/patches-5.4/803-PM-devfreq-rockchip-add-devfreq-driver-for-rk3328-dmc.patch @@ -33,7 +33,7 @@ index defe1d438710..5ae0832f046b 100644 + config ARM_RK3399_DMC_DEVFREQ tristate "ARM RK3399 DMC DEVFREQ Driver" - depends on ARCH_ROCKCHIP + depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 338ae8440db6..ec568406ef50 100644 --- a/drivers/devfreq/Makefile diff --git a/target/linux/x86/patches-5.4/200-pcengines-apu2-reboot.patch b/target/linux/x86/patches-5.4/200-pcengines-apu2-reboot.patch index bc57806c74..742b6d6ad5 100644 --- a/target/linux/x86/patches-5.4/200-pcengines-apu2-reboot.patch +++ b/target/linux/x86/patches-5.4/200-pcengines-apu2-reboot.patch @@ -1,6 +1,6 @@ --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c -@@ -477,6 +477,16 @@ static const struct dmi_system_id reboot +@@ -486,6 +486,16 @@ static const struct dmi_system_id reboot }, }, diff --git a/tools/Makefile b/tools/Makefile index fee66ddefb..0e99e442db 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -28,7 +28,7 @@ tools-y += mklibs mm-macros mtd-utils mtools padjffs2 patch-image patchelf tools-y += pkgconf quilt squashfskit4 sstrip ucl upx xxd zip zlib zstd tools-$(BUILD_B43_TOOLS) += b43-tools tools-$(BUILD_ISL) += isl -tools-$(BUILD_TOOLCHAIN) += expat gmp libelf mpc mpfr +tools-$(BUILD_TOOLCHAIN) += expat gmp mpc mpfr tools-$(CONFIG_EFI_IMAGES) += gptfdisk popt tools-$(CONFIG_TARGET_apm821xx)$(CONFIG_TARGET_gemini) += genext2fs tools-$(CONFIG_TARGET_ath79) += lzma-old squashfs @@ -55,7 +55,6 @@ $(curdir)/gengetopt/compile := $(curdir)/libtool/compile $(curdir)/gmp/compile := $(curdir)/libtool/compile $(curdir)/gptfdisk/compile += $(curdir)/e2fsprogs/compile $(curdir)/popt/compile $(curdir)/isl/compile := $(curdir)/gmp/compile -$(curdir)/libelf/compile := $(curdir)/libtool/compile $(curdir)/libressl/compile := $(curdir)/pkgconf/compile $(curdir)/libtool/compile := $(curdir)/m4/compile $(curdir)/autoconf/compile $(curdir)/automake/compile $(curdir)/missing-macros/compile $(curdir)/lzma-old/compile := $(curdir)/zlib/compile diff --git a/tools/libelf/Makefile b/tools/libelf/Makefile deleted file mode 100644 index 726c4776bf..0000000000 --- a/tools/libelf/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# -# Copyright (C) 2010 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=libelf -PKG_VERSION:=0.8.13 -PKG_HASH:=591a9b4ec81c1f2042a97aa60564e0cb79d041c52faa7416acb38bc95bd2c76d -PKG_RELEASE:=1 - -PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:= \ - http://distfiles.gentoo.org/distfiles/ \ - http://distcache.freebsd.org/ports-distfiles/ -HOST_BUILD_PARALLEL:=1 - -HOST_FIXUP:=autoreconf - -include $(INCLUDE_DIR)/host-build.mk - -PKG_REMOVE_FILES := - -HOST_CONFIGURE_ARGS += \ - --disable-shared \ - --enable-elf64 - -define Host/Configure - (cd $(HOST_BUILD_DIR)/$(3); \ - $(HOST_CONFIGURE_CMD) \ - $(HOST_CONFIGURE_ARGS); \ - ) -endef - - -define Host/Compile - +$(MAKE) $(HOST_JOBS) -C $(HOST_BUILD_DIR)/lib/ libelf.a -endef - -define Host/Install - $(INSTALL_DIR) $(STAGING_DIR_HOST)/{lib/pkgconfig,include/libelf} - $(CP) $(HOST_BUILD_DIR)/lib/{elf_repl.h,gelf.h,libelf.h,nlist.h,sys_elf.h} \ - $(STAGING_DIR_HOST)/include/libelf/ - $(CP) $(HOST_BUILD_DIR)/lib/libelf.a $(STAGING_DIR_HOST)/lib/ - $(CP) $(HOST_BUILD_DIR)/libelf.pc $(STAGING_DIR_HOST)/lib/pkgconfig/ -endef - -define Host/Clean - rm -rf $(STAGING_DIR_HOST)/include/libelf - rm -f $(STAGING_DIR_HOST)/lib/libelf.a - rm -f $(STAGING_DIR_HOST)/lib/pkgconfig/libelf.pc - $(call Host/Clean/Default) -endef - -$(eval $(call HostBuild)) diff --git a/tools/libelf/patches/900-fix-undefined-macro-access.patch b/tools/libelf/patches/900-fix-undefined-macro-access.patch deleted file mode 100644 index 21e7d1ba8f..0000000000 --- a/tools/libelf/patches/900-fix-undefined-macro-access.patch +++ /dev/null @@ -1,198 +0,0 @@ ---- a/lib/elf_repl.h -+++ b/lib/elf_repl.h -@@ -45,7 +45,7 @@ typedef __libelf_u32_t Elf32_Word; - #define ELF32_FSZ_SWORD 4 - #define ELF32_FSZ_WORD 4 - --#if __LIBELF64 -+#if defined(__LIBELF64) - - typedef __libelf_u64_t Elf64_Addr; - typedef __libelf_u16_t Elf64_Half; -@@ -93,7 +93,7 @@ typedef struct { - Elf32_Half e_shstrndx; - } Elf32_Ehdr; - --#if __LIBELF64 -+#if defined(__LIBELF64) - typedef struct { - unsigned char e_ident[EI_NIDENT]; - Elf64_Half e_type; -@@ -307,7 +307,7 @@ typedef struct { - Elf32_Word sh_entsize; - } Elf32_Shdr; - --#if __LIBELF64 -+#if defined(__LIBELF64) - typedef struct { - Elf64_Word sh_name; - Elf64_Word sh_type; -@@ -434,7 +434,7 @@ typedef struct { - Elf32_Half st_shndx; - } Elf32_Sym; - --#if __LIBELF64 -+#if defined(__LIBELF64) - typedef struct { - Elf64_Word st_name; - unsigned char st_info; -@@ -457,7 +457,7 @@ typedef struct { - #define ELF32_ST_TYPE(i) ((i)&0xf) - #define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) - --#if __LIBELF64 -+#if defined(__LIBELF64) - #define ELF64_ST_BIND(i) ((i)>>4) - #define ELF64_ST_TYPE(i) ((i)&0xf) - #define ELF64_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) -@@ -495,7 +495,7 @@ typedef struct { - * Macros for manipulating st_other - */ - #define ELF32_ST_VISIBILITY(o) ((o)&0x3) --#if __LIBELF64 -+#if defined(__LIBELF64) - #define ELF64_ST_VISIBILITY(o) ((o)&0x3) - #endif /* __LIBELF64 */ - -@@ -521,7 +521,7 @@ typedef struct { - Elf32_Sword r_addend; - } Elf32_Rela; - --#if __LIBELF64 -+#if defined(__LIBELF64) - typedef struct { - Elf64_Addr r_offset; - Elf64_Xword r_info; -@@ -541,7 +541,7 @@ typedef struct { - #define ELF32_R_TYPE(i) ((unsigned char)(i)) - #define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t)) - --#if __LIBELF64 -+#if defined(__LIBELF64) - #define ELF64_R_SYM(i) ((Elf64_Xword)(i)>>32) - #define ELF64_R_TYPE(i) ((i)&0xffffffffL) - #define ELF64_R_INFO(s,t) (((Elf64_Xword)(s)<<32)+((t)&0xffffffffL)) -@@ -556,7 +556,7 @@ typedef struct { - Elf32_Word n_type; /* descriptor type */ - } Elf32_Nhdr; - --#if __LIBELF64 -+#if defined(__LIBELF64) - /* Solaris and GNU use this layout. Be compatible. */ - /* XXX: Latest ELF specs say it's 64-bit!!! */ - typedef struct { -@@ -587,7 +587,7 @@ typedef struct { - Elf32_Word p_align; - } Elf32_Phdr; - --#if __LIBELF64 -+#if defined(__LIBELF64) - typedef struct { - Elf64_Word p_type; - Elf64_Word p_flags; -@@ -654,7 +654,7 @@ typedef struct { - } d_un; - } Elf32_Dyn; - --#if __LIBELF64 -+#if defined(__LIBELF64) - typedef struct { - Elf64_Sxword d_tag; - union { -@@ -798,7 +798,7 @@ typedef struct { - Elf32_Half si_flags; - } Elf32_Syminfo; - --#if __LIBELF64 -+#if defined(__LIBELF64) - typedef struct { - Elf64_Half si_boundto; - Elf64_Half si_flags; -@@ -863,7 +863,7 @@ typedef struct { - - typedef Elf32_Half Elf32_Versym; - --#if __LIBELF64 -+#if defined(__LIBELF64) - - typedef struct { - Elf64_Half vd_version; -@@ -933,7 +933,7 @@ typedef Elf64_Half Elf64_Versym; - /* - * Move section - */ --#if __LIBELF64 -+#if defined(__LIBELF64) - - typedef struct { - Elf32_Lword m_value; -@@ -973,7 +973,7 @@ typedef struct { - } c_un; - } Elf32_Cap; - --#if __LIBELF64 -+#if defined(__LIBELF64) - - typedef struct { - Elf64_Xword c_tag; ---- a/lib/gelf.h -+++ b/lib/gelf.h -@@ -22,15 +22,15 @@ - #ifndef _GELF_H - #define _GELF_H - --#if __LIBELF_INTERNAL__ -+#if defined(__LIBELF_INTERNAL__) - #include - #else /* __LIBELF_INTERNAL__ */ - #include - #endif /* __LIBELF_INTERNAL__ */ - --#if __LIBELF_NEED_LINK_H -+#if defined(__LIBELF_NEED_LINK_H) - #include --#elif __LIBELF_NEED_SYS_LINK_H -+#elif defined(__LIBELF_NEED_SYS_LINK_H) - #include - #endif /* __LIBELF_NEED_LINK_H */ - -@@ -71,7 +71,7 @@ typedef Elf64_Sym GElf_Sym; - /* - * Symbol versioning - */ --#if __LIBELF_SYMBOL_VERSIONS -+#if defined(__LIBELF_SYMBOL_VERSIONS) - typedef Elf64_Verdef GElf_Verdef; - typedef Elf64_Verneed GElf_Verneed; - typedef Elf64_Verdaux GElf_Verdaux; ---- a/lib/libelf.h -+++ b/lib/libelf.h -@@ -25,7 +25,7 @@ - #include /* for size_t */ - #include - --#if __LIBELF_INTERNAL__ -+#if defined(__LIBELF_INTERNAL__) - #include - #else /* __LIBELF_INTERNAL__ */ - #include -@@ -224,7 +224,7 @@ extern Elf_Data *elf32_xlatetom __P((Elf - */ - extern long elf32_checksum __P((Elf *__elf)); - --#if __LIBELF64 -+#if defined(__LIBELF64) - /* - * 64-bit ELF functions - * Not available on all platforms ---- a/lib/sys_elf.h.in -+++ b/lib/sys_elf.h.in -@@ -116,7 +116,7 @@ Foundation, Inc., 51 Franklin Street, Fi - # define ELF64_R_INFO(s,t) (((Elf64_Xword)(s)<<32)+((t)&0xffffffffL)) - # endif /* ELF64_R_SYM */ - --# if __LIBELF64_LINUX -+# if defined(__LIBELF64_LINUX) - typedef __libelf_u64_t Elf64_Addr; - typedef __libelf_u16_t Elf64_Half; - typedef __libelf_u64_t Elf64_Off; diff --git a/tools/mkimage/patches/080-mtk_image-add-support-for-booting-ARM64-images.patch b/tools/mkimage/patches/080-mtk_image-add-support-for-booting-ARM64-images.patch new file mode 100644 index 0000000000..dfe8128fb5 --- /dev/null +++ b/tools/mkimage/patches/080-mtk_image-add-support-for-booting-ARM64-images.patch @@ -0,0 +1,134 @@ +From 44165e4c676d266f73fda2e6ba82b4bf3262daf2 Mon Sep 17 00:00:00 2001 +From: Fabien Parent +Date: Fri, 16 Oct 2020 19:52:37 +0200 +Subject: [PATCH] tools: mtk_image: add support for booting ARM64 images + +mkimage is only able to package aarch32 binaries. Add support for +AArch64 images. + +One can create a ARM64 image using the following command line: +mkimage -T mtk_image -a 0x201000 -e 0x201000 -n "media=emmc;arm64=1" +-d bl2.bin bl2.img + +Signed-off-by: Fabien Parent +--- + tools/mtk_image.c | 28 ++++++++++++++++++++++++---- + tools/mtk_image.h | 6 +++++- + 2 files changed, 29 insertions(+), 5 deletions(-) + +diff --git a/tools/mtk_image.c b/tools/mtk_image.c +index 2ca519483d..bde1e5da4b 100644 +--- a/tools/mtk_image.c ++++ b/tools/mtk_image.c +@@ -246,6 +246,7 @@ static const struct brom_img_type { + /* Image type selected by user */ + static enum brlyt_img_type hdr_media; + static int use_lk_hdr; ++static bool is_arm64_image; + + /* LK image name */ + static char lk_name[32] = "U-Boot"; +@@ -276,6 +277,7 @@ static int mtk_brom_parse_imagename(const char *imagename) + static const char *media = ""; + static const char *nandinfo = ""; + static const char *lk = ""; ++ static const char *arm64_param = ""; + + key = buf; + while (key) { +@@ -323,6 +325,9 @@ static int mtk_brom_parse_imagename(const char *imagename) + + if (!strcmp(key, "lkname")) + snprintf(lk_name, sizeof(lk_name), "%s", val); ++ ++ if (!strcmp(key, "arm64")) ++ arm64_param = val; + } + + if (next) +@@ -354,6 +359,9 @@ static int mtk_brom_parse_imagename(const char *imagename) + } + } + ++ if (arm64_param && arm64_param[0] == '1') ++ is_arm64_image = true; ++ + free(buf); + + if (hdr_media == BRLYT_TYPE_INVALID) { +@@ -458,6 +466,9 @@ static int mtk_image_verify_gen_header(const uint8_t *ptr, int print) + le32_to_cpu(gfh->file_info.load_addr) + + le32_to_cpu(gfh->file_info.jump_offset)); + ++ if (print) ++ printf("Architecture: %s\n", is_arm64_image ? "ARM64" : "ARM"); ++ + return 0; + } + +@@ -523,6 +534,9 @@ static int mtk_image_verify_nand_header(const uint8_t *ptr, int print) + le32_to_cpu(gfh->file_info.load_addr) + + le32_to_cpu(gfh->file_info.jump_offset)); + ++ if (print) ++ printf("Architecture: %s\n", is_arm64_image ? "ARM64" : "ARM"); ++ + return 0; + } + +@@ -581,6 +595,8 @@ static void put_ghf_common_header(struct gfh_common_header *gfh, int size, + static void put_ghf_header(struct gfh_header *gfh, int file_size, + int dev_hdr_size, int load_addr, int flash_type) + { ++ uint32_t cfg_bits; ++ + memset(gfh, 0, sizeof(struct gfh_header)); + + /* GFH_FILE_INFO header */ +@@ -608,11 +624,15 @@ static void put_ghf_header(struct gfh_header *gfh, int file_size, + /* GFH_BROM_CFG header */ + put_ghf_common_header(&gfh->brom_cfg.gfh, sizeof(gfh->brom_cfg), + GFH_TYPE_BROM_CFG, 3); +- gfh->brom_cfg.cfg_bits = cpu_to_le32( +- GFH_BROM_CFG_USBDL_AUTO_DETECT_DIS | +- GFH_BROM_CFG_USBDL_BY_KCOL0_TIMEOUT_EN | +- GFH_BROM_CFG_USBDL_BY_FLAG_TIMEOUT_EN); ++ cfg_bits = GFH_BROM_CFG_USBDL_AUTO_DETECT_DIS | ++ GFH_BROM_CFG_USBDL_BY_KCOL0_TIMEOUT_EN | ++ GFH_BROM_CFG_USBDL_BY_FLAG_TIMEOUT_EN; + gfh->brom_cfg.usbdl_by_kcol0_timeout_ms = cpu_to_le32(5000); ++ if (is_arm64_image) { ++ gfh->brom_cfg.jump_bl_arm64 = GFH_BROM_CFG_JUMP_BL_ARM64; ++ cfg_bits |= GFH_BROM_CFG_JUMP_BL_ARM64_EN; ++ } ++ gfh->brom_cfg.cfg_bits = cpu_to_le32(cfg_bits); + + /* GFH_BL_SEC_KEY header */ + put_ghf_common_header(&gfh->bl_sec_key.gfh, sizeof(gfh->bl_sec_key), +diff --git a/tools/mtk_image.h b/tools/mtk_image.h +index 4e78b3d0ff..7dda71ce88 100644 +--- a/tools/mtk_image.h ++++ b/tools/mtk_image.h +@@ -136,7 +136,9 @@ struct gfh_brom_cfg { + struct gfh_common_header gfh; + uint32_t cfg_bits; + uint32_t usbdl_by_auto_detect_timeout_ms; +- uint8_t unused[0x48]; ++ uint8_t unused[0x45]; ++ uint8_t jump_bl_arm64; ++ uint8_t unused2[2]; + uint32_t usbdl_by_kcol0_timeout_ms; + uint32_t usbdl_by_flag_timeout_ms; + uint32_t pad; +@@ -146,6 +148,8 @@ struct gfh_brom_cfg { + #define GFH_BROM_CFG_USBDL_AUTO_DETECT_DIS 0x10 + #define GFH_BROM_CFG_USBDL_BY_KCOL0_TIMEOUT_EN 0x80 + #define GFH_BROM_CFG_USBDL_BY_FLAG_TIMEOUT_EN 0x100 ++#define GFH_BROM_CFG_JUMP_BL_ARM64_EN 0x1000 ++#define GFH_BROM_CFG_JUMP_BL_ARM64 0x64 + + struct gfh_bl_sec_key { + struct gfh_common_header gfh; +-- +2.30.1 + diff --git a/tools/mkimage/patches/081-mtk_image-add-an-option-to-set-device-header-offset.patch b/tools/mkimage/patches/081-mtk_image-add-an-option-to-set-device-header-offset.patch new file mode 100644 index 0000000000..45f8f7c1c1 --- /dev/null +++ b/tools/mkimage/patches/081-mtk_image-add-an-option-to-set-device-header-offset.patch @@ -0,0 +1,226 @@ +From patchwork Tue Mar 9 07:52:31 2021 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Weijie Gao +X-Patchwork-Id: 1449568 +Return-Path: +X-Original-To: incoming@patchwork.ozlabs.org +Delivered-To: patchwork-incoming@bilbo.ozlabs.org +Authentication-Results: ozlabs.org; + spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de + (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; + envelope-from=u-boot-bounces@lists.denx.de; receiver=) +Authentication-Results: ozlabs.org; + dkim=pass (1024-bit key; + unprotected) header.d=mediatek.com header.i=@mediatek.com header.a=rsa-sha256 + header.s=dk header.b=i1dK9gFR; + dkim-atps=neutral +Received: from phobos.denx.de (phobos.denx.de + [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) + (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 4DvnX84rrHz9sW1 + for ; Tue, 9 Mar 2021 18:53:44 +1100 (AEDT) +Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) + by phobos.denx.de (Postfix) with ESMTP id DF60F8219C; + Tue, 9 Mar 2021 08:53:29 +0100 (CET) +Authentication-Results: phobos.denx.de; + dmarc=pass (p=none dis=none) header.from=mediatek.com +Authentication-Results: phobos.denx.de; + spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de +Authentication-Results: phobos.denx.de; + dkim=pass (1024-bit key; + unprotected) header.d=mediatek.com header.i=@mediatek.com + header.b="i1dK9gFR"; + dkim-atps=neutral +Received: by phobos.denx.de (Postfix, from userid 109) + id 5456882625; Tue, 9 Mar 2021 08:53:28 +0100 (CET) +X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de +X-Spam-Level: +X-Spam-Status: No, score=0.5 required=5.0 tests=BAYES_00,DKIM_SIGNED, + DKIM_VALID,DKIM_VALID_AU,MIME_BASE64_TEXT,RDNS_NONE,SPF_HELO_NONE, + UNPARSEABLE_RELAY autolearn=no autolearn_force=no version=3.4.2 +Received: from mailgw02.mediatek.com (unknown [1.203.163.81]) + by phobos.denx.de (Postfix) with ESMTP id 7526E80EF2 + for ; Tue, 9 Mar 2021 08:53:19 +0100 (CET) +Authentication-Results: phobos.denx.de; + dmarc=pass (p=none dis=none) header.from=mediatek.com +Authentication-Results: phobos.denx.de; + spf=pass smtp.mailfrom=weijie.gao@mediatek.com +X-UUID: 3b5ccbd89ab948daa31ec738ee94e7ed-20210309 +DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; + d=mediatek.com; + s=dk; + h=Content-Transfer-Encoding:Content-Type:MIME-Version:Message-ID:Date:Subject:CC:To:From; + bh=mijplkmurYfYet7eQPGQD6GyyMtN6xMXZKHVAlpy0hM=; + b=i1dK9gFRfmkqD5vAud81Q3CdZlOQm3XK0H/NwbxYyncwalZqRZA1YBKTQhmPW0avcbwOQpGVlRmu1VAfALWgK80acX7bPIjWjtaJtK4/99vc+wIthmm1E5QMewyBAFkzGfx7A8ryh4HdcsG/esbnI0Mk2nletBHwRFAEVs3uUfU=; +X-UUID: 3b5ccbd89ab948daa31ec738ee94e7ed-20210309 +Received: from mtkcas32.mediatek.inc [(172.27.4.253)] by mailgw02.mediatek.com + (envelope-from ) + (mailgw01.mediatek.com ESMTP with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) + with ESMTP id 23424931; Tue, 09 Mar 2021 15:53:06 +0800 +Received: from MTKCAS32.mediatek.inc (172.27.4.184) by MTKMBS31N1.mediatek.inc + (172.27.4.69) with Microsoft SMTP Server (TLS) id 15.0.1497.2; + Tue, 9 Mar 2021 15:52:58 +0800 +Received: from mcddlt001.mediatek.inc (10.19.240.15) by MTKCAS32.mediatek.inc + (172.27.4.170) with Microsoft SMTP Server id 15.0.1497.2 via Frontend + Transport; Tue, 9 Mar 2021 15:52:58 +0800 +From: Weijie Gao +To: +CC: GSS_MTK_Uboot_upstream , Weijie Gao + +Subject: [PATCH] tools: mtk_image: add an option to set device header offset +Date: Tue, 9 Mar 2021 15:52:31 +0800 +Message-ID: <1615276351-30641-1-git-send-email-weijie.gao@mediatek.com> +X-Mailer: git-send-email 1.9.1 +MIME-Version: 1.0 +X-TM-SNTS-SMTP: + B09EA906E69093D91FA73A3F764A0B89D3838DA91A4FA20DA0483EBE19962CA02000:8 +X-MTK: N +X-BeenThere: u-boot@lists.denx.de +X-Mailman-Version: 2.1.34 +Precedence: list +List-Id: U-Boot discussion +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +Errors-To: u-boot-bounces@lists.denx.de +Sender: "U-Boot" +X-Virus-Scanned: clamav-milter 0.102.4 at phobos.denx.de +X-Virus-Status: Clean + +This patch adds an option which allows setting the device header offset. +This is useful if this tool is used to generate ATF BL2 image of mt7622 for +SD cards. + +Signed-off-by: Weijie Gao +--- + tools/mtk_image.c | 50 ++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 47 insertions(+), 3 deletions(-) + +diff --git a/tools/mtk_image.c b/tools/mtk_image.c +index bde1e5da4b..418c5fd54b 100644 +--- a/tools/mtk_image.c ++++ b/tools/mtk_image.c +@@ -243,8 +243,13 @@ static const struct brom_img_type { + } + }; + ++/* Indicates whether we're generating or verifying */ ++static bool img_gen; ++static uint32_t img_size; ++ + /* Image type selected by user */ + static enum brlyt_img_type hdr_media; ++static uint32_t hdr_offset; + static int use_lk_hdr; + static bool is_arm64_image; + +@@ -275,6 +280,7 @@ static int mtk_brom_parse_imagename(const char *imagename) + + /* User passed arguments from image name */ + static const char *media = ""; ++ static const char *hdr_offs = ""; + static const char *nandinfo = ""; + static const char *lk = ""; + static const char *arm64_param = ""; +@@ -317,6 +323,9 @@ static int mtk_brom_parse_imagename(const char *imagename) + if (!strcmp(key, "media")) + media = val; + ++ if (!strcmp(key, "hdroffset")) ++ hdr_offs = val; ++ + if (!strcmp(key, "nandinfo")) + nandinfo = val; + +@@ -359,6 +368,10 @@ static int mtk_brom_parse_imagename(const char *imagename) + } + } + ++ /* parse device header offset */ ++ if (hdr_offs && hdr_offs[0]) ++ hdr_offset = strtoul(hdr_offs, NULL, 0); ++ + if (arm64_param && arm64_param[0] == '1') + is_arm64_image = true; + +@@ -422,6 +435,7 @@ static int mtk_image_vrec_header(struct image_tool_params *params, + static int mtk_image_verify_gen_header(const uint8_t *ptr, int print) + { + union gen_boot_header *gbh = (union gen_boot_header *)ptr; ++ uint32_t gfh_offset, total_size, devh_size; + struct brom_layout_header *bh; + struct gfh_header *gfh; + const char *bootmedia; +@@ -453,7 +467,32 @@ static int mtk_image_verify_gen_header(const uint8_t *ptr, int print) + le32_to_cpu(bh->type) != BRLYT_TYPE_SDMMC)) + return -1; + +- gfh = (struct gfh_header *)(ptr + le32_to_cpu(bh->header_size)); ++ devh_size = sizeof(struct gen_device_header); ++ ++ if (img_gen) { ++ gfh_offset = devh_size; ++ } else { ++ gfh_offset = le32_to_cpu(bh->header_size); ++ ++ if (gfh_offset + sizeof(struct gfh_header) > img_size) { ++ /* ++ * This may happen if the hdr_offset used to generate ++ * this image is not zero. ++ * Since device header size is not fixed, we can't ++ * cover all possible cases. ++ * Assuming the image is valid only if the real ++ * device header size equals to devh_size. ++ */ ++ total_size = le32_to_cpu(bh->total_size); ++ ++ if (total_size - gfh_offset > img_size - devh_size) ++ return -1; ++ ++ gfh_offset = devh_size; ++ } ++ } ++ ++ gfh = (struct gfh_header *)(ptr + gfh_offset); + + if (strcmp(gfh->file_info.name, GFH_FILE_INFO_NAME)) + return -1; +@@ -549,6 +588,8 @@ static int mtk_image_verify_header(unsigned char *ptr, int image_size, + if (le32_to_cpu(lk->magic) == LK_PART_MAGIC) + return 0; + ++ img_size = image_size; ++ + if (!strcmp((char *)ptr, NAND_BOOT_NAME)) + return mtk_image_verify_nand_header(ptr, 0); + else +@@ -682,8 +723,8 @@ static void mtk_image_set_gen_header(void *ptr, off_t filesize, + + /* BRLYT header */ + put_brom_layout_header(&hdr->brlyt, hdr_media); +- hdr->brlyt.header_size = cpu_to_le32(sizeof(struct gen_device_header)); +- hdr->brlyt.total_size = cpu_to_le32(filesize); ++ hdr->brlyt.header_size = cpu_to_le32(hdr_offset + sizeof(*hdr)); ++ hdr->brlyt.total_size = cpu_to_le32(hdr_offset + filesize); + hdr->brlyt.header_size_2 = hdr->brlyt.header_size; + hdr->brlyt.total_size_2 = hdr->brlyt.total_size; + +@@ -747,6 +788,9 @@ static void mtk_image_set_header(void *ptr, struct stat *sbuf, int ifd, + return; + } + ++ img_gen = true; ++ img_size = sbuf->st_size; ++ + if (hdr_media == BRLYT_TYPE_NAND || hdr_media == BRLYT_TYPE_SNAND) + mtk_image_set_nand_header(ptr, sbuf->st_size, params->addr); + else