diff options
author | Daniel Golle <daniel@makrotopia.org> | 2021-02-20 13:04:38 +0000 |
---|---|---|
committer | Daniel Golle <daniel@makrotopia.org> | 2021-03-11 16:24:53 +0000 |
commit | 9dfc2b3ca4dff28259576e21bc3ade910ff52a05 (patch) | |
tree | bc7a3bbea0fa638ece89027d3f04fcd0d09a9359 /package | |
parent | e2cffbb80528b1c13bbaba6e53835de378ef3923 (diff) | |
download | upstream-9dfc2b3ca4dff28259576e21bc3ade910ff52a05.tar.gz upstream-9dfc2b3ca4dff28259576e21bc3ade910ff52a05.tar.bz2 upstream-9dfc2b3ca4dff28259576e21bc3ade910ff52a05.zip |
uboot-mediatek: update to 2021.04-rc3 with MediaTek's patches
MediaTek published their current U-Boot patchset on github:
https://github.com/mtk-openwrt/u-boot/commits/mtksoc
Import the platform patches from there (`00-mtk-*.patch`), arrange,
them nicely, drop no longer needed local patches and rebase on top of
U-Boot 2021.04-rc3.
Tested and works well on Linksys E8450 (snand-1ddr) as well as
Bananapi BPi-R64 (sdmmc-2ddr, emmc-2ddr).
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Diffstat (limited to 'package')
39 files changed, 5711 insertions, 8847 deletions
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 <daniel@makrotopia.org> +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 <mbrugger@suse.com> +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 <mbrugger@suse.com> +--- + 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 <sam.shih@mediatek.com> +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 <sam.shih@mediatek.com> +--- + 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 <sam.shih@mediatek.com> +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 <sam.shih@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> + */ + +-#include <config.h> +-/ { +- binman { +- filename = "u-boot-mtk.bin"; +- pad-byte = <0xff>; +- +-#ifdef CONFIG_SPL +- blob { +- filename = "spl/u-boot-spl-mtk.bin"; +- size = <CONFIG_SPL_PAD_TO>; +- }; +- +- 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> ++# ++# 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 <weijie.gao@mediatek.com> ++# ++# 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 <weijie.gao@mediatek.com> ++ */ ++ ++#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 <mtk-snand.h> ++#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 <weijie.gao@mediatek.com> ++ */ ++ ++#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 <weijie.gao@mediatek.com> ++ */ ++ ++#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 <weijie.gao@mediatek.com> ++ */ ++ ++#include <common.h> ++#include <dm.h> ++#include <malloc.h> ++#include <mapmem.h> ++#include <linux/mtd/mtd.h> ++#include <watchdog.h> ++ ++#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 <weijie.gao@mediatek.com> ++ */ ++ ++#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 <weijie.gao@mediatek.com> ++ */ ++ ++#ifndef _MTK_SNAND_OS_H_ ++#define _MTK_SNAND_OS_H_ ++ ++#include <common.h> ++#include <cpu_func.h> ++#include <errno.h> ++#include <div64.h> ++#include <malloc.h> ++#include <stdbool.h> ++#include <stdarg.h> ++#include <linux/types.h> ++#include <asm/io.h> ++#include <linux/bitops.h> ++#include <linux/sizes.h> ++#include <linux/iopoll.h> ++ ++#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 <weijie.gao@mediatek.com> ++ */ ++ ++#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 <weijie.gao@mediatek.com> ++ */ ++ ++#ifndef _MTK_SNAND_H_ ++#define _MTK_SNAND_H_ ++ ++#ifndef PRIVATE_MTK_SNAND_HEADER ++#include <stddef.h> ++#include <stdint.h> ++#include <stdbool.h> ++#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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> ++ */ ++ ++#include <common.h> ++#include <dm.h> ++#include <dm/uclass.h> ++#include <malloc.h> ++#include <mapmem.h> ++#include <mtd.h> ++#include <watchdog.h> ++ ++#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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> ++ */ ++ ++#include <command.h> ++#include <env.h> ++#include <env_internal.h> ++#include <errno.h> ++#include <linux/kernel.h> ++#include <linux/stddef.h> ++#include <linux/types.h> ++#include <linux/mtd/mtd.h> ++#include <malloc.h> ++#include <memalign.h> ++#include <mtd.h> ++#include <search.h> ++ ++#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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + 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 <daniel@makrotopia.org> +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 <daniel@makrotopia.org> +--- + 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 <sam.shih@mediatek.com> -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 <xiangsheng.hou@mediatek.com> ---- - 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 <common.h> -+#include <linux/io.h> -+#include <dm.h> -+#include <clk.h> -+#include <nand.h> -+#include <linux/iopoll.h> -+#include <linux/delay.h> -+#include <linux/mtd/nand.h> -+#include <linux/mtd/mtd.h> -+#include <linux/mtd/partitions.h> -+#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 <common.h> -+#include <dm.h> -+#include <clk.h> -+#include <asm/dma-mapping.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/errno.h> -+#include <linux/bitops.h> -+#include <linux/kernel.h> -+#include <linux/compiler-gcc.h> -+ -+#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 <linux/sizes.h> - -+/* 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 <sam.shih@mediatek.com> -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 <xiangsheng.hou@mediatek.com> ---- - 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 = <GIC_SPI 96 IRQ_TYPE_LEVEL_LOW>, -+ <GIC_SPI 95 IRQ_TYPE_LEVEL_LOW>; -+ 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 <sam.shih@mediatek.com> -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 <sam.shih@mediatek.com> ---- - 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 <xypron.glpk@gmx.de> -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 <xypron.glpk@gmx.de> -Reviewed-by: Philippe Reynes <philippe.reynes@softathome.com> ---- - 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", - "<button_label> \tGet button state\n" - "button list\t\tShow a list of buttons" 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 <mail@david-bauer.net> +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 <mail@david-bauer.net> +--- + 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 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 <weijie.gao@mediatek.com> +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 <weijie.gao@mediatek.com> +--- + cmd/bootmenu.c | 77 +++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 70 insertions(+), 7 deletions(-) + +--- a/cmd/bootmenu.c ++++ b/cmd/bootmenu.c +@@ -11,6 +11,7 @@ + #include <menu.h> + #include <watchdog.h> + #include <malloc.h> ++#include <linux/ctype.h> + #include <linux/delay.h> + #include <linux/string.h> + +@@ -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 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 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/009-mt7622-generic-reset-button-ignore-env.patch b/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 <common.h> #include <config.h> +#include <dm.h> +#include <button.h> ++ #include <env.h> #include <init.h> + #include <asm/global_data.h> +#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 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 index 37d1b6a671..37d1b6a671 100644 --- a/package/boot/uboot-mediatek/patches/010-update-u7623-defconfig.patch +++ b/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 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 index 3624c0102a..2fb2d3aa8f 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,6 +1,6 @@ --- /dev/null +++ b/configs/mt7622_bananapi_bpi-r64-sdmmc_defconfig -@@ -0,0 +1,123 @@ +@@ -0,0 +1,124 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y @@ -10,7 +10,10 @@ +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_DEBUG_UART=y +CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-sdmmc_env" +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_SMBIOS_PRODUCT_NAME="" @@ -50,7 +53,7 @@ +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 @@ -61,7 +64,7 @@ +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 @@ -111,8 +114,6 @@ +CONFIG_MMC_MTK=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 +132,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= [0;34m( ( ( [1;39mOpenWrt[0;34m ) ) ) [0;36m[SD card][0m -+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=[31m6. Install bootloader and recovery to eMMC.[0m=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=[31mInstall bootloader and recovery to eMMC.[0m=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,7 +184,7 @@ +_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title [33m$ver[0m" --- /dev/null +++ b/configs/mt7622_bananapi_bpi-r64-emmc_defconfig -@@ -0,0 +1,123 @@ +@@ -0,0 +1,125 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y @@ -193,7 +194,10 @@ +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_DEBUG_UART=y +CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-emmc_env" +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_SMBIOS_PRODUCT_NAME="" @@ -233,7 +237,7 @@ +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 @@ -244,7 +248,7 @@ +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 @@ -292,10 +296,9 @@ +CONFIG_MMC=y +CONFIG_MMC_DEFAULT_DEV=0 +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 +317,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= [0;34m( ( ( [1;39mOpenWrt[0;34m ) ) ) [0;36m[eMMC][0m -+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 index 711c1e6983..e78641e8fe 100644 --- a/package/boot/uboot-mediatek/patches/020-add-linksys-e8450.patch +++ b/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch @@ -1,6 +1,6 @@ --- /dev/null +++ b/configs/mt7622_linksys_e8450_defconfig -@@ -0,0 +1,130 @@ +@@ -0,0 +1,128 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y @@ -11,7 +11,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 @@ -49,10 +52,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 +67,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 @@ -98,7 +100,6 @@ +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 +115,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 +131,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 +153,7 @@ + }; + + aliases { -+ spi0 = &snfi; ++ spi0 = &snand; + }; + + gpio-keys { @@ -288,22 +286,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 +329,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 +337,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 +351,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= [0;34m( ( ( [1;39mOpenWrt[0;34m ) ) )[0m -+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=[31m6. Load BL31+U-Boot FIP via TFTP then write to flash.[0m=run boot_tftp_write_fip ; run bootmenu_confirm_return -+bootmenu_7=[31m7. Load BL2 preloader via TFTP then write to flash.[0m=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=[31mLoad BL31+U-Boot FIP via TFTP then write to flash.[0m=run boot_tftp_write_fip ; run bootmenu_confirm_return ++bootmenu_7=[31mLoad BL2 preloader via TFTP then write to flash.[0m=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 +380,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 |