[openwrt/openwrt] ramips: 5.10: copy patches from 5.4

LEDE Commits lede-commits at lists.infradead.org
Sat Mar 6 10:26:06 GMT 2021


adrian pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/eda836e390af2e9f08618dde8e06af851e732a47

commit eda836e390af2e9f08618dde8e06af851e732a47
Author: Ilya Lipnitskiy <ilya.lipnitskiy at gmail.com>
AuthorDate: Fri Feb 19 18:59:29 2021 -0800

    ramips: 5.10: copy patches from 5.4
    
    Strict copy, no changes made.
    
    Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy at gmail.com>
---
 ...e-Clean-up-boot_command_line-initializati.patch |  192 +++
 .../0002-MIPS-Always-define-builtin_cmdline.patch  |   44 +
 ...mory-reservation-in-bootmem_init-for-cert.patch |   45 +
 ...t_mode-to-enable-disable-the-cevt-r4k-irq.patch |   82 ++
 ...006-MIPS-ralink-add-cpu-frequency-scaling.patch |  200 +++
 ...-copy-the-commandline-from-the-devicetree.patch |   21 +
 .../0010-MIPS-add-bootargs-override-property.patch |   63 +
 .../0013-owrt-hack-fix-mt7688-cache-issue.patch    |   29 +
 ...o-not-select-illegal-access-driver-by-def.patch |   25 +
 .../0024-GPIO-add-named-gpio-exports.patch         |  165 +++
 ...0026-DT-Add-documentation-for-gpio-ralink.patch |   59 +
 ...IPS-ralink-add-gpio-driver-for-ralink-SoC.patch |  416 ++++++
 ...-Add-support-for-GPIO-as-interrupt-contro.patch |   44 +
 .../0031-uvc-add-iPassion-iP2970-support.patch     |  246 ++++
 ...0037-mtd-cfi-cmdset-0002-force-word-write.patch |   20 +
 .../0041-DT-Add-documentation-for-spi-rt2880.patch |   44 +
 ...0042-SPI-ralink-add-Ralink-SoC-spi-driver.patch |  574 ++++++++
 .../0044-i2c-MIPS-adds-ralink-I2C-driver.patch     |  507 +++++++
 ...mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch |   43 +
 .../0048-asoc-add-mt7620-support.patch             | 1046 +++++++++++++++
 ...051-serial-add-ugly-custom-baud-rate-hack.patch |   22 +
 .../0052-pwm-add-mediatek-support.patch            |  217 +++
 .../0069-awake-rt305x-dwc2-controller.patch        |   15 +
 .../ramips/patches-5.10/0070-weak_reordering.patch |   10 +
 .../ramips/patches-5.10/0098-disable_cm.patch      |   19 +
 ...621-pci-simplify-mt7621_pcie_init_virtual.patch |  133 ++
 ...mt7621-pci-enable-clock-bit-for-each-port.patch |   74 ++
 ...g-mt7621-pci-use-gpios-for-properly-reset.patch |  222 ++++
 ...t7621-pci-change-value-for-PERST_DELAY_MS.patch |   45 +
 ...621-pci-release-gpios-after-pci-initializ.patch |   76 ++
 ...621-pci-delete-no-more-needed-mt7621_rese.patch |   46 +
 ...621-pci-phy-add-mt7621_phy_rmw-to-simplif.patch |  234 ++++
 ...621-pci-fix-io-space-and-properly-set-res.patch |  131 ++
 ...621-pci-fix-register-to-set-up-virtual-br.patch |   28 +
 ...mt7621-pci-don-t-return-if-get-gpio-fails.patch |   34 +
 ...621-pci-phy-avoid-to-create-to-different-.patch |  272 ++++
 ...621-pci-use-only-two-phys-from-device-tre.patch |   42 +
 ...621-pci-change-variable-to-print-for-slot.patch |   26 +
 ...621-pci-be-sure-gpio-descriptor-is-null-o.patch |   33 +
 ...621-pci-avoid-to-poweroff-the-phy-for-slo.patch |   79 ++
 ...621-pci-delete-release-gpios-related-code.patch |   91 ++
 ...ng-mt7621-pci-use-builtin_platform_driver.patch |   29 +
 ...t7621-pci-phy-use-builtin_platform_driver.patch |   32 +
 ...-mt7621-pci-phy-re-do-xtal_mode-detection.patch |   68 +
 ...621-pci-avoid-to-set-iomem_resource-addre.patch |   35 +
 ...621-pci-properly-power-off-dual-ported-pc.patch |   65 +
 ...ing-mt7621-pci-fix-PCIe-interrupt-mapping.patch |  157 +++
 .../0122-mips-ralink-enable-zboot-support.patch    |   26 +
 .../0123-mips-ralink-manage-low-reset-lines.patch  |   45 +
 .../patches-5.10/0200-linkit_bootstrap.patch       |   97 ++
 ...-add-driver-support-for-MT7621-nand-flash.patch | 1400 ++++++++++++++++++++
 ...-add-documentation-for-mt7621-nand-driver.patch |   85 ++
 .../patches-5.10/100-mt7621-core-detect-hack.patch |   61 +
 .../ramips/patches-5.10/101-mt7621-timer.patch     |   87 ++
 .../102-mt7621-fix-cpu-clk-add-clkdev.patch        |  224 ++++
 .../patches-5.10/105-mt7621-memory-detect.patch    |  125 ++
 .../patches-5.10/110-mt7621-perfctr-fix.patch      |   15 +
 ...pio-mmio-introduce-BGPIOF_NO_SET_ON_INPUT.patch |   85 ++
 ...io-mt7621-add-BGPIOF_NO_SET_ON_INPUT-flag.patch |   33 +
 .../ramips/patches-5.10/200-add-ralink-eth.patch   |   20 +
 .../300-mt7620-export-chip-version-and-pkg.patch   |   19 +
 ...-mt7621-introduce-soc_device-initializati.patch |   89 ++
 .../patches-5.10/302-spi-nor-add-gd25q512.patch    |   14 +
 ...-net-ethernet-mediatek-support-net-labels.patch |   34 +
 .../990-NET-no-auto-carrier-off-support.patch      |   47 +
 target/linux/ramips/patches-5.10/991-at803x.patch  |  156 +++
 .../patches-5.10/999-fix-pci-init-mt7620.patch     |   21 +
 67 files changed, 8783 insertions(+)

diff --git a/target/linux/ramips/patches-5.10/0001-MIPS-cmdline-Clean-up-boot_command_line-initializati.patch b/target/linux/ramips/patches-5.10/0001-MIPS-cmdline-Clean-up-boot_command_line-initializati.patch
new file mode 100644
index 0000000000..eedc7498be
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0001-MIPS-cmdline-Clean-up-boot_command_line-initializati.patch
@@ -0,0 +1,192 @@
+From: Paul Burton <paul.burton at mips.com>
+Date: Wed, 9 Oct 2019 23:09:45 +0000
+Subject: MIPS: cmdline: Clean up boot_command_line initialization
+
+Our current code to initialize boot_command_line is a mess. Some of this
+is due to the addition of too many options over the years, and some of
+this is due to workarounds for early_init_dt_scan_chosen() performing
+actions specific to options from other architectures that probably
+shouldn't be in generic code.
+
+Clean this up by introducing a new bootcmdline_init() function that
+simplifies the initialization somewhat. The major changes are:
+
+- Because bootcmdline_init() is a function it can return early in the
+  CONFIG_CMDLINE_OVERRIDE case.
+
+- We clear boot_command_line rather than inheriting whatever
+  early_init_dt_scan_chosen() may have left us. This means we no longer
+  need to set boot_command_line to a space character in an attempt to
+  prevent early_init_dt_scan_chosen() from copying CONFIG_CMDLINE into
+  boot_command_line without us knowing about it.
+
+- Indirection via USE_PROM_CMDLINE, USE_DTB_CMDLINE, EXTEND_WITH_PROM &
+  BUILTIN_EXTEND_WITH_PROM macros is removed; they seemingly served only
+  to obfuscate the code.
+
+- The logic is cleaner, clearer & commented.
+
+Two minor drawbacks of this approach are:
+
+1) We call of_scan_flat_dt(), which means we scan through the DT again.
+   The overhead is fairly minimal & shouldn't be noticeable.
+
+2) cmdline_scan_chosen() duplicates a small amount of the logic from
+   early_init_dt_scan_chosen(). Alternatives might be to allow the
+   generic FDT code to keep & expose a copy of the arguments taken from
+   the /chosen node's bootargs property, or to introduce a function like
+   early_init_dt_scan_chosen() that retrieves them without modification
+   to handle CONFIG_CMDLINE. Neither of these sounds particularly
+   cleaner though, and this way we at least keep the extra work in
+   arch/mips.
+
+Origin: upstream, https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7784cac697351f0cc0a4bb619594c0c99348c5aa
+Signed-off-by: Paul Burton <paul.burton at mips.com>
+Cc: linux-mips at vger.kernel.org
+
+--- a/arch/mips/kernel/setup.c
++++ b/arch/mips/kernel/setup.c
+@@ -538,11 +538,88 @@ static void __init check_kernel_sections
+ 	}
+ }
+ 
+-#define USE_PROM_CMDLINE	IS_ENABLED(CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER)
+-#define USE_DTB_CMDLINE		IS_ENABLED(CONFIG_MIPS_CMDLINE_FROM_DTB)
+-#define EXTEND_WITH_PROM	IS_ENABLED(CONFIG_MIPS_CMDLINE_DTB_EXTEND)
+-#define BUILTIN_EXTEND_WITH_PROM	\
+-	IS_ENABLED(CONFIG_MIPS_CMDLINE_BUILTIN_EXTEND)
++static void __init bootcmdline_append(const char *s, size_t max)
++{
++	if (!s[0] || !max)
++		return;
++
++	if (boot_command_line[0])
++		strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
++
++	strlcat(boot_command_line, s, max);
++}
++
++static int __init bootcmdline_scan_chosen(unsigned long node, const char *uname,
++					  int depth, void *data)
++{
++	bool *dt_bootargs = data;
++	const char *p;
++	int l;
++
++	if (depth != 1 || !data ||
++	    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen at 0") != 0))
++		return 0;
++
++	p = of_get_flat_dt_prop(node, "bootargs", &l);
++	if (p != NULL && l > 0) {
++		bootcmdline_append(p, min(l, COMMAND_LINE_SIZE));
++		*dt_bootargs = true;
++	}
++
++	return 1;
++}
++
++static void __init bootcmdline_init(char **cmdline_p)
++{
++	bool dt_bootargs = false;
++
++	/*
++	 * If CMDLINE_OVERRIDE is enabled then initializing the command line is
++	 * trivial - we simply use the built-in command line unconditionally &
++	 * unmodified.
++	 */
++	if (IS_ENABLED(CONFIG_CMDLINE_OVERRIDE)) {
++		strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
++		return;
++	}
++
++	/*
++	 * If the user specified a built-in command line &
++	 * MIPS_CMDLINE_BUILTIN_EXTEND, then the built-in command line is
++	 * prepended to arguments from the bootloader or DT so we'll copy them
++	 * to the start of boot_command_line here. Otherwise, empty
++	 * boot_command_line to undo anything early_init_dt_scan_chosen() did.
++	 */
++	if (IS_ENABLED(CONFIG_MIPS_CMDLINE_BUILTIN_EXTEND))
++		strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
++	else
++		boot_command_line[0] = 0;
++
++	/*
++	 * If we're configured to take boot arguments from DT, look for those
++	 * now.
++	 */
++	if (IS_ENABLED(CONFIG_MIPS_CMDLINE_FROM_DTB))
++		of_scan_flat_dt(bootcmdline_scan_chosen, &dt_bootargs);
++
++	/*
++	 * If we didn't get any arguments from DT (regardless of whether that's
++	 * because we weren't configured to look for them, or because we looked
++	 * & found none) then we'll take arguments from the bootloader.
++	 * plat_mem_setup() should have filled arcs_cmdline with arguments from
++	 * the bootloader.
++	 */
++	if (IS_ENABLED(CONFIG_MIPS_CMDLINE_DTB_EXTEND) || !dt_bootargs)
++		bootcmdline_append(arcs_cmdline, COMMAND_LINE_SIZE);
++
++	/*
++	 * If the user specified a built-in command line & we didn't already
++	 * prepend it, we append it to boot_command_line here.
++	 */
++	if (IS_ENABLED(CONFIG_CMDLINE_BOOL) &&
++	    !IS_ENABLED(CONFIG_MIPS_CMDLINE_BUILTIN_EXTEND))
++		bootcmdline_append(builtin_cmdline, COMMAND_LINE_SIZE);
++}
+ 
+ /*
+  * arch_mem_init - initialize memory management subsystem
+@@ -570,48 +647,12 @@ static void __init arch_mem_init(char **
+ {
+ 	extern void plat_mem_setup(void);
+ 
+-	/*
+-	 * Initialize boot_command_line to an innocuous but non-empty string in
+-	 * order to prevent early_init_dt_scan_chosen() from copying
+-	 * CONFIG_CMDLINE into it without our knowledge. We handle
+-	 * CONFIG_CMDLINE ourselves below & don't want to duplicate its
+-	 * content because repeating arguments can be problematic.
+-	 */
+-	strlcpy(boot_command_line, " ", COMMAND_LINE_SIZE);
+-
+ 	/* call board setup routine */
+ 	plat_mem_setup();
+ 	memblock_set_bottom_up(true);
+ 
+-#if defined(CONFIG_CMDLINE_BOOL) && defined(CONFIG_CMDLINE_OVERRIDE)
+-	strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
+-#else
+-	if ((USE_PROM_CMDLINE && arcs_cmdline[0]) ||
+-	    (USE_DTB_CMDLINE && !boot_command_line[0]))
+-		strlcpy(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE);
+-
+-	if (EXTEND_WITH_PROM && arcs_cmdline[0]) {
+-		if (boot_command_line[0])
+-			strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+-		strlcat(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE);
+-	}
+-
+-#if defined(CONFIG_CMDLINE_BOOL)
+-	if (builtin_cmdline[0]) {
+-		if (boot_command_line[0])
+-			strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+-		strlcat(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
+-	}
+-
+-	if (BUILTIN_EXTEND_WITH_PROM && arcs_cmdline[0]) {
+-		if (boot_command_line[0])
+-			strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+-		strlcat(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE);
+-	}
+-#endif
+-#endif
++	bootcmdline_init(cmdline_p);
+ 	strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
+-
+ 	*cmdline_p = command_line;
+ 
+ 	parse_early_param();
diff --git a/target/linux/ramips/patches-5.10/0002-MIPS-Always-define-builtin_cmdline.patch b/target/linux/ramips/patches-5.10/0002-MIPS-Always-define-builtin_cmdline.patch
new file mode 100644
index 0000000000..03124d07d1
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0002-MIPS-Always-define-builtin_cmdline.patch
@@ -0,0 +1,44 @@
+From b7340422cc16c5deff100812f38114bb5ec81203 Mon Sep 17 00:00:00 2001
+From: Paul Burton <paul.burton at mips.com>
+Date: Sat, 12 Oct 2019 20:43:36 +0000
+Subject: [PATCH] MIPS: Always define builtin_cmdline
+
+Commit 7784cac69735 ("MIPS: cmdline: Clean up boot_command_line
+initialization") made use of builtin_cmdline conditional upon plain C if
+statements rather than preprocessor #ifdef's. This caused build failures
+for configurations with CONFIG_CMDLINE_BOOL=n where builtin_cmdline
+wasn't defined, for example:
+
+   arch/mips/kernel/setup.c: In function 'bootcmdline_init':
+>> arch/mips/kernel/setup.c:582:30: error: 'builtin_cmdline' undeclared
+    (first use in this function); did you mean 'builtin_driver'?
+      strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
+                                 ^~~~~~~~~~~~~~~
+                                 builtin_driver
+   arch/mips/kernel/setup.c:582:30: note: each undeclared identifier is
+    reported only once for each function it appears in
+
+Fix this by defining builtin_cmdline as an empty string in the affected
+configurations. All of the paths that use it should be optimized out
+anyway so the data itself gets optimized away too.
+
+Signed-off-by: Paul Burton <paul.burton at mips.com>
+Fixes: 7784cac69735 ("MIPS: cmdline: Clean up boot_command_line initialization")
+Reported-by: kbuild test robot <lkp at intel.com>
+Reported-by: Nathan Chancellor <natechancellor at gmail.com>
+Cc: linux-mips at vger.kernel.org
+---
+ arch/mips/kernel/setup.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/mips/kernel/setup.c
++++ b/arch/mips/kernel/setup.c
+@@ -68,6 +68,8 @@ char __initdata arcs_cmdline[COMMAND_LIN
+ 
+ #ifdef CONFIG_CMDLINE_BOOL
+ static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE;
++#else
++static const char builtin_cmdline[] __initconst = "";
+ #endif
+ 
+ /*
diff --git a/target/linux/ramips/patches-5.10/0003-MIPS-Fix-memory-reservation-in-bootmem_init-for-cert.patch b/target/linux/ramips/patches-5.10/0003-MIPS-Fix-memory-reservation-in-bootmem_init-for-cert.patch
new file mode 100644
index 0000000000..63429f49b0
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0003-MIPS-Fix-memory-reservation-in-bootmem_init-for-cert.patch
@@ -0,0 +1,45 @@
+From: Tobias Wolf <dev-NTEO at vplace.de>
+Subject: [v2] MIPS: Fix memory reservation in bootmem_init for certain non-usermem setups
+
+Commit 67a3ba25aa95 ("MIPS: Fix incorrect mem=X at Y handling") introduced a new
+issue for rt288x where "PHYS_OFFSET" is 0x0 but the calculated "ramstart" is
+not. As the prerequisite of custom memory map has been removed, this results
+in the full memory range of 0x0 - 0x8000000 to be marked as reserved for this
+platform.
+
+v2: Correctly compare that usermem is not null.
+
+This patch adds the originally intended prerequisite again.
+
+Signed-off-by: Tobias Wolf <dev-NTEO at vplace.de>
+---
+
+--- a/arch/mips/kernel/setup.c
++++ b/arch/mips/kernel/setup.c
+@@ -287,6 +287,8 @@ static unsigned long __init init_initrd(
+  * Initialize the bootmem allocator. It also setup initrd related data
+  * if needed.
+  */
++static int usermem __initdata;
++
+ #if defined(CONFIG_SGI_IP27) || (defined(CONFIG_CPU_LOONGSON3) && defined(CONFIG_NUMA))
+ 
+ static void __init bootmem_init(void)
+@@ -325,7 +327,7 @@ static void __init bootmem_init(void)
+ 	/*
+ 	 * Reserve any memory between the start of RAM and PHYS_OFFSET
+ 	 */
+-	if (ramstart > PHYS_OFFSET)
++	if (usermem && ramstart > PHYS_OFFSET)
+ 		memblock_reserve(PHYS_OFFSET, ramstart - PHYS_OFFSET);
+ 
+ 	if (PFN_UP(ramstart) > ARCH_PFN_OFFSET) {
+@@ -386,8 +388,6 @@ static void __init bootmem_init(void)
+ 
+ #endif	/* CONFIG_SGI_IP27 */
+ 
+-static int usermem __initdata;
+-
+ static int __init early_parse_mem(char *p)
+ {
+ 	phys_addr_t start, size;
diff --git a/target/linux/ramips/patches-5.10/0005-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch b/target/linux/ramips/patches-5.10/0005-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch
new file mode 100644
index 0000000000..773c74f4e9
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0005-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch
@@ -0,0 +1,82 @@
+From ce3d4a4111a5f7e6b4e74bceae5faa6ce388e8ec Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Sun, 14 Jul 2013 23:08:11 +0200
+Subject: [PATCH 05/53] MIPS: use set_mode() to enable/disable the cevt-r4k
+ irq
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ arch/mips/ralink/Kconfig |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/arch/mips/ralink/Kconfig
++++ b/arch/mips/ralink/Kconfig
+@@ -1,12 +1,17 @@
+ # SPDX-License-Identifier: GPL-2.0
+ if RALINK
+ 
++config CEVT_SYSTICK_QUIRK
++	bool
++	default n
++
+ config CLKEVT_RT3352
+ 	bool
+ 	depends on SOC_RT305X || SOC_MT7620
+ 	default y
+ 	select TIMER_OF
+ 	select CLKSRC_MMIO
++	select CEVT_SYSTICK_QUIRK
+ 
+ config RALINK_ILL_ACC
+ 	bool
+--- a/arch/mips/kernel/cevt-r4k.c
++++ b/arch/mips/kernel/cevt-r4k.c
+@@ -15,6 +15,26 @@
+ #include <asm/time.h>
+ #include <asm/cevt-r4k.h>
+ 
++static int mips_state_oneshot(struct clock_event_device *evt)
++{
++	if (!cp0_timer_irq_installed) {
++		cp0_timer_irq_installed = 1;
++		setup_irq(evt->irq, &c0_compare_irqaction);
++	}
++
++	return 0;
++}
++
++static int mips_state_shutdown(struct clock_event_device *evt)
++{
++	if (cp0_timer_irq_installed) {
++		cp0_timer_irq_installed = 0;
++		remove_irq(evt->irq, &c0_compare_irqaction);
++	}
++
++	return 0;
++}
++
+ static int mips_next_event(unsigned long delta,
+ 			   struct clock_event_device *evt)
+ {
+@@ -281,17 +301,21 @@ int r4k_clockevent_init(void)
+ 	cd->rating		= 300;
+ 	cd->irq			= irq;
+ 	cd->cpumask		= cpumask_of(cpu);
++	cd->set_state_shutdown	= mips_state_shutdown;
++	cd->set_state_oneshot	= mips_state_oneshot;
+ 	cd->set_next_event	= mips_next_event;
+ 	cd->event_handler	= mips_event_handler;
+ 
+ 	clockevents_config_and_register(cd, mips_hpt_frequency, min_delta, 0x7fffffff);
+ 
++#ifndef CONFIG_CEVT_SYSTICK_QUIRK
+ 	if (cp0_timer_irq_installed)
+ 		return 0;
+ 
+ 	cp0_timer_irq_installed = 1;
+ 
+ 	setup_irq(irq, &c0_compare_irqaction);
++#endif
+ 
+ 	return 0;
+ }
diff --git a/target/linux/ramips/patches-5.10/0006-MIPS-ralink-add-cpu-frequency-scaling.patch b/target/linux/ramips/patches-5.10/0006-MIPS-ralink-add-cpu-frequency-scaling.patch
new file mode 100644
index 0000000000..a3b8e4d250
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0006-MIPS-ralink-add-cpu-frequency-scaling.patch
@@ -0,0 +1,200 @@
+From bd30f19a006fb52bac80c6463c49dd2f4159f4ac Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Sun, 28 Jul 2013 16:26:41 +0200
+Subject: [PATCH 06/53] MIPS: ralink: add cpu frequency scaling
+
+This feature will break udelay() and cause the delay loop to have longer delays
+when the frequency is scaled causing a performance hit.
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ arch/mips/ralink/cevt-rt3352.c |   38 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+
+--- a/arch/mips/ralink/cevt-rt3352.c
++++ b/arch/mips/ralink/cevt-rt3352.c
+@@ -29,6 +29,10 @@
+ /* enable the counter */
+ #define CFG_CNT_EN		0x1
+ 
++/* mt7620 frequency scaling defines */
++#define CLK_LUT_CFG	0x40
++#define SLEEP_EN	BIT(31)
++
+ struct systick_device {
+ 	void __iomem *membase;
+ 	struct clock_event_device dev;
+@@ -36,21 +40,53 @@ struct systick_device {
+ 	int freq_scale;
+ };
+ 
++static void (*systick_freq_scaling)(struct systick_device *sdev, int status);
++
+ static int systick_set_oneshot(struct clock_event_device *evt);
+ static int systick_shutdown(struct clock_event_device *evt);
+ 
++static inline void mt7620_freq_scaling(struct systick_device *sdev, int status)
++{
++	if (sdev->freq_scale == status)
++		return;
++
++	sdev->freq_scale = status;
++
++	pr_info("%s: %s autosleep mode\n", sdev->dev.name,
++			(status) ? ("enable") : ("disable"));
++	if (status)
++		rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) | SLEEP_EN, CLK_LUT_CFG);
++	else
++		rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) & ~SLEEP_EN, CLK_LUT_CFG);
++}
++
++static inline unsigned int read_count(struct systick_device *sdev)
++{
++	return ioread32(sdev->membase + SYSTICK_COUNT);
++}
++
++static inline unsigned int read_compare(struct systick_device *sdev)
++{
++	return ioread32(sdev->membase + SYSTICK_COMPARE);
++}
++
++static inline void write_compare(struct systick_device *sdev, unsigned int val)
++{
++	iowrite32(val, sdev->membase + SYSTICK_COMPARE);
++}
++
+ static int systick_next_event(unsigned long delta,
+ 				struct clock_event_device *evt)
+ {
+ 	struct systick_device *sdev;
+-	u32 count;
++	int res;
+ 
+ 	sdev = container_of(evt, struct systick_device, dev);
+-	count = ioread32(sdev->membase + SYSTICK_COUNT);
+-	count = (count + delta) % SYSTICK_FREQ;
+-	iowrite32(count, sdev->membase + SYSTICK_COMPARE);
++	delta += read_count(sdev);
++	write_compare(sdev, delta);
++	res = ((int)(read_count(sdev) - delta) >= 0) ? -ETIME : 0;
+ 
+-	return 0;
++	return res;
+ }
+ 
+ static void systick_event_handler(struct clock_event_device *dev)
+@@ -60,20 +96,25 @@ static void systick_event_handler(struct
+ 
+ static irqreturn_t systick_interrupt(int irq, void *dev_id)
+ {
+-	struct clock_event_device *dev = (struct clock_event_device *) dev_id;
++	int ret = 0;
++	struct clock_event_device *cdev;
++	struct systick_device *sdev;
+ 
+-	dev->event_handler(dev);
++	if (read_c0_cause() & STATUSF_IP7) {
++		cdev = (struct clock_event_device *) dev_id;
++		sdev = container_of(cdev, struct systick_device, dev);
++
++		/* Clear Count/Compare Interrupt */
++		write_compare(sdev, read_compare(sdev));
++		cdev->event_handler(cdev);
++		ret = 1;
++	}
+ 
+-	return IRQ_HANDLED;
++	return IRQ_RETVAL(ret);
+ }
+ 
+ static struct systick_device systick = {
+ 	.dev = {
+-		/*
+-		 * cevt-r4k uses 300, make sure systick
+-		 * gets used if available
+-		 */
+-		.rating			= 310,
+ 		.features		= CLOCK_EVT_FEAT_ONESHOT,
+ 		.set_next_event		= systick_next_event,
+ 		.set_state_shutdown	= systick_shutdown,
+@@ -95,9 +136,15 @@ static int systick_shutdown(struct clock
+ 	sdev = container_of(evt, struct systick_device, dev);
+ 
+ 	if (sdev->irq_requested)
+-		free_irq(systick.dev.irq, &systick_irqaction);
++		remove_irq(systick.dev.irq, &systick_irqaction);
+ 	sdev->irq_requested = 0;
+-	iowrite32(0, systick.membase + SYSTICK_CONFIG);
++	iowrite32(CFG_CNT_EN, systick.membase + SYSTICK_CONFIG);
++
++	if (systick_freq_scaling)
++		systick_freq_scaling(sdev, 0);
++
++	if (systick_freq_scaling)
++		systick_freq_scaling(sdev, 1);
+ 
+ 	return 0;
+ }
+@@ -117,34 +164,48 @@ static int systick_set_oneshot(struct cl
+ 	return 0;
+ }
+ 
++static const struct of_device_id systick_match[] = {
++	{ .compatible = "ralink,mt7620a-systick", .data = mt7620_freq_scaling},
++	{},
++};
++
+ static int __init ralink_systick_init(struct device_node *np)
+ {
++	const struct of_device_id *match;
++	int rating = 200;
+ 	int ret;
+ 
+ 	systick.membase = of_iomap(np, 0);
+ 	if (!systick.membase)
+ 		return -ENXIO;
+ 
+-	systick_irqaction.name = np->name;
+-	systick.dev.name = np->name;
+-	clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60);
+-	systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev);
+-	systick.dev.max_delta_ticks = 0x7fff;
+-	systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev);
+-	systick.dev.min_delta_ticks = 0x3;
++	match = of_match_node(systick_match, np);
++	if (match) {
++		systick_freq_scaling = match->data;
++		/*
++		 * cevt-r4k uses 300, make sure systick
++		 * gets used if available
++		 */
++		rating = 310;
++	}
++
++	/* enable counter than register clock source */
++	iowrite32(CFG_CNT_EN, systick.membase + SYSTICK_CONFIG);
++	clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
++			SYSTICK_FREQ, rating, 16, clocksource_mmio_readl_up);
++
++	/* register clock event */
+ 	systick.dev.irq = irq_of_parse_and_map(np, 0);
+ 	if (!systick.dev.irq) {
+ 		pr_err("%pOFn: request_irq failed", np);
+ 		return -EINVAL;
+ 	}
+ 
+-	ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
+-				    SYSTICK_FREQ, 301, 16,
+-				    clocksource_mmio_readl_up);
+-	if (ret)
+-		return ret;
+-
+-	clockevents_register_device(&systick.dev);
++	systick_irqaction.name = np->name;
++	systick.dev.name = np->name;
++	systick.dev.rating = rating;
++	systick.dev.cpumask = cpumask_of(0);
++	clockevents_config_and_register(&systick.dev, SYSTICK_FREQ, 0x3, 0x7fff);
+ 
+ 	pr_info("%pOFn: running - mult: %d, shift: %d\n",
+ 			np, systick.dev.mult, systick.dev.shift);
diff --git a/target/linux/ramips/patches-5.10/0007-MIPS-ralink-copy-the-commandline-from-the-devicetree.patch b/target/linux/ramips/patches-5.10/0007-MIPS-ralink-copy-the-commandline-from-the-devicetree.patch
new file mode 100644
index 0000000000..54af571d85
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0007-MIPS-ralink-copy-the-commandline-from-the-devicetree.patch
@@ -0,0 +1,21 @@
+From 67b7bff0fd364c194e653f69baa623ba2141bd4c Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Mon, 4 Aug 2014 18:46:02 +0200
+Subject: [PATCH 07/53] MIPS: ralink: copy the commandline from the devicetree
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ arch/mips/ralink/of.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/mips/ralink/of.c
++++ b/arch/mips/ralink/of.c
+@@ -80,6 +80,8 @@ void __init plat_mem_setup(void)
+ 
+ 	__dt_setup_arch(dtb);
+ 
++	strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
++
+ 	of_scan_flat_dt(early_init_dt_find_memory, NULL);
+ 	if (memory_dtb)
+ 		of_scan_flat_dt(early_init_dt_scan_memory, NULL);
diff --git a/target/linux/ramips/patches-5.10/0010-MIPS-add-bootargs-override-property.patch b/target/linux/ramips/patches-5.10/0010-MIPS-add-bootargs-override-property.patch
new file mode 100644
index 0000000000..c19a0fb480
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0010-MIPS-add-bootargs-override-property.patch
@@ -0,0 +1,63 @@
+From f15d27f9c90ede4b16eb37f9ae573ef81c2b6996 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail at david-bauer.net>
+Date: Thu, 31 Dec 2020 18:49:12 +0100
+Subject: [PATCH] MIPS: add bootargs-override property
+
+Add support for the bootargs-override property to the chosen node
+similar to the one used on ipq806x or mpc85xx.
+
+This is necessary, as the U-Boot used on some boards, notably the
+Ubiquiti UniFi 6 Lite, overwrite the bootargs property of the chosen
+node leading to a kernel panic when loading OpenWrt.
+
+Signed-off-by: David Bauer <mail at david-bauer.net>
+---
+ arch/mips/kernel/setup.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+
+--- a/arch/mips/kernel/setup.c
++++ b/arch/mips/kernel/setup.c
+@@ -571,8 +571,28 @@ static int __init bootcmdline_scan_chose
+ 	return 1;
+ }
+ 
++static int __init bootcmdline_scan_chosen_override(unsigned long node, const char *uname,
++						   int depth, void *data)
++{
++	bool *dt_bootargs = data;
++	const char *p;
++	int l;
++
++	if (depth != 1 || !data || strcmp(uname, "chosen") != 0)
++		return 0;
++
++	p = of_get_flat_dt_prop(node, "bootargs-override", &l);
++	if (p != NULL && l > 0) {
++		strlcpy(boot_command_line, p, COMMAND_LINE_SIZE);
++		*dt_bootargs = true;
++	}
++
++	return 1;
++}
++
+ static void __init bootcmdline_init(char **cmdline_p)
+ {
++	bool dt_bootargs_override = false;
+ 	bool dt_bootargs = false;
+ 
+ 	/*
+@@ -586,6 +606,14 @@ static void __init bootcmdline_init(char
+ 	}
+ 
+ 	/*
++	 * If bootargs-override in the chosen node is set, use this as the
++	 * command line
++	 */
++	of_scan_flat_dt(bootcmdline_scan_chosen_override, &dt_bootargs_override);
++	if (dt_bootargs_override)
++		return;
++
++	/*
+ 	 * If the user specified a built-in command line &
+ 	 * MIPS_CMDLINE_BUILTIN_EXTEND, then the built-in command line is
+ 	 * prepended to arguments from the bootloader or DT so we'll copy them
diff --git a/target/linux/ramips/patches-5.10/0013-owrt-hack-fix-mt7688-cache-issue.patch b/target/linux/ramips/patches-5.10/0013-owrt-hack-fix-mt7688-cache-issue.patch
new file mode 100644
index 0000000000..bedea14506
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0013-owrt-hack-fix-mt7688-cache-issue.patch
@@ -0,0 +1,29 @@
+From 5ede027f6c4a57ed25da872420508b7f1168b36b Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Mon, 7 Dec 2015 17:15:32 +0100
+Subject: [PATCH 13/53] owrt: hack: fix mt7688 cache issue
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ arch/mips/kernel/setup.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/kernel/setup.c
++++ b/arch/mips/kernel/setup.c
+@@ -723,8 +723,6 @@ static void __init arch_mem_init(char **
+ 		memblock_reserve(crashk_res.start,
+ 				 crashk_res.end - crashk_res.start + 1);
+ #endif
+-	device_tree_init();
+-
+ 	/*
+ 	 * In order to reduce the possibility of kernel panic when failed to
+ 	 * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate
+@@ -841,6 +839,7 @@ void __init setup_arch(char **cmdline_p)
+ 
+ 	cpu_cache_init();
+ 	paging_init();
++	device_tree_init();
+ }
+ 
+ unsigned long kernelsp[NR_CPUS];
diff --git a/target/linux/ramips/patches-5.10/0015-arch-mips-do-not-select-illegal-access-driver-by-def.patch b/target/linux/ramips/patches-5.10/0015-arch-mips-do-not-select-illegal-access-driver-by-def.patch
new file mode 100644
index 0000000000..1dc54ccf23
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0015-arch-mips-do-not-select-illegal-access-driver-by-def.patch
@@ -0,0 +1,25 @@
+From 9e6ce539092a1dd605a20bf73c655a9de58d8641 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Mon, 7 Dec 2015 17:18:05 +0100
+Subject: [PATCH 15/53] arch: mips: do not select illegal access driver by
+ default
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ arch/mips/ralink/Kconfig |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/mips/ralink/Kconfig
++++ b/arch/mips/ralink/Kconfig
+@@ -14,9 +14,9 @@ config CLKEVT_RT3352
+ 	select CEVT_SYSTICK_QUIRK
+ 
+ config RALINK_ILL_ACC
+-	bool
++	bool "illegal access irq"
+ 	depends on SOC_RT305X
+-	default y
++	default n
+ 
+ config IRQ_INTC
+ 	bool
diff --git a/target/linux/ramips/patches-5.10/0024-GPIO-add-named-gpio-exports.patch b/target/linux/ramips/patches-5.10/0024-GPIO-add-named-gpio-exports.patch
new file mode 100644
index 0000000000..d4ea379159
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0024-GPIO-add-named-gpio-exports.patch
@@ -0,0 +1,165 @@
+From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Tue, 12 Aug 2014 20:49:27 +0200
+Subject: [PATCH 24/53] GPIO: add named gpio exports
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ drivers/gpio/gpiolib-of.c     |   68 +++++++++++++++++++++++++++++++++++++++++
+ drivers/gpio/gpiolib-sysfs.c  |   10 +++++-
+ include/asm-generic/gpio.h    |    6 ++++
+ include/linux/gpio/consumer.h |    8 +++++
+ 4 files changed, 91 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpio/gpiolib-of.c
++++ b/drivers/gpio/gpiolib-of.c
+@@ -19,6 +19,8 @@
+ #include <linux/pinctrl/pinctrl.h>
+ #include <linux/slab.h>
+ #include <linux/gpio/machine.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
+ 
+ #include "gpiolib.h"
+ #include "gpiolib-of.h"
+@@ -915,3 +917,68 @@ void of_gpiochip_remove(struct gpio_chip
+ {
+ 	of_node_put(chip->of_node);
+ }
++
++static struct of_device_id gpio_export_ids[] = {
++	{ .compatible = "gpio-export" },
++	{ /* sentinel */ }
++};
++
++static int of_gpio_export_probe(struct platform_device *pdev)
++{
++	struct device_node *np = pdev->dev.of_node;
++	struct device_node *cnp;
++	u32 val;
++	int nb = 0;
++
++	for_each_child_of_node(np, cnp) {
++		const char *name = NULL;
++		int gpio;
++		bool dmc;
++		int max_gpio = 1;
++		int i;
++
++		of_property_read_string(cnp, "gpio-export,name", &name);
++
++		if (!name)
++			max_gpio = of_gpio_count(cnp);
++
++		for (i = 0; i < max_gpio; i++) {
++			unsigned flags = 0;
++			enum of_gpio_flags of_flags;
++
++			gpio = of_get_gpio_flags(cnp, i, &of_flags);
++			if (!gpio_is_valid(gpio))
++				return gpio;
++
++			if (of_flags == OF_GPIO_ACTIVE_LOW)
++				flags |= GPIOF_ACTIVE_LOW;
++
++			if (!of_property_read_u32(cnp, "gpio-export,output", &val))
++				flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
++			else
++				flags |= GPIOF_IN;
++
++			if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
++				continue;
++
++			dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
++			gpio_export_with_name(gpio, dmc, name);
++			nb++;
++		}
++	}
++
++	dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
++
++	return 0;
++}
++
++static struct platform_driver gpio_export_driver = {
++	.driver		= {
++		.name		= "gpio-export",
++		.owner	= THIS_MODULE,
++		.of_match_table	= of_match_ptr(gpio_export_ids),
++	},
++	.probe		= of_gpio_export_probe,
++};
++
++module_platform_driver(gpio_export_driver);
+--- a/drivers/gpio/gpiolib-sysfs.c
++++ b/drivers/gpio/gpiolib-sysfs.c
+@@ -563,7 +563,7 @@ static struct class gpio_class = {
+  *
+  * Returns zero on success, else an error.
+  */
+-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
+ {
+ 	struct gpio_chip	*chip;
+ 	struct gpio_device	*gdev;
+@@ -625,6 +625,8 @@ int gpiod_export(struct gpio_desc *desc,
+ 	offset = gpio_chip_hwgpio(desc);
+ 	if (chip->names && chip->names[offset])
+ 		ioname = chip->names[offset];
++	if (name)
++		ioname = name;
+ 
+ 	dev = device_create_with_groups(&gpio_class, &gdev->dev,
+ 					MKDEV(0, 0), data, gpio_groups,
+@@ -646,6 +648,12 @@ err_unlock:
+ 	gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ 	return status;
+ }
++EXPORT_SYMBOL_GPL(__gpiod_export);
++
++int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++{
++	return __gpiod_export(desc, direction_may_change, NULL);
++}
+ EXPORT_SYMBOL_GPL(gpiod_export);
+ 
+ static int match_export(struct device *dev, const void *desc)
+--- a/include/asm-generic/gpio.h
++++ b/include/asm-generic/gpio.h
+@@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g
+ 	return gpiod_export(gpio_to_desc(gpio), direction_may_change);
+ }
+ 
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
++{
++	return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
++}
++
+ static inline int gpio_export_link(struct device *dev, const char *name,
+ 				   unsigned gpio)
+ {
+--- a/include/linux/gpio/consumer.h
++++ b/include/linux/gpio/consumer.h
+@@ -668,6 +668,7 @@ static inline void devm_acpi_dev_remove_
+ 
+ #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
+ 
++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
+ int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
+ int gpiod_export_link(struct device *dev, const char *name,
+ 		      struct gpio_desc *desc);
+@@ -675,6 +676,13 @@ void gpiod_unexport(struct gpio_desc *de
+ 
+ #else  /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
+ 
++static inline int _gpiod_export(struct gpio_desc *desc,
++			       bool direction_may_change,
++			       const char *name)
++{
++	return -ENOSYS;
++}
++
+ static inline int gpiod_export(struct gpio_desc *desc,
+ 			       bool direction_may_change)
+ {
diff --git a/target/linux/ramips/patches-5.10/0026-DT-Add-documentation-for-gpio-ralink.patch b/target/linux/ramips/patches-5.10/0026-DT-Add-documentation-for-gpio-ralink.patch
new file mode 100644
index 0000000000..7d5f98f647
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0026-DT-Add-documentation-for-gpio-ralink.patch
@@ -0,0 +1,59 @@
+From d410e5478c622c01fcf31427533df5f433df9146 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Sun, 28 Jul 2013 19:45:30 +0200
+Subject: [PATCH 26/53] DT: Add documentation for gpio-ralink
+
+Describe gpio-ralink binding.
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+Cc: linux-mips at linux-mips.org
+Cc: devicetree at vger.kernel.org
+Cc: linux-gpio at vger.kernel.org
+---
+ .../devicetree/bindings/gpio/gpio-ralink.txt       |   40 ++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/gpio/gpio-ralink.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/gpio/gpio-ralink.txt
+@@ -0,0 +1,40 @@
++Ralink SoC GPIO controller bindings
++
++Required properties:
++- compatible:
++  - "ralink,rt2880-gpio" for Ralink controllers
++- #gpio-cells : Should be two.
++  - first cell is the pin number
++  - second cell is used to specify optional parameters (unused)
++- gpio-controller : Marks the device node as a GPIO controller
++- reg : Physical base address and length of the controller's registers
++- interrupt-parent: phandle to the INTC device node
++- interrupts : Specify the INTC interrupt number
++- ralink,num-gpios : Specify the number of GPIOs
++- ralink,register-map : The register layout depends on the GPIO bank and actual
++		SoC type. Register offsets need to be in this order.
++		[ INT, EDGE, RENA, FENA, DATA, DIR, POL, SET, RESET, TOGGLE ]
++
++Optional properties:
++- ralink,gpio-base : Specify the GPIO chips base number
++
++Example:
++
++	gpio0: gpio at 600 {
++		compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio";
++
++		#gpio-cells = <2>;
++		gpio-controller;
++
++		reg = <0x600 0x34>;
++
++		interrupt-parent = <&intc>;
++		interrupts = <6>;
++
++		ralink,gpio-base = <0>;
++		ralink,num-gpios = <24>;
++		ralink,register-map = [ 00 04 08 0c
++				20 24 28 2c
++				30 34 ];
++
++	};
diff --git a/target/linux/ramips/patches-5.10/0027-GPIO-MIPS-ralink-add-gpio-driver-for-ralink-SoC.patch b/target/linux/ramips/patches-5.10/0027-GPIO-MIPS-ralink-add-gpio-driver-for-ralink-SoC.patch
new file mode 100644
index 0000000000..eae507bcd7
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0027-GPIO-MIPS-ralink-add-gpio-driver-for-ralink-SoC.patch
@@ -0,0 +1,416 @@
+From 69fdd2c4f937796b934e89c33acde9d082e27bfd Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Mon, 4 Aug 2014 20:36:29 +0200
+Subject: [PATCH 27/53] GPIO: MIPS: ralink: add gpio driver for ralink SoC
+
+Add gpio driver for Ralink SoC. This driver makes the gpio core on
+RT2880, RT305x, rt3352, rt3662, rt3883, rt5350 and mt7620 work.
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+Cc: linux-mips at linux-mips.org
+Cc: linux-gpio at vger.kernel.org
+---
+ arch/mips/include/asm/mach-ralink/gpio.h |   24 ++
+ drivers/gpio/Kconfig                     |    6 +
+ drivers/gpio/Makefile                    |    1 +
+ drivers/gpio/gpio-ralink.c               |  355 ++++++++++++++++++++++++++++++
+ 4 files changed, 386 insertions(+)
+ create mode 100644 arch/mips/include/asm/mach-ralink/gpio.h
+ create mode 100644 drivers/gpio/gpio-ralink.c
+
+--- /dev/null
++++ b/arch/mips/include/asm/mach-ralink/gpio.h
+@@ -0,0 +1,24 @@
++/*
++ *  Ralink SoC GPIO API support
++ *
++ *  Copyright (C) 2008-2009 Gabor Juhos <juhosg at openwrt.org>
++ *  Copyright (C) 2008 Imre Kaloz <kaloz at openwrt.org>
++ *
++ *  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.
++ *
++ */
++
++#ifndef __ASM_MACH_RALINK_GPIO_H
++#define __ASM_MACH_RALINK_GPIO_H
++
++#define ARCH_NR_GPIOS	128
++#include <asm-generic/gpio.h>
++
++#define gpio_get_value	__gpio_get_value
++#define gpio_set_value	__gpio_set_value
++#define gpio_cansleep	__gpio_cansleep
++#define gpio_to_irq	__gpio_to_irq
++
++#endif /* __ASM_MACH_RALINK_GPIO_H */
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -471,6 +471,12 @@ config GPIO_SNPS_CREG
+ 	  where only several fields in register belong to GPIO lines and
+ 	  each GPIO line owns a field with different length and on/off value.
+ 
++config GPIO_RALINK
++	bool "Ralink GPIO Support"
++	depends on RALINK
++	help
++	  Say yes here to support the Ralink SoC GPIO device
++
+ config GPIO_SPEAR_SPICS
+ 	bool "ST SPEAr13xx SPI Chip Select as GPIO support"
+ 	depends on PLAT_SPEAR
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -112,6 +112,7 @@ obj-$(CONFIG_GPIO_PISOSR)		+= gpio-pisos
+ obj-$(CONFIG_GPIO_PL061)		+= gpio-pl061.o
+ obj-$(CONFIG_GPIO_PMIC_EIC_SPRD)	+= gpio-pmic-eic-sprd.o
+ obj-$(CONFIG_GPIO_PXA)			+= gpio-pxa.o
++obj-$(CONFIG_GPIO_RALINK)		+= gpio-ralink.o
+ obj-$(CONFIG_GPIO_RASPBERRYPI_EXP)	+= gpio-raspberrypi-exp.o
+ obj-$(CONFIG_GPIO_RC5T583)		+= gpio-rc5t583.o
+ obj-$(CONFIG_GPIO_RCAR)			+= gpio-rcar.o
+--- /dev/null
++++ b/drivers/gpio/gpio-ralink.c
+@@ -0,0 +1,341 @@
++/*
++ * 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.
++ *
++ * Copyright (C) 2009-2011 Gabor Juhos <juhosg at openwrt.org>
++ * Copyright (C) 2013 John Crispin <blogic at openwrt.org>
++ */
++
++#include <linux/module.h>
++#include <linux/io.h>
++#include <linux/gpio.h>
++#include <linux/spinlock.h>
++#include <linux/platform_device.h>
++#include <linux/of_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/interrupt.h>
++
++enum ralink_gpio_reg {
++	GPIO_REG_INT = 0,
++	GPIO_REG_EDGE,
++	GPIO_REG_RENA,
++	GPIO_REG_FENA,
++	GPIO_REG_DATA,
++	GPIO_REG_DIR,
++	GPIO_REG_POL,
++	GPIO_REG_SET,
++	GPIO_REG_RESET,
++	GPIO_REG_TOGGLE,
++	GPIO_REG_MAX
++};
++
++struct ralink_gpio_chip {
++	struct gpio_chip chip;
++	u8 regs[GPIO_REG_MAX];
++
++	spinlock_t lock;
++	void __iomem *membase;
++	struct irq_domain *domain;
++	int irq;
++
++	u32 rising;
++	u32 falling;
++};
++
++#define MAP_MAX	4
++static struct irq_domain *irq_map[MAP_MAX];
++static int irq_map_count;
++static atomic_t irq_refcount = ATOMIC_INIT(0);
++
++static inline struct ralink_gpio_chip *to_ralink_gpio(struct gpio_chip *chip)
++{
++	struct ralink_gpio_chip *rg;
++
++	rg = container_of(chip, struct ralink_gpio_chip, chip);
++
++	return rg;
++}
++
++static inline void rt_gpio_w32(struct ralink_gpio_chip *rg, u8 reg, u32 val)
++{
++	iowrite32(val, rg->membase + rg->regs[reg]);
++}
++
++static inline u32 rt_gpio_r32(struct ralink_gpio_chip *rg, u8 reg)
++{
++	return ioread32(rg->membase + rg->regs[reg]);
++}
++
++static void ralink_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
++{
++	struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
++
++	rt_gpio_w32(rg, (value) ? GPIO_REG_SET : GPIO_REG_RESET, BIT(offset));
++}
++
++static int ralink_gpio_get(struct gpio_chip *chip, unsigned offset)
++{
++	struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
++
++	return !!(rt_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset));
++}
++
++static int ralink_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
++{
++	struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
++	unsigned long flags;
++	u32 t;
++
++	spin_lock_irqsave(&rg->lock, flags);
++	t = rt_gpio_r32(rg, GPIO_REG_DIR);
++	t &= ~BIT(offset);
++	rt_gpio_w32(rg, GPIO_REG_DIR, t);
++	spin_unlock_irqrestore(&rg->lock, flags);
++
++	return 0;
++}
++
++static int ralink_gpio_direction_output(struct gpio_chip *chip,
++					unsigned offset, int value)
++{
++	struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
++	unsigned long flags;
++	u32 t;
++
++	spin_lock_irqsave(&rg->lock, flags);
++	ralink_gpio_set(chip, offset, value);
++	t = rt_gpio_r32(rg, GPIO_REG_DIR);
++	t |= BIT(offset);
++	rt_gpio_w32(rg, GPIO_REG_DIR, t);
++	spin_unlock_irqrestore(&rg->lock, flags);
++
++	return 0;
++}
++
++static int ralink_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
++{
++	struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
++
++	if (rg->irq < 1)
++		return -1;
++
++	return irq_create_mapping(rg->domain, pin);
++}
++
++static void ralink_gpio_irq_handler(struct irq_desc *desc)
++{
++	int i;
++
++	for (i = 0; i < irq_map_count; i++) {
++		struct irq_domain *domain = irq_map[i];
++		struct ralink_gpio_chip *rg;
++		unsigned long pending;
++		int bit;
++
++		rg = (struct ralink_gpio_chip *) domain->host_data;
++		pending = rt_gpio_r32(rg, GPIO_REG_INT);
++
++		for_each_set_bit(bit, &pending, rg->chip.ngpio) {
++			u32 map = irq_find_mapping(domain, bit);
++			generic_handle_irq(map);
++			rt_gpio_w32(rg, GPIO_REG_INT, BIT(bit));
++		}
++	}
++}
++
++static void ralink_gpio_irq_unmask(struct irq_data *d)
++{
++	struct ralink_gpio_chip *rg;
++	unsigned long flags;
++	u32 rise, fall;
++
++	rg = (struct ralink_gpio_chip *) d->domain->host_data;
++	rise = rt_gpio_r32(rg, GPIO_REG_RENA);
++	fall = rt_gpio_r32(rg, GPIO_REG_FENA);
++
++	spin_lock_irqsave(&rg->lock, flags);
++	rt_gpio_w32(rg, GPIO_REG_RENA, rise | (BIT(d->hwirq) & rg->rising));
++	rt_gpio_w32(rg, GPIO_REG_FENA, fall | (BIT(d->hwirq) & rg->falling));
++	spin_unlock_irqrestore(&rg->lock, flags);
++}
++
++static void ralink_gpio_irq_mask(struct irq_data *d)
++{
++	struct ralink_gpio_chip *rg;
++	unsigned long flags;
++	u32 rise, fall;
++
++	rg = (struct ralink_gpio_chip *) d->domain->host_data;
++	rise = rt_gpio_r32(rg, GPIO_REG_RENA);
++	fall = rt_gpio_r32(rg, GPIO_REG_FENA);
++
++	spin_lock_irqsave(&rg->lock, flags);
++	rt_gpio_w32(rg, GPIO_REG_FENA, fall & ~BIT(d->hwirq));
++	rt_gpio_w32(rg, GPIO_REG_RENA, rise & ~BIT(d->hwirq));
++	spin_unlock_irqrestore(&rg->lock, flags);
++}
++
++static int ralink_gpio_irq_type(struct irq_data *d, unsigned int type)
++{
++	struct ralink_gpio_chip *rg;
++	u32 mask = BIT(d->hwirq);
++
++	rg = (struct ralink_gpio_chip *) d->domain->host_data;
++
++	if (type == IRQ_TYPE_PROBE) {
++		if ((rg->rising | rg->falling) & mask)
++			return 0;
++
++		type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
++	}
++
++	if (type & IRQ_TYPE_EDGE_RISING)
++		rg->rising |= mask;
++	else
++		rg->rising &= ~mask;
++
++	if (type & IRQ_TYPE_EDGE_FALLING)
++		rg->falling |= mask;
++	else
++		rg->falling &= ~mask;
++
++	return 0;
++}
++
++static struct irq_chip ralink_gpio_irq_chip = {
++	.name		= "GPIO",
++	.irq_unmask	= ralink_gpio_irq_unmask,
++	.irq_mask	= ralink_gpio_irq_mask,
++	.irq_mask_ack	= ralink_gpio_irq_mask,
++	.irq_set_type	= ralink_gpio_irq_type,
++};
++
++static int gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
++{
++	irq_set_chip_and_handler(irq, &ralink_gpio_irq_chip, handle_level_irq);
++	irq_set_handler_data(irq, d);
++
++	return 0;
++}
++
++static const struct irq_domain_ops irq_domain_ops = {
++	.xlate = irq_domain_xlate_onecell,
++	.map = gpio_map,
++};
++
++static void ralink_gpio_irq_init(struct device_node *np,
++				 struct ralink_gpio_chip *rg)
++{
++	if (irq_map_count >= MAP_MAX)
++		return;
++
++	rg->irq = irq_of_parse_and_map(np, 0);
++	if (!rg->irq)
++		return;
++
++	rg->domain = irq_domain_add_linear(np, rg->chip.ngpio,
++					   &irq_domain_ops, rg);
++	if (!rg->domain) {
++		dev_err(rg->chip.parent, "irq_domain_add_linear failed\n");
++		return;
++	}
++
++	irq_map[irq_map_count++] = rg->domain;
++
++	rt_gpio_w32(rg, GPIO_REG_RENA, 0x0);
++	rt_gpio_w32(rg, GPIO_REG_FENA, 0x0);
++
++	if (!atomic_read(&irq_refcount))
++		irq_set_chained_handler(rg->irq, ralink_gpio_irq_handler);
++	atomic_inc(&irq_refcount);
++
++	dev_info(rg->chip.parent, "registering %d irq handlers\n", rg->chip.ngpio);
++}
++
++static int ralink_gpio_probe(struct platform_device *pdev)
++{
++	struct device_node *np = pdev->dev.of_node;
++	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	struct ralink_gpio_chip *rg;
++	const __be32 *ngpio, *gpiobase;
++
++	if (!res) {
++		dev_err(&pdev->dev, "failed to find resource\n");
++		return -ENOMEM;
++	}
++
++	rg = devm_kzalloc(&pdev->dev,
++			sizeof(struct ralink_gpio_chip), GFP_KERNEL);
++	if (!rg)
++		return -ENOMEM;
++
++	rg->membase = devm_ioremap_resource(&pdev->dev, res);
++	if (!rg->membase) {
++		dev_err(&pdev->dev, "cannot remap I/O memory region\n");
++		return -ENOMEM;
++	}
++
++	if (of_property_read_u8_array(np, "ralink,register-map",
++			rg->regs, GPIO_REG_MAX)) {
++		dev_err(&pdev->dev, "failed to read register definition\n");
++		return -EINVAL;
++	}
++
++	ngpio = of_get_property(np, "ralink,num-gpios", NULL);
++	if (!ngpio) {
++		dev_err(&pdev->dev, "failed to read number of pins\n");
++		return -EINVAL;
++	}
++
++	gpiobase = of_get_property(np, "ralink,gpio-base", NULL);
++	if (gpiobase)
++		rg->chip.base = be32_to_cpu(*gpiobase);
++	else
++		rg->chip.base = -1;
++
++	spin_lock_init(&rg->lock);
++
++	rg->chip.parent = &pdev->dev;
++	rg->chip.label = dev_name(&pdev->dev);
++	rg->chip.of_node = np;
++	rg->chip.ngpio = be32_to_cpu(*ngpio);
++	rg->chip.direction_input = ralink_gpio_direction_input;
++	rg->chip.direction_output = ralink_gpio_direction_output;
++	rg->chip.get = ralink_gpio_get;
++	rg->chip.set = ralink_gpio_set;
++	rg->chip.request = gpiochip_generic_request;
++	rg->chip.to_irq = ralink_gpio_to_irq;
++	rg->chip.free = gpiochip_generic_free;
++
++	/* set polarity to low for all lines */
++	rt_gpio_w32(rg, GPIO_REG_POL, 0);
++
++	dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio);
++
++	ralink_gpio_irq_init(np, rg);
++
++	return gpiochip_add(&rg->chip);
++}
++
++static const struct of_device_id ralink_gpio_match[] = {
++	{ .compatible = "ralink,rt2880-gpio" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, ralink_gpio_match);
++
++static struct platform_driver ralink_gpio_driver = {
++	.probe = ralink_gpio_probe,
++	.driver = {
++		.name = "rt2880_gpio",
++		.owner = THIS_MODULE,
++		.of_match_table = ralink_gpio_match,
++	},
++};
++
++static int __init ralink_gpio_init(void)
++{
++	return platform_driver_register(&ralink_gpio_driver);
++}
++
++subsys_initcall(ralink_gpio_init);
diff --git a/target/linux/ramips/patches-5.10/0029-gpio-ralink-Add-support-for-GPIO-as-interrupt-contro.patch b/target/linux/ramips/patches-5.10/0029-gpio-ralink-Add-support-for-GPIO-as-interrupt-contro.patch
new file mode 100644
index 0000000000..8520ce32ff
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0029-gpio-ralink-Add-support-for-GPIO-as-interrupt-contro.patch
@@ -0,0 +1,44 @@
+From 57fa7f2f4ef6f78ce1d30509c0d111aa3791b524 Mon Sep 17 00:00:00 2001
+From: Daniel Santos <daniel.santos at pobox.com>
+Date: Sun, 4 Nov 2018 20:24:32 -0600
+Subject: gpio-ralink: Add support for GPIO as interrupt-controller
+
+Signed-off-by: Daniel Santos <daniel.santos at pobox.com>
+---
+ Documentation/devicetree/bindings/gpio/gpio-ralink.txt | 6 ++++++
+ drivers/gpio/gpio-ralink.c                             | 2 +-
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/gpio/gpio-ralink.txt
++++ b/Documentation/devicetree/bindings/gpio/gpio-ralink.txt
+@@ -17,6 +17,9 @@ Required properties:
+ 
+ Optional properties:
+ - ralink,gpio-base : Specify the GPIO chips base number
++- interrupt-controller : marks this as an interrupt controller
++- #interrupt-cells : a standard two-cell interrupt flag, see
++  interrupt-controller/interrupts.txt
+ 
+ Example:
+ 
+@@ -28,6 +31,9 @@ Example:
+ 
+ 		reg = <0x600 0x34>;
+ 
++		interrupt-controller;
++		#interrupt-cells = <2>;
++
+ 		interrupt-parent = <&intc>;
+ 		interrupts = <6>;
+ 
+--- a/drivers/gpio/gpio-ralink.c
++++ b/drivers/gpio/gpio-ralink.c
+@@ -220,7 +220,7 @@ static int gpio_map(struct irq_domain *d
+ }
+ 
+ static const struct irq_domain_ops irq_domain_ops = {
+-	.xlate = irq_domain_xlate_onecell,
++	.xlate = irq_domain_xlate_twocell,
+ 	.map = gpio_map,
+ };
+ 
diff --git a/target/linux/ramips/patches-5.10/0031-uvc-add-iPassion-iP2970-support.patch b/target/linux/ramips/patches-5.10/0031-uvc-add-iPassion-iP2970-support.patch
new file mode 100644
index 0000000000..084b655483
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0031-uvc-add-iPassion-iP2970-support.patch
@@ -0,0 +1,246 @@
+From 975e76214cd2516eb6cfff4c3eec581872645e88 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Thu, 19 Sep 2013 01:50:59 +0200
+Subject: [PATCH 31/53] uvc: add iPassion iP2970 support
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ drivers/media/usb/uvc/uvc_driver.c |   12 +++
+ drivers/media/usb/uvc/uvc_status.c |    2 +
+ drivers/media/usb/uvc/uvc_video.c  |  147 ++++++++++++++++++++++++++++++++++++
+ drivers/media/usb/uvc/uvcvideo.h   |    5 +-
+ 4 files changed, 165 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/usb/uvc/uvc_driver.c
++++ b/drivers/media/usb/uvc/uvc_driver.c
+@@ -2908,6 +2908,18 @@ static const struct usb_device_id uvc_id
+ 	  .bInterfaceSubClass	= 1,
+ 	  .bInterfaceProtocol	= 0,
+ 	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
++	/* iPassion iP2970 */
++	{ .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
++				| USB_DEVICE_ID_MATCH_INT_INFO,
++	 .idVendor		= 0x1B3B,
++	 .idProduct		= 0x2970,
++	 .bInterfaceClass	= USB_CLASS_VIDEO,
++	 .bInterfaceSubClass	= 1,
++	 .bInterfaceProtocol	= 0,
++	 .driver_info		= UVC_QUIRK_PROBE_MINMAX
++				| UVC_QUIRK_STREAM_NO_FID
++				| UVC_QUIRK_MOTION
++				| UVC_QUIRK_SINGLE_ISO },
+ 	/* Generic USB Video Class */
+ 	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) },
+ 	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) },
+--- a/drivers/media/usb/uvc/uvc_status.c
++++ b/drivers/media/usb/uvc/uvc_status.c
+@@ -223,6 +223,7 @@ static void uvc_status_complete(struct u
+ 			if (uvc_event_control(urb, status, len))
+ 				/* The URB will be resubmitted in work context. */
+ 				return;
++			dev->motion = 1;
+ 			break;
+ 		}
+ 
+@@ -271,6 +272,7 @@ int uvc_status_init(struct uvc_device *d
+ 	}
+ 
+ 	pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
++	dev->motion = 0;
+ 
+ 	/* For high-speed interrupt endpoints, the bInterval value is used as
+ 	 * an exponent of two. Some developers forgot about it.
+--- a/drivers/media/usb/uvc/uvc_video.c
++++ b/drivers/media/usb/uvc/uvc_video.c
+@@ -16,6 +16,11 @@
+ #include <linux/wait.h>
+ #include <linux/atomic.h>
+ #include <asm/unaligned.h>
++#include <linux/skbuff.h>
++#include <linux/kobject.h>
++#include <linux/netlink.h>
++#include <linux/kobject.h>
++#include <linux/workqueue.h>
+ 
+ #include <media/v4l2-common.h>
+ 
+@@ -1156,9 +1161,149 @@ static void uvc_video_decode_data(struct
+ 	uvc_urb->async_operations++;
+ }
+ 
++struct bh_priv {
++	unsigned long	seen;
++};
++
++struct bh_event {
++	const char		*name;
++	struct sk_buff		*skb;
++	struct work_struct	work;
++};
++
++#define BH_ERR(fmt, args...) printk(KERN_ERR "%s: " fmt, "webcam", ##args )
++#define BH_DBG(fmt, args...) do {} while (0)
++#define BH_SKB_SIZE     2048
++
++extern u64 uevent_next_seqnum(void);
++static int seen = 0;
++
++static int bh_event_add_var(struct bh_event *event, int argv,
++		const char *format, ...)
++{
++	static char buf[128];
++	char *s;
++	va_list args;
++	int len;
++
++	if (argv)
++		return 0;
++
++	va_start(args, format);
++	len = vsnprintf(buf, sizeof(buf), format, args);
++	va_end(args);
++
++	if (len >= sizeof(buf)) {
++		BH_ERR("buffer size too small\n");
++		WARN_ON(1);
++		return -ENOMEM;
++	}
++
++	s = skb_put(event->skb, len + 1);
++	strcpy(s, buf);
++
++	BH_DBG("added variable '%s'\n", s);
++
++	return 0;
++}
++
++static int motion_hotplug_fill_event(struct bh_event *event)
++{
++	int s = jiffies;
++	int ret;
++
++	if (!seen)
++		seen = jiffies;
++
++	ret = bh_event_add_var(event, 0, "HOME=%s", "/");
++	if (ret)
++		return ret;
++
++	ret = bh_event_add_var(event, 0, "PATH=%s",
++		"/sbin:/bin:/usr/sbin:/usr/bin");
++	if (ret)
++		return ret;
++
++	ret = bh_event_add_var(event, 0, "SUBSYSTEM=usb");
++	if (ret)
++		return ret;
++
++	ret = bh_event_add_var(event, 0, "ACTION=motion");
++	if (ret)
++		return ret;
++
++	ret = bh_event_add_var(event, 0, "SEEN=%d", s - seen);
++	if (ret)
++		return ret;
++	seen = s;
++
++	ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum());
++
++	return ret;
++}
++
++static void motion_hotplug_work(struct work_struct *work)
++{
++	struct bh_event *event = container_of(work, struct bh_event, work);
++	int ret = 0;
++
++	event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL);
++	if (!event->skb)
++		goto out_free_event;
++
++	ret = bh_event_add_var(event, 0, "%s@", "add");
++	if (ret)
++		goto out_free_skb;
++
++	ret = motion_hotplug_fill_event(event);
++	if (ret)
++		goto out_free_skb;
++
++	NETLINK_CB(event->skb).dst_group = 1;
++	broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);
++
++out_free_skb:
++	if (ret) {
++		BH_ERR("work error %d\n", ret);
++		kfree_skb(event->skb);
++	}
++out_free_event:
++	kfree(event);
++}
++
++static int motion_hotplug_create_event(void)
++{
++	struct bh_event *event;
++
++	event = kzalloc(sizeof(*event), GFP_KERNEL);
++	if (!event)
++		return -ENOMEM;
++
++	event->name = "motion";
++
++	INIT_WORK(&event->work, (void *)(void *)motion_hotplug_work);
++	schedule_work(&event->work);
++
++	return 0;
++}
++
++#define MOTION_FLAG_OFFSET	4
+ static void uvc_video_decode_end(struct uvc_streaming *stream,
+ 		struct uvc_buffer *buf, const u8 *data, int len)
+ {
++	if ((stream->dev->quirks & UVC_QUIRK_MOTION) &&
++			(data[len - 2] == 0xff) && (data[len - 1] == 0xd9)) {
++		u8 *mem;
++		buf->state = UVC_BUF_STATE_READY;
++		mem = (u8 *) (buf->mem + MOTION_FLAG_OFFSET);
++		if ( stream->dev->motion ) {
++			stream->dev->motion = 0;
++			motion_hotplug_create_event();
++		} else {
++			*mem &= 0x7f;
++		}
++	}
++
+ 	/* Mark the buffer as done if the EOF marker is set. */
+ 	if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
+ 		uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
+@@ -1715,6 +1860,8 @@ static int uvc_init_video_isoc(struct uv
+ 	if (npackets == 0)
+ 		return -ENOMEM;
+ 
++	if (stream->dev->quirks & UVC_QUIRK_SINGLE_ISO)
++		npackets = 1;
+ 	size = npackets * psize;
+ 
+ 	for_each_uvc_urb(uvc_urb, stream) {
+--- a/drivers/media/usb/uvc/uvcvideo.h
++++ b/drivers/media/usb/uvc/uvcvideo.h
+@@ -199,7 +199,9 @@
+ #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT	0x00000400
+ #define UVC_QUIRK_FORCE_Y8		0x00000800
+ #define UVC_QUIRK_FORCE_BPP		0x00001000
+-
++#define UVC_QUIRK_MOTION		0x00001000
++#define UVC_QUIRK_SINGLE_ISO		0x00002000
++ 
+ /* Format flags */
+ #define UVC_FMT_FLAG_COMPRESSED		0x00000001
+ #define UVC_FMT_FLAG_STREAM		0x00000002
+@@ -666,6 +668,7 @@ struct uvc_device {
+ 	u8 *status;
+ 	struct input_dev *input;
+ 	char input_phys[64];
++	int motion;
+ 
+ 	struct uvc_ctrl_work {
+ 		struct work_struct work;
diff --git a/target/linux/ramips/patches-5.10/0037-mtd-cfi-cmdset-0002-force-word-write.patch b/target/linux/ramips/patches-5.10/0037-mtd-cfi-cmdset-0002-force-word-write.patch
new file mode 100644
index 0000000000..7011bbe50b
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0037-mtd-cfi-cmdset-0002-force-word-write.patch
@@ -0,0 +1,20 @@
+From ee9081b2726a5ca8cde5497afdc5425e21ff8f8b Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Mon, 15 Jul 2013 00:39:21 +0200
+Subject: [PATCH 37/53] mtd: cfi cmdset 0002 force word write
+
+---
+ drivers/mtd/chips/cfi_cmdset_0002.c |    9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/chips/cfi_cmdset_0002.c
++++ b/drivers/mtd/chips/cfi_cmdset_0002.c
+@@ -40,7 +40,7 @@
+ #include <linux/mtd/xip.h>
+ 
+ #define AMD_BOOTLOC_BUG
+-#define FORCE_WORD_WRITE 0
++#define FORCE_WORD_WRITE 1
+ 
+ #define MAX_RETRIES 3
+ 
diff --git a/target/linux/ramips/patches-5.10/0041-DT-Add-documentation-for-spi-rt2880.patch b/target/linux/ramips/patches-5.10/0041-DT-Add-documentation-for-spi-rt2880.patch
new file mode 100644
index 0000000000..e2643e3f25
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0041-DT-Add-documentation-for-spi-rt2880.patch
@@ -0,0 +1,44 @@
+From da6015e7f19d749f135f7ac55c4ec47b06faa868 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Fri, 9 Aug 2013 20:12:59 +0200
+Subject: [PATCH 41/53] DT: Add documentation for spi-rt2880
+
+Describe the SPI master found on the MIPS based Ralink RT2880 SoC.
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ .../devicetree/bindings/spi/spi-rt2880.txt         |   28 ++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/spi/spi-rt2880.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/spi/spi-rt2880.txt
+@@ -0,0 +1,28 @@
++Ralink SoC RT2880 SPI master controller.
++
++This SPI controller is found on most wireless SoCs made by ralink.
++
++Required properties:
++- compatible : "ralink,rt2880-spi"
++- reg : The register base for the controller.
++- #address-cells : <1>, as required by generic SPI binding.
++- #size-cells : <0>, also as required by generic SPI binding.
++
++Child nodes as per the generic SPI binding.
++
++Example:
++
++	spi at b00 {
++		compatible = "ralink,rt2880-spi";
++		reg = <0xb00 0x100>;
++
++		#address-cells = <1>;
++		#size-cells = <0>;
++
++		m25p80 at 0 {
++			compatible = "m25p80";
++			reg = <0>;
++			spi-max-frequency = <10000000>;
++		};
++	};
++
diff --git a/target/linux/ramips/patches-5.10/0042-SPI-ralink-add-Ralink-SoC-spi-driver.patch b/target/linux/ramips/patches-5.10/0042-SPI-ralink-add-Ralink-SoC-spi-driver.patch
new file mode 100644
index 0000000000..652371617a
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0042-SPI-ralink-add-Ralink-SoC-spi-driver.patch
@@ -0,0 +1,574 @@
+From 683af4ebb91a1600df1946ac4769d916b8a1be65 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Sun, 27 Jul 2014 11:15:12 +0100
+Subject: [PATCH 42/53] SPI: ralink: add Ralink SoC spi driver
+
+Add the driver needed to make SPI work on Ralink SoC.
+
+Signed-off-by: Gabor Juhos <juhosg at openwrt.org>
+Acked-by: John Crispin <blogic at openwrt.org>
+---
+ drivers/spi/Kconfig      |    6 +
+ drivers/spi/Makefile     |    1 +
+ drivers/spi/spi-rt2880.c |  530 ++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 537 insertions(+)
+ create mode 100644 drivers/spi/spi-rt2880.c
+
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -605,6 +605,12 @@ config SPI_QCOM_GENI
+ 	  This driver can also be built as a module.  If so, the module
+ 	  will be called spi-geni-qcom.
+ 
++config SPI_RT2880
++	tristate "Ralink RT288x SPI Controller"
++	depends on RALINK
++	help
++	  This selects a driver for the Ralink RT288x/RT305x SPI Controller.
++
+ config SPI_S3C24XX
+ 	tristate "Samsung S3C24XX series SPI"
+ 	depends on ARCH_S3C24XX
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -87,6 +87,7 @@ obj-$(CONFIG_SPI_QUP)			+= spi-qup.o
+ obj-$(CONFIG_SPI_ROCKCHIP)		+= spi-rockchip.o
+ obj-$(CONFIG_SPI_RB4XX)			+= spi-rb4xx.o
+ obj-$(CONFIG_SPI_RSPI)			+= spi-rspi.o
++obj-$(CONFIG_SPI_RT2880)		+= spi-rt2880.o
+ obj-$(CONFIG_SPI_S3C24XX)		+= spi-s3c24xx-hw.o
+ spi-s3c24xx-hw-y			:= spi-s3c24xx.o
+ spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
+--- /dev/null
++++ b/drivers/spi/spi-rt2880.c
+@@ -0,0 +1,530 @@
++/*
++ * spi-rt2880.c -- Ralink RT288x/RT305x SPI controller driver
++ *
++ * Copyright (C) 2011 Sergiy <piratfm at gmail.com>
++ * Copyright (C) 2011-2013 Gabor Juhos <juhosg at openwrt.org>
++ *
++ * Some parts are based on spi-orion.c:
++ *   Author: Shadi Ammouri <shadi at marvell.com>
++ *   Copyright (C) 2007-2008 Marvell Ltd.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/reset.h>
++#include <linux/spi/spi.h>
++#include <linux/platform_device.h>
++#include <linux/gpio.h>
++
++#define DRIVER_NAME			"spi-rt2880"
++
++#define RAMIPS_SPI_STAT			0x00
++#define RAMIPS_SPI_CFG			0x10
++#define RAMIPS_SPI_CTL			0x14
++#define RAMIPS_SPI_DATA			0x20
++#define RAMIPS_SPI_ADDR			0x24
++#define RAMIPS_SPI_BS			0x28
++#define RAMIPS_SPI_USER			0x2C
++#define RAMIPS_SPI_TXFIFO		0x30
++#define RAMIPS_SPI_RXFIFO		0x34
++#define RAMIPS_SPI_FIFO_STAT		0x38
++#define RAMIPS_SPI_MODE			0x3C
++#define RAMIPS_SPI_DEV_OFFSET		0x40
++#define RAMIPS_SPI_DMA			0x80
++#define RAMIPS_SPI_DMASTAT		0x84
++#define RAMIPS_SPI_ARBITER		0xF0
++
++/* SPISTAT register bit field */
++#define SPISTAT_BUSY			BIT(0)
++
++/* SPICFG register bit field */
++#define SPICFG_ADDRMODE			BIT(12)
++#define SPICFG_RXENVDIS			BIT(11)
++#define SPICFG_RXCAP			BIT(10)
++#define SPICFG_SPIENMODE		BIT(9)
++#define SPICFG_MSBFIRST			BIT(8)
++#define SPICFG_SPICLKPOL		BIT(6)
++#define SPICFG_RXCLKEDGE_FALLING	BIT(5)
++#define SPICFG_TXCLKEDGE_FALLING	BIT(4)
++#define SPICFG_HIZSPI			BIT(3)
++#define SPICFG_SPICLK_PRESCALE_MASK	0x7
++#define SPICFG_SPICLK_DIV2		0
++#define SPICFG_SPICLK_DIV4		1
++#define SPICFG_SPICLK_DIV8		2
++#define SPICFG_SPICLK_DIV16		3
++#define SPICFG_SPICLK_DIV32		4
++#define SPICFG_SPICLK_DIV64		5
++#define SPICFG_SPICLK_DIV128		6
++#define SPICFG_SPICLK_DISABLE		7
++
++/* SPICTL register bit field */
++#define SPICTL_START			BIT(4)
++#define SPICTL_HIZSDO			BIT(3)
++#define SPICTL_STARTWR			BIT(2)
++#define SPICTL_STARTRD			BIT(1)
++#define SPICTL_SPIENA			BIT(0)
++
++/* SPIUSER register bit field */
++#define SPIUSER_USERMODE		BIT(21)
++#define SPIUSER_INSTR_PHASE		BIT(20)
++#define SPIUSER_ADDR_PHASE_MASK		0x7
++#define SPIUSER_ADDR_PHASE_OFFSET	17
++#define SPIUSER_MODE_PHASE		BIT(16)
++#define SPIUSER_DUMMY_PHASE_MASK	0x3
++#define SPIUSER_DUMMY_PHASE_OFFSET	14
++#define SPIUSER_DATA_PHASE_MASK		0x3
++#define SPIUSER_DATA_PHASE_OFFSET	12
++#define SPIUSER_DATA_READ		(BIT(0) << SPIUSER_DATA_PHASE_OFFSET)
++#define SPIUSER_DATA_WRITE		(BIT(1) << SPIUSER_DATA_PHASE_OFFSET)
++#define SPIUSER_ADDR_TYPE_OFFSET	9
++#define SPIUSER_MODE_TYPE_OFFSET	6
++#define SPIUSER_DUMMY_TYPE_OFFSET	3
++#define SPIUSER_DATA_TYPE_OFFSET	0
++#define SPIUSER_TRANSFER_MASK		0x7
++#define SPIUSER_TRANSFER_SINGLE		BIT(0)
++#define SPIUSER_TRANSFER_DUAL		BIT(1)
++#define SPIUSER_TRANSFER_QUAD		BIT(2)
++
++#define SPIUSER_TRANSFER_TYPE(type) ( \
++	(type << SPIUSER_ADDR_TYPE_OFFSET) | \
++	(type << SPIUSER_MODE_TYPE_OFFSET) | \
++	(type << SPIUSER_DUMMY_TYPE_OFFSET) | \
++	(type << SPIUSER_DATA_TYPE_OFFSET) \
++)
++
++/* SPIFIFOSTAT register bit field */
++#define SPIFIFOSTAT_TXEMPTY		BIT(19)
++#define SPIFIFOSTAT_RXEMPTY		BIT(18)
++#define SPIFIFOSTAT_TXFULL		BIT(17)
++#define SPIFIFOSTAT_RXFULL		BIT(16)
++#define SPIFIFOSTAT_FIFO_MASK		0xff
++#define SPIFIFOSTAT_TX_OFFSET		8
++#define SPIFIFOSTAT_RX_OFFSET		0
++
++#define SPI_FIFO_DEPTH			16
++
++/* SPIMODE register bit field */
++#define SPIMODE_MODE_OFFSET		24
++#define SPIMODE_DUMMY_OFFSET		0
++
++/* SPIARB register bit field */
++#define SPICTL_ARB_EN			BIT(31)
++#define SPICTL_CSCTL1			BIT(16)
++#define SPI1_POR			BIT(1)
++#define SPI0_POR			BIT(0)
++
++#define RT2880_SPI_MODE_BITS	(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | \
++		SPI_CS_HIGH)
++
++static atomic_t hw_reset_count = ATOMIC_INIT(0);
++
++struct rt2880_spi {
++	struct spi_master	*master;
++	void __iomem		*base;
++	u32			speed;
++	u16			wait_loops;
++	u16			mode;
++	struct clk		*clk;
++};
++
++static inline struct rt2880_spi *spidev_to_rt2880_spi(struct spi_device *spi)
++{
++	return spi_master_get_devdata(spi->master);
++}
++
++static inline u32 rt2880_spi_read(struct rt2880_spi *rs, u32 reg)
++{
++	return ioread32(rs->base + reg);
++}
++
++static inline void rt2880_spi_write(struct rt2880_spi *rs, u32 reg,
++		const u32 val)
++{
++	iowrite32(val, rs->base + reg);
++}
++
++static inline void rt2880_spi_setbits(struct rt2880_spi *rs, u32 reg, u32 mask)
++{
++	void __iomem *addr = rs->base + reg;
++
++	iowrite32((ioread32(addr) | mask), addr);
++}
++
++static inline void rt2880_spi_clrbits(struct rt2880_spi *rs, u32 reg, u32 mask)
++{
++	void __iomem *addr = rs->base + reg;
++
++	iowrite32((ioread32(addr) & ~mask), addr);
++}
++
++static u32 rt2880_spi_baudrate_get(struct spi_device *spi, unsigned int speed)
++{
++	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
++	u32 rate;
++	u32 prescale;
++
++	/*
++	 * the supported rates are: 2, 4, 8, ... 128
++	 * round up as we look for equal or less speed
++	 */
++	rate = DIV_ROUND_UP(clk_get_rate(rs->clk), speed);
++	rate = roundup_pow_of_two(rate);
++
++	/* Convert the rate to SPI clock divisor value.	*/
++	prescale = ilog2(rate / 2);
++
++	/* some tolerance. double and add 100 */
++	rs->wait_loops = (8 * HZ * loops_per_jiffy) /
++		(clk_get_rate(rs->clk) / rate);
++	rs->wait_loops = (rs->wait_loops << 1) + 100;
++	rs->speed = speed;
++
++	dev_dbg(&spi->dev, "speed: %lu/%u, rate: %u, prescal: %u, loops: %hu\n",
++			clk_get_rate(rs->clk) / rate, speed, rate, prescale,
++			rs->wait_loops);
++
++	return prescale;
++}
++
++static u32 get_arbiter_offset(struct spi_master *master)
++{
++	u32 offset;
++
++	offset = RAMIPS_SPI_ARBITER;
++	if (master->bus_num == 1)
++		offset -= RAMIPS_SPI_DEV_OFFSET;
++
++	return offset;
++}
++
++static void rt2880_spi_set_cs(struct spi_device *spi, bool enable)
++{
++	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
++
++	if (enable)
++		rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
++	else
++		rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
++}
++
++static int rt2880_spi_wait_ready(struct rt2880_spi *rs, int len)
++{
++	int loop = rs->wait_loops * len;
++
++	while ((rt2880_spi_read(rs, RAMIPS_SPI_STAT) & SPISTAT_BUSY) && --loop)
++		cpu_relax();
++
++	if (loop)
++		return 0;
++
++	return -ETIMEDOUT;
++}
++
++static void rt2880_dump_reg(struct spi_master *master)
++{
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
++
++	dev_dbg(&master->dev, "stat: %08x, cfg: %08x, ctl: %08x, " \
++			"data: %08x, arb: %08x\n",
++			rt2880_spi_read(rs, RAMIPS_SPI_STAT),
++			rt2880_spi_read(rs, RAMIPS_SPI_CFG),
++			rt2880_spi_read(rs, RAMIPS_SPI_CTL),
++			rt2880_spi_read(rs, RAMIPS_SPI_DATA),
++			rt2880_spi_read(rs, get_arbiter_offset(master)));
++}
++
++static int rt2880_spi_transfer_one(struct spi_master *master,
++		struct spi_device *spi, struct spi_transfer *xfer)
++{
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
++	unsigned len;
++	const u8 *tx = xfer->tx_buf;
++	u8 *rx = xfer->rx_buf;
++	int err = 0;
++
++	/* change clock speed  */
++	if (unlikely(rs->speed != xfer->speed_hz)) {
++		u32 reg;
++		reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
++		reg &= ~SPICFG_SPICLK_PRESCALE_MASK;
++		reg |= rt2880_spi_baudrate_get(spi, xfer->speed_hz);
++		rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
++	}
++
++	if (tx) {
++		len = xfer->len;
++		while (len-- > 0) {
++			rt2880_spi_write(rs, RAMIPS_SPI_DATA, *tx++);
++			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR);
++			err = rt2880_spi_wait_ready(rs, 1);
++			if (err) {
++				dev_err(&spi->dev, "TX failed, err=%d\n", err);
++				goto out;
++			}
++		}
++	}
++
++	if (rx) {
++		len = xfer->len;
++		while (len-- > 0) {
++			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD);
++			err = rt2880_spi_wait_ready(rs, 1);
++			if (err) {
++				dev_err(&spi->dev, "RX failed, err=%d\n", err);
++				goto out;
++			}
++			*rx++ = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA);
++		}
++	}
++
++out:
++	return err;
++}
++
++/* copy from spi.c */
++static void spi_set_cs(struct spi_device *spi, bool enable)
++{
++	if (spi->mode & SPI_CS_HIGH)
++		enable = !enable;
++
++	if (spi->cs_gpio >= 0)
++		gpio_set_value(spi->cs_gpio, !enable);
++	else if (spi->master->set_cs)
++		spi->master->set_cs(spi, !enable);
++}
++
++static int rt2880_spi_setup(struct spi_device *spi)
++{
++	struct spi_master *master = spi->master;
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
++	u32 reg, old_reg, arbit_off;
++
++	if ((spi->max_speed_hz > master->max_speed_hz) ||
++			(spi->max_speed_hz < master->min_speed_hz)) {
++		dev_err(&spi->dev, "invalide requested speed %d Hz\n",
++				spi->max_speed_hz);
++		return -EINVAL;
++	}
++
++	if (!(master->bits_per_word_mask &
++				BIT(spi->bits_per_word - 1))) {
++		dev_err(&spi->dev, "invalide bits_per_word %d\n",
++				spi->bits_per_word);
++		return -EINVAL;
++	}
++
++	/* the hardware seems can't work on mode0 force it to mode3 */
++	if ((spi->mode & (SPI_CPOL | SPI_CPHA)) == SPI_MODE_0) {
++		dev_warn(&spi->dev, "force spi mode3\n");
++		spi->mode |= SPI_MODE_3;
++	}
++
++	/* chip polarity */
++	arbit_off = get_arbiter_offset(master);
++	reg = old_reg = rt2880_spi_read(rs, arbit_off);
++	if (spi->mode & SPI_CS_HIGH) {
++		switch (master->bus_num) {
++		case 1:
++			reg |= SPI1_POR;
++			break;
++		default:
++			reg |= SPI0_POR;
++			break;
++		}
++	} else {
++		switch (master->bus_num) {
++		case 1:
++			reg &= ~SPI1_POR;
++			break;
++		default:
++			reg &= ~SPI0_POR;
++			break;
++		}
++	}
++
++	/* enable spi1 */
++	if (master->bus_num == 1)
++		reg |= SPICTL_ARB_EN;
++
++	if (reg != old_reg)
++		rt2880_spi_write(rs, arbit_off, reg);
++
++	/* deselected the spi device */
++	spi_set_cs(spi, false);
++
++	rt2880_dump_reg(master);
++
++	return 0;
++}
++
++static int rt2880_spi_prepare_message(struct spi_master *master,
++		struct spi_message *msg)
++{
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
++	struct spi_device *spi = msg->spi;
++	u32 reg;
++
++	if ((rs->mode == spi->mode) && (rs->speed == spi->max_speed_hz))
++		return 0;
++
++#if 0
++	/* set spido to tri-state */
++	rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO);
++#endif
++
++	reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
++
++	reg &= ~(SPICFG_MSBFIRST | SPICFG_SPICLKPOL |
++			SPICFG_RXCLKEDGE_FALLING |
++			SPICFG_TXCLKEDGE_FALLING |
++			SPICFG_SPICLK_PRESCALE_MASK);
++
++	/* MSB */
++	if (!(spi->mode & SPI_LSB_FIRST))
++		reg |= SPICFG_MSBFIRST;
++
++	/* spi mode */
++	switch (spi->mode & (SPI_CPOL | SPI_CPHA)) {
++	case SPI_MODE_0:
++		reg |= SPICFG_TXCLKEDGE_FALLING;
++		break;
++	case SPI_MODE_1:
++		reg |= SPICFG_RXCLKEDGE_FALLING;
++		break;
++	case SPI_MODE_2:
++		reg |= SPICFG_SPICLKPOL | SPICFG_RXCLKEDGE_FALLING;
++		break;
++	case SPI_MODE_3:
++		reg |= SPICFG_SPICLKPOL | SPICFG_TXCLKEDGE_FALLING;
++		break;
++	}
++	rs->mode = spi->mode;
++
++#if 0
++	/* set spiclk and spiena to tri-state */
++	reg |= SPICFG_HIZSPI;
++#endif
++
++	/* clock divide */
++	reg |= rt2880_spi_baudrate_get(spi, spi->max_speed_hz);
++
++	rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
++
++	return 0;
++}
++
++static int rt2880_spi_probe(struct platform_device *pdev)
++{
++	struct spi_master *master;
++	struct rt2880_spi *rs;
++	void __iomem *base;
++	struct resource *r;
++	struct clk *clk;
++	int ret;
++
++	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	base = devm_ioremap_resource(&pdev->dev, r);
++	if (IS_ERR(base))
++		return PTR_ERR(base);
++
++	clk = devm_clk_get(&pdev->dev, NULL);
++	if (IS_ERR(clk)) {
++		dev_err(&pdev->dev, "unable to get SYS clock\n");
++		return PTR_ERR(clk);
++	}
++
++	ret = clk_prepare_enable(clk);
++	if (ret)
++		goto err_clk;
++
++	master = spi_alloc_master(&pdev->dev, sizeof(*rs));
++	if (master == NULL) {
++		dev_dbg(&pdev->dev, "master allocation failed\n");
++		ret = -ENOMEM;
++		goto err_clk;
++	}
++
++	master->dev.of_node = pdev->dev.of_node;
++	master->mode_bits = RT2880_SPI_MODE_BITS;
++	master->bits_per_word_mask = SPI_BPW_MASK(8);
++	master->min_speed_hz = clk_get_rate(clk) / 128;
++	master->max_speed_hz = clk_get_rate(clk) / 2;
++	master->flags = SPI_MASTER_HALF_DUPLEX;
++	master->setup = rt2880_spi_setup;
++	master->prepare_message = rt2880_spi_prepare_message;
++	master->set_cs = rt2880_spi_set_cs;
++	master->transfer_one = rt2880_spi_transfer_one,
++
++	dev_set_drvdata(&pdev->dev, master);
++
++	rs = spi_master_get_devdata(master);
++	rs->master = master;
++	rs->base = base;
++	rs->clk = clk;
++
++	if (atomic_inc_return(&hw_reset_count) == 1)
++		device_reset(&pdev->dev);
++
++	ret = devm_spi_register_master(&pdev->dev, master);
++	if (ret < 0) {
++		dev_err(&pdev->dev, "devm_spi_register_master error.\n");
++		goto err_master;
++	}
++
++	return ret;
++
++err_master:
++	spi_master_put(master);
++	kfree(master);
++err_clk:
++	clk_disable_unprepare(clk);
++
++	return ret;
++}
++
++static int rt2880_spi_remove(struct platform_device *pdev)
++{
++	struct spi_master *master;
++	struct rt2880_spi *rs;
++
++	master = dev_get_drvdata(&pdev->dev);
++	rs = spi_master_get_devdata(master);
++
++	clk_disable_unprepare(rs->clk);
++	atomic_dec(&hw_reset_count);
++
++	return 0;
++}
++
++MODULE_ALIAS("platform:" DRIVER_NAME);
++
++static const struct of_device_id rt2880_spi_match[] = {
++	{ .compatible = "ralink,rt2880-spi" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, rt2880_spi_match);
++
++static struct platform_driver rt2880_spi_driver = {
++	.driver = {
++		.name = DRIVER_NAME,
++		.owner = THIS_MODULE,
++		.of_match_table = rt2880_spi_match,
++	},
++	.probe = rt2880_spi_probe,
++	.remove = rt2880_spi_remove,
++};
++
++module_platform_driver(rt2880_spi_driver);
++
++MODULE_DESCRIPTION("Ralink SPI driver");
++MODULE_AUTHOR("Sergiy <piratfm at gmail.com>");
++MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/ramips/patches-5.10/0044-i2c-MIPS-adds-ralink-I2C-driver.patch b/target/linux/ramips/patches-5.10/0044-i2c-MIPS-adds-ralink-I2C-driver.patch
new file mode 100644
index 0000000000..e3bb7f2488
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0044-i2c-MIPS-adds-ralink-I2C-driver.patch
@@ -0,0 +1,507 @@
+From 723b8beaabf3c3c4b1ce69480141f1e926f3f3b2 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Sun, 27 Jul 2014 09:52:56 +0100
+Subject: [PATCH 44/53] i2c: MIPS: adds ralink I2C driver
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ .../devicetree/bindings/i2c/i2c-ralink.txt         |   27 ++
+ drivers/i2c/busses/Kconfig                         |    4 +
+ drivers/i2c/busses/Makefile                        |    1 +
+ drivers/i2c/busses/i2c-ralink.c                    |  327 ++++++++++++++++++++
+ 4 files changed, 359 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/i2c/i2c-ralink.txt
+ create mode 100644 drivers/i2c/busses/i2c-ralink.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/i2c/i2c-ralink.txt
+@@ -0,0 +1,27 @@
++I2C for Ralink platforms
++
++Required properties :
++- compatible : Must be "link,rt3052-i2c"
++- reg: physical base address of the controller and length of memory mapped
++     region.
++- #address-cells = <1>;
++- #size-cells = <0>;
++
++Optional properties:
++- Child nodes conforming to i2c bus binding
++
++Example :
++
++palmbus at 10000000 {
++	i2c at 900 {
++		compatible = "link,rt3052-i2c";
++		reg = <0x900 0x100>;
++		#address-cells = <1>;
++		#size-cells = <0>;
++
++		hwmon at 4b {
++			compatible = "national,lm92";
++			reg = <0x4b>;
++		};
++	};
++};
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -922,6 +922,11 @@ config I2C_RK3X
+ 	  This driver can also be built as a module. If so, the module will
+ 	  be called i2c-rk3x.
+ 
++config I2C_RALINK
++	tristate "Ralink I2C Controller"
++	depends on RALINK && !SOC_MT7621
++	select OF_I2C
++
+ config HAVE_S3C2410_I2C
+ 	bool
+ 	help
+--- a/drivers/i2c/busses/Makefile
++++ b/drivers/i2c/busses/Makefile
+@@ -91,6 +91,7 @@ obj-$(CONFIG_I2C_PNX)		+= i2c-pnx.o
+ obj-$(CONFIG_I2C_PUV3)		+= i2c-puv3.o
+ obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
+ obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
++obj-$(CONFIG_I2C_RALINK)	+= i2c-ralink.o
+ obj-$(CONFIG_I2C_QCOM_GENI)	+= i2c-qcom-geni.o
+ obj-$(CONFIG_I2C_QUP)		+= i2c-qup.o
+ obj-$(CONFIG_I2C_RIIC)		+= i2c-riic.o
+--- /dev/null
++++ b/drivers/i2c/busses/i2c-ralink.c
+@@ -0,0 +1,435 @@
++/*
++ * drivers/i2c/busses/i2c-ralink.c
++ *
++ * Copyright (C) 2013 Steven Liu <steven_liu at mediatek.com>
++ * Copyright (C) 2016 Michael Lee <igvtee at gmail.com>
++ *
++ * Improve driver for i2cdetect from i2c-tools to detect i2c devices on the bus.
++ * (C) 2014 Sittisak <sittisaks at hotmail.com>
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#include <linux/interrupt.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/reset.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/platform_device.h>
++#include <linux/of_platform.h>
++#include <linux/i2c.h>
++#include <linux/io.h>
++#include <linux/err.h>
++#include <linux/clk.h>
++
++#define REG_CONFIG_REG		0x00
++#define REG_CLKDIV_REG		0x04
++#define REG_DEVADDR_REG		0x08
++#define REG_ADDR_REG		0x0C
++#define REG_DATAOUT_REG		0x10
++#define REG_DATAIN_REG		0x14
++#define REG_STATUS_REG		0x18
++#define REG_STARTXFR_REG	0x1C
++#define REG_BYTECNT_REG		0x20
++
++/* REG_CONFIG_REG */
++#define I2C_ADDRLEN_OFFSET	5
++#define I2C_DEVADLEN_OFFSET	2
++#define I2C_ADDRLEN_MASK	0x3
++#define I2C_ADDR_DIS		BIT(1)
++#define I2C_DEVADDR_DIS		BIT(0)
++#define I2C_ADDRLEN_8		(7 << I2C_ADDRLEN_OFFSET)
++#define I2C_DEVADLEN_7		(6 << I2C_DEVADLEN_OFFSET)
++#define I2C_CONF_DEFAULT	(I2C_ADDRLEN_8 | I2C_DEVADLEN_7)
++
++/* REG_CLKDIV_REG */
++#define I2C_CLKDIV_MASK		0xffff
++
++/* REG_DEVADDR_REG */
++#define I2C_DEVADDR_MASK	0x7f
++
++/* REG_ADDR_REG */
++#define I2C_ADDR_MASK		0xff
++
++/* REG_STATUS_REG */
++#define I2C_STARTERR		BIT(4)
++#define I2C_ACKERR		BIT(3)
++#define I2C_DATARDY		BIT(2)
++#define I2C_SDOEMPTY		BIT(1)
++#define I2C_BUSY		BIT(0)
++
++/* REG_STARTXFR_REG */
++#define NOSTOP_CMD		BIT(2)
++#define NODATA_CMD		BIT(1)
++#define READ_CMD		BIT(0)
++
++/* REG_BYTECNT_REG */
++#define BYTECNT_MAX		64
++#define SET_BYTECNT(x)		(x - 1)
++
++/* timeout waiting for I2C devices to respond (clock streching) */
++#define TIMEOUT_MS              1000
++#define DELAY_INTERVAL_US       100
++
++struct rt_i2c {
++	void __iomem *base;
++	struct clk *clk;
++	struct device *dev;
++	struct i2c_adapter adap;
++	u32 cur_clk;
++	u32 clk_div;
++	u32 flags;
++};
++
++static void rt_i2c_w32(struct rt_i2c *i2c, u32 val, unsigned reg)
++{
++	iowrite32(val, i2c->base + reg);
++}
++
++static u32 rt_i2c_r32(struct rt_i2c *i2c, unsigned reg)
++{
++	return ioread32(i2c->base + reg);
++}
++
++static int poll_down_timeout(void __iomem *addr, u32 mask)
++{
++	unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
++
++	do {
++		if (!(readl_relaxed(addr) & mask))
++			return 0;
++
++		usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
++	} while (time_before(jiffies, timeout));
++
++	return (readl_relaxed(addr) & mask) ? -EAGAIN : 0;
++}
++
++static int rt_i2c_wait_idle(struct rt_i2c *i2c)
++{
++	int ret;
++
++	ret = poll_down_timeout(i2c->base + REG_STATUS_REG, I2C_BUSY);
++	if (ret < 0)
++		dev_dbg(i2c->dev, "idle err(%d)\n", ret);
++
++	return ret;
++}
++
++static int poll_up_timeout(void __iomem *addr, u32 mask)
++{
++	unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
++	u32 status;
++
++	do {
++		status = readl_relaxed(addr);
++
++		/* check error status */
++		if (status & I2C_STARTERR)
++			return -EAGAIN;
++		else if (status & I2C_ACKERR)
++			return -ENXIO;
++		else if (status & mask)
++			return 0;
++
++		usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
++	} while (time_before(jiffies, timeout));
++
++	return -ETIMEDOUT;
++}
++
++static int rt_i2c_wait_rx_done(struct rt_i2c *i2c)
++{
++	int ret;
++
++	ret = poll_up_timeout(i2c->base + REG_STATUS_REG, I2C_DATARDY);
++	if (ret < 0)
++		dev_dbg(i2c->dev, "rx err(%d)\n", ret);
++
++	return ret;
++}
++
++static int rt_i2c_wait_tx_done(struct rt_i2c *i2c)
++{
++	int ret;
++
++	ret = poll_up_timeout(i2c->base + REG_STATUS_REG, I2C_SDOEMPTY);
++	if (ret < 0)
++		dev_dbg(i2c->dev, "tx err(%d)\n", ret);
++
++	return ret;
++}
++
++static void rt_i2c_reset(struct rt_i2c *i2c)
++{
++	device_reset(i2c->adap.dev.parent);
++	barrier();
++	rt_i2c_w32(i2c, i2c->clk_div, REG_CLKDIV_REG);
++}
++
++static void rt_i2c_dump_reg(struct rt_i2c *i2c)
++{
++	dev_dbg(i2c->dev, "conf %08x, clkdiv %08x, devaddr %08x, " \
++			"addr %08x, dataout %08x, datain %08x, " \
++			"status %08x, startxfr %08x, bytecnt %08x\n",
++			rt_i2c_r32(i2c, REG_CONFIG_REG),
++			rt_i2c_r32(i2c, REG_CLKDIV_REG),
++			rt_i2c_r32(i2c, REG_DEVADDR_REG),
++			rt_i2c_r32(i2c, REG_ADDR_REG),
++			rt_i2c_r32(i2c, REG_DATAOUT_REG),
++			rt_i2c_r32(i2c, REG_DATAIN_REG),
++			rt_i2c_r32(i2c, REG_STATUS_REG),
++			rt_i2c_r32(i2c, REG_STARTXFR_REG),
++			rt_i2c_r32(i2c, REG_BYTECNT_REG));
++}
++
++static int rt_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
++		int num)
++{
++	struct rt_i2c *i2c;
++	struct i2c_msg *pmsg;
++	unsigned char addr;
++	int i, j, ret;
++	u32 cmd;
++
++	i2c = i2c_get_adapdata(adap);
++
++	for (i = 0; i < num; i++) {
++		pmsg = &msgs[i];
++		if (i == (num - 1))
++			cmd = 0;
++		else
++			cmd = NOSTOP_CMD;
++
++		dev_dbg(i2c->dev, "addr: 0x%x, len: %d, flags: 0x%x, stop: %d\n",
++				pmsg->addr, pmsg->len, pmsg->flags,
++				(cmd == 0)? 1 : 0);
++
++		/* wait hardware idle */
++		if ((ret = rt_i2c_wait_idle(i2c)))
++			goto err_timeout;
++
++		if (pmsg->flags & I2C_M_TEN) {
++			rt_i2c_w32(i2c, I2C_CONF_DEFAULT, REG_CONFIG_REG);
++			/* 10 bits address */
++			addr = 0x78 | ((pmsg->addr >> 8) & 0x03);
++			rt_i2c_w32(i2c, addr & I2C_DEVADDR_MASK,
++					REG_DEVADDR_REG);
++			rt_i2c_w32(i2c, pmsg->addr & I2C_ADDR_MASK,
++					REG_ADDR_REG);
++		} else {
++			rt_i2c_w32(i2c, I2C_CONF_DEFAULT | I2C_ADDR_DIS,
++					REG_CONFIG_REG);
++			/* 7 bits address */
++			rt_i2c_w32(i2c, pmsg->addr & I2C_DEVADDR_MASK,
++					REG_DEVADDR_REG);
++		}
++
++		/* buffer length */
++		if (pmsg->len == 0)
++			cmd |= NODATA_CMD;
++		else
++			rt_i2c_w32(i2c, SET_BYTECNT(pmsg->len),
++					REG_BYTECNT_REG);
++
++		j = 0;
++		if (pmsg->flags & I2C_M_RD) {
++			cmd |= READ_CMD;
++			/* start transfer */
++			barrier();
++			rt_i2c_w32(i2c, cmd, REG_STARTXFR_REG);
++			do {
++				/* wait */
++				if ((ret = rt_i2c_wait_rx_done(i2c)))
++					goto err_timeout;
++				/* read data */
++				if (pmsg->len)
++					pmsg->buf[j] = rt_i2c_r32(i2c,
++							REG_DATAIN_REG);
++				j++;
++			} while (j < pmsg->len);
++		} else {
++			do {
++				/* write data */
++				if (pmsg->len)
++					rt_i2c_w32(i2c, pmsg->buf[j],
++							REG_DATAOUT_REG);
++				/* start transfer */
++				if (j == 0) {
++					barrier();
++					rt_i2c_w32(i2c, cmd, REG_STARTXFR_REG);
++				}
++				/* wait */
++				if ((ret = rt_i2c_wait_tx_done(i2c)))
++					goto err_timeout;
++				j++;
++			} while (j < pmsg->len);
++		}
++	}
++	/* the return value is number of executed messages */
++	ret = i;
++
++	return ret;
++
++err_timeout:
++	rt_i2c_dump_reg(i2c);
++	rt_i2c_reset(i2c);
++	return ret;
++}
++
++static u32 rt_i2c_func(struct i2c_adapter *a)
++{
++	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
++}
++
++static const struct i2c_algorithm rt_i2c_algo = {
++	.master_xfer	= rt_i2c_master_xfer,
++	.functionality	= rt_i2c_func,
++};
++
++static const struct of_device_id i2c_rt_dt_ids[] = {
++	{ .compatible = "ralink,rt2880-i2c" },
++	{ /* sentinel */ }
++};
++
++MODULE_DEVICE_TABLE(of, i2c_rt_dt_ids);
++
++static struct i2c_adapter_quirks rt_i2c_quirks = {
++        .max_write_len = BYTECNT_MAX,
++        .max_read_len = BYTECNT_MAX,
++};
++
++static int rt_i2c_init(struct rt_i2c *i2c)
++{
++	u32 reg;
++
++	/* i2c_sclk = periph_clk / ((2 * clk_div) + 5) */
++	i2c->clk_div = (clk_get_rate(i2c->clk) - (5 * i2c->cur_clk)) /
++		(2 * i2c->cur_clk);
++	if (i2c->clk_div < 8)
++		i2c->clk_div = 8;
++	if (i2c->clk_div > I2C_CLKDIV_MASK)
++		i2c->clk_div = I2C_CLKDIV_MASK;
++
++	/* check support combinde/repeated start message */
++	rt_i2c_w32(i2c, NOSTOP_CMD, REG_STARTXFR_REG);
++	reg = rt_i2c_r32(i2c, REG_STARTXFR_REG) & NOSTOP_CMD;
++
++	rt_i2c_reset(i2c);
++
++	return reg;
++}
++
++static int rt_i2c_probe(struct platform_device *pdev)
++{
++	struct resource *res;
++	struct rt_i2c *i2c;
++	struct i2c_adapter *adap;
++	const struct of_device_id *match;
++	int ret, restart;
++
++	match = of_match_device(i2c_rt_dt_ids, &pdev->dev);
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (!res) {
++		dev_err(&pdev->dev, "no memory resource found\n");
++		return -ENODEV;
++	}
++
++	i2c = devm_kzalloc(&pdev->dev, sizeof(struct rt_i2c), GFP_KERNEL);
++	if (!i2c) {
++		dev_err(&pdev->dev, "failed to allocate i2c_adapter\n");
++		return -ENOMEM;
++	}
++
++	i2c->base = devm_ioremap_resource(&pdev->dev, res);
++	if (IS_ERR(i2c->base))
++		return PTR_ERR(i2c->base);
++
++	i2c->clk = devm_clk_get(&pdev->dev, NULL);
++	if (IS_ERR(i2c->clk)) {
++		dev_err(&pdev->dev, "no clock defined\n");
++		return -ENODEV;
++	}
++	clk_prepare_enable(i2c->clk);
++	i2c->dev = &pdev->dev;
++
++	if (of_property_read_u32(pdev->dev.of_node,
++				"clock-frequency", &i2c->cur_clk))
++		i2c->cur_clk = 100000;
++
++	adap = &i2c->adap;
++	adap->owner = THIS_MODULE;
++	adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
++	adap->algo = &rt_i2c_algo;
++	adap->retries = 3;
++	adap->dev.parent = &pdev->dev;
++	i2c_set_adapdata(adap, i2c);
++	adap->dev.of_node = pdev->dev.of_node;
++	strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
++	adap->quirks = &rt_i2c_quirks;
++
++	platform_set_drvdata(pdev, i2c);
++
++	restart = rt_i2c_init(i2c);
++
++	ret = i2c_add_adapter(adap);
++	if (ret < 0) {
++		dev_err(&pdev->dev, "failed to add adapter\n");
++		clk_disable_unprepare(i2c->clk);
++		return ret;
++	}
++
++	dev_info(&pdev->dev, "clock %uKHz, re-start %ssupport\n",
++			i2c->cur_clk/1000, restart ? "" : "not ");
++
++	return ret;
++}
++
++static int rt_i2c_remove(struct platform_device *pdev)
++{
++	struct rt_i2c *i2c = platform_get_drvdata(pdev);
++
++	i2c_del_adapter(&i2c->adap);
++	clk_disable_unprepare(i2c->clk);
++
++	return 0;
++}
++
++static struct platform_driver rt_i2c_driver = {
++	.probe		= rt_i2c_probe,
++	.remove		= rt_i2c_remove,
++	.driver		= {
++		.owner	= THIS_MODULE,
++		.name	= "i2c-ralink",
++		.of_match_table = i2c_rt_dt_ids,
++	},
++};
++
++static int __init i2c_rt_init (void)
++{
++	return platform_driver_register(&rt_i2c_driver);
++}
++subsys_initcall(i2c_rt_init);
++
++static void __exit i2c_rt_exit (void)
++{
++	platform_driver_unregister(&rt_i2c_driver);
++}
++module_exit(i2c_rt_exit);
++
++MODULE_AUTHOR("Steven Liu <steven_liu at mediatek.com>");
++MODULE_DESCRIPTION("Ralink I2c host driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:Ralink-I2C");
diff --git a/target/linux/ramips/patches-5.10/0046-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch b/target/linux/ramips/patches-5.10/0046-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch
new file mode 100644
index 0000000000..f3912a1054
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0046-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch
@@ -0,0 +1,43 @@
+From 23147af14531cbdada194b94120ef8774f46292d Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Thu, 13 Nov 2014 19:08:40 +0100
+Subject: [PATCH 46/53] mmc: MIPS: ralink: add sdhci for mt7620a SoC
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ drivers/mmc/host/Kconfig             |    2 +
+ drivers/mmc/host/Makefile            |    1 +
+ drivers/mmc/host/mtk-mmc/Kconfig     |   16 +
+ drivers/mmc/host/mtk-mmc/Makefile    |   42 +
+ drivers/mmc/host/mtk-mmc/board.h     |  137 ++
+ drivers/mmc/host/mtk-mmc/dbg.c       |  347 ++++
+ drivers/mmc/host/mtk-mmc/dbg.h       |  156 ++
+ drivers/mmc/host/mtk-mmc/mt6575_sd.h | 1001 +++++++++++
+ drivers/mmc/host/mtk-mmc/sd.c        | 3060 ++++++++++++++++++++++++++++++++++
+ 9 files changed, 4762 insertions(+)
+ create mode 100644 drivers/mmc/host/mtk-mmc/Kconfig
+ create mode 100644 drivers/mmc/host/mtk-mmc/Makefile
+ create mode 100644 drivers/mmc/host/mtk-mmc/board.h
+ create mode 100644 drivers/mmc/host/mtk-mmc/dbg.c
+ create mode 100644 drivers/mmc/host/mtk-mmc/dbg.h
+ create mode 100644 drivers/mmc/host/mtk-mmc/mt6575_sd.h
+ create mode 100644 drivers/mmc/host/mtk-mmc/sd.c
+
+--- a/drivers/mmc/host/Kconfig
++++ b/drivers/mmc/host/Kconfig
+@@ -1019,3 +1019,5 @@ config MMC_SDHCI_AM654
+ 	  If you have a controller with this interface, say Y or M here.
+ 
+ 	  If unsure, say N.
++
++source "drivers/mmc/host/mtk-mmc/Kconfig"
+--- a/drivers/mmc/host/Makefile
++++ b/drivers/mmc/host/Makefile
+@@ -3,6 +3,7 @@
+ # Makefile for MMC/SD host controller drivers
+ #
+ 
++obj-$(CONFIG_MTK_MMC) 		+= mtk-mmc/
+ obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
+ armmmci-y := mmci.o
+ armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
diff --git a/target/linux/ramips/patches-5.10/0048-asoc-add-mt7620-support.patch b/target/linux/ramips/patches-5.10/0048-asoc-add-mt7620-support.patch
new file mode 100644
index 0000000000..d0d4141a8d
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0048-asoc-add-mt7620-support.patch
@@ -0,0 +1,1046 @@
+From 7f29222b1731e8182ba94a331531dec18865a1e4 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Sun, 27 Jul 2014 09:31:47 +0100
+Subject: [PATCH 48/53] asoc: add mt7620 support
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ arch/mips/ralink/of.c            |    2 +
+ sound/soc/Kconfig                |    1 +
+ sound/soc/Makefile               |    1 +
+ sound/soc/ralink/Kconfig         |   15 ++
+ sound/soc/ralink/Makefile        |   11 +
+ sound/soc/ralink/mt7620-i2s.c    |  436 ++++++++++++++++++++++++++++++++++++++
+ sound/soc/ralink/mt7620-wm8960.c |  233 ++++++++++++++++++++
+ 7 files changed, 699 insertions(+)
+ create mode 100644 sound/soc/ralink/Kconfig
+ create mode 100644 sound/soc/ralink/Makefile
+ create mode 100644 sound/soc/ralink/mt7620-i2s.c
+ create mode 100644 sound/soc/ralink/mt7620-wm8960.c
+
+--- a/arch/mips/ralink/of.c
++++ b/arch/mips/ralink/of.c
+@@ -13,6 +13,7 @@
+ #include <linux/of_fdt.h>
+ #include <linux/kernel.h>
+ #include <linux/memblock.h>
++#include <linux/module.h>
+ #include <linux/of_platform.h>
+ #include <linux/of_address.h>
+ 
+@@ -24,6 +25,7 @@
+ #include "common.h"
+ 
+ __iomem void *rt_sysc_membase;
++EXPORT_SYMBOL(rt_sysc_membase);
+ __iomem void *rt_memc_membase;
+ 
+ __iomem void *plat_of_remap_node(const char *node)
+--- a/sound/soc/Kconfig
++++ b/sound/soc/Kconfig
+@@ -60,6 +60,7 @@ source "sound/soc/mxs/Kconfig"
+ source "sound/soc/pxa/Kconfig"
+ source "sound/soc/qcom/Kconfig"
+ source "sound/soc/rockchip/Kconfig"
++source "sound/soc/ralink/Kconfig"
+ source "sound/soc/samsung/Kconfig"
+ source "sound/soc/sh/Kconfig"
+ source "sound/soc/sirf/Kconfig"
+--- a/sound/soc/Makefile
++++ b/sound/soc/Makefile
+@@ -43,6 +43,7 @@ obj-$(CONFIG_SND_SOC)	+= kirkwood/
+ obj-$(CONFIG_SND_SOC)	+= pxa/
+ obj-$(CONFIG_SND_SOC)	+= qcom/
+ obj-$(CONFIG_SND_SOC)	+= rockchip/
++obj-$(CONFIG_SND_SOC)	+= ralink/
+ obj-$(CONFIG_SND_SOC)	+= samsung/
+ obj-$(CONFIG_SND_SOC)	+= sh/
+ obj-$(CONFIG_SND_SOC)	+= sirf/
+--- /dev/null
++++ b/sound/soc/ralink/Kconfig
+@@ -0,0 +1,8 @@
++config SND_RALINK_SOC_I2S
++	depends on RALINK && SND_SOC && !SOC_RT288X
++	select SND_SOC_GENERIC_DMAENGINE_PCM
++	select REGMAP_MMIO
++	tristate "SoC Audio (I2S protocol) for Ralink SoC"
++	help
++	  Say Y if you want to use I2S protocol and I2S codec on Ralink/MediaTek
++	  based boards.
+--- /dev/null
++++ b/sound/soc/ralink/Makefile
+@@ -0,0 +1,6 @@
++#
++# Ralink/MediaTek Platform Support
++#
++snd-soc-ralink-i2s-objs := ralink-i2s.o
++
++obj-$(CONFIG_SND_RALINK_SOC_I2S) += snd-soc-ralink-i2s.o
+--- /dev/null
++++ b/sound/soc/ralink/ralink-i2s.c
+@@ -0,0 +1,965 @@
++/*
++ *  Copyright (C) 2010, Lars-Peter Clausen <lars at metafoo.de>
++ *  Copyright (C) 2016 Michael Lee <igvtee at gmail.com>
++ *
++ *  This program is free software; you can redistribute it and/or modify it
++ *  under  the terms of the GNU General  Public License as published by the
++ *  Free Software Foundation;  either version 2 of the License, or (at your
++ *  option) any later version.
++ *
++ *  You should have received a copy of the GNU General Public License along
++ *  with this program; if not, write to the Free Software Foundation, Inc.,
++ *  675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++#include <linux/debugfs.h>
++#include <linux/of_device.h>
++#include <sound/pcm_params.h>
++#include <sound/dmaengine_pcm.h>
++
++#include <asm/mach-ralink/ralink_regs.h>
++
++#define DRV_NAME "ralink-i2s"
++
++#define I2S_REG_CFG0		0x00
++#define I2S_REG_INT_STATUS	0x04
++#define I2S_REG_INT_EN		0x08
++#define I2S_REG_FF_STATUS	0x0c
++#define I2S_REG_WREG		0x10
++#define I2S_REG_RREG		0x14
++#define I2S_REG_CFG1		0x18
++#define I2S_REG_DIVCMP		0x20
++#define I2S_REG_DIVINT		0x24
++
++/* I2S_REG_CFG0 */
++#define I2S_REG_CFG0_EN		BIT(31)
++#define I2S_REG_CFG0_DMA_EN	BIT(30)
++#define I2S_REG_CFG0_BYTE_SWAP	BIT(28)
++#define I2S_REG_CFG0_TX_EN	BIT(24)
++#define I2S_REG_CFG0_RX_EN	BIT(20)
++#define I2S_REG_CFG0_SLAVE	BIT(16)
++#define I2S_REG_CFG0_RX_THRES	12
++#define I2S_REG_CFG0_TX_THRES	4
++#define I2S_REG_CFG0_THRES_MASK	(0xf << I2S_REG_CFG0_RX_THRES) | \
++	(4 << I2S_REG_CFG0_TX_THRES)
++#define I2S_REG_CFG0_DFT_THRES	(4 << I2S_REG_CFG0_RX_THRES) | \
++	(4 << I2S_REG_CFG0_TX_THRES)
++/* RT305x */
++#define I2S_REG_CFG0_CLK_DIS	BIT(8)
++#define I2S_REG_CFG0_TXCH_SWAP	BIT(3)
++#define I2S_REG_CFG0_TXCH1_OFF	BIT(2)
++#define I2S_REG_CFG0_TXCH0_OFF	BIT(1)
++#define I2S_REG_CFG0_SLAVE_EN	BIT(0)
++/* RT3883 */
++#define I2S_REG_CFG0_RXCH_SWAP	BIT(11)
++#define I2S_REG_CFG0_RXCH1_OFF	BIT(10)
++#define I2S_REG_CFG0_RXCH0_OFF	BIT(9)
++#define I2S_REG_CFG0_WS_INV	BIT(0)
++/* MT7628 */
++#define I2S_REG_CFG0_FMT_LE	BIT(29)
++#define I2S_REG_CFG0_SYS_BE	BIT(28)
++#define I2S_REG_CFG0_NORM_24	BIT(18)
++#define I2S_REG_CFG0_DATA_24	BIT(17)
++
++/* I2S_REG_INT_STATUS */
++#define I2S_REG_INT_RX_FAULT	BIT(7)
++#define I2S_REG_INT_RX_OVRUN	BIT(6)
++#define I2S_REG_INT_RX_UNRUN	BIT(5)
++#define I2S_REG_INT_RX_THRES	BIT(4)
++#define I2S_REG_INT_TX_FAULT	BIT(3)
++#define I2S_REG_INT_TX_OVRUN	BIT(2)
++#define I2S_REG_INT_TX_UNRUN	BIT(1)
++#define I2S_REG_INT_TX_THRES	BIT(0)
++#define I2S_REG_INT_TX_MASK	0xf
++#define I2S_REG_INT_RX_MASK	0xf0
++
++/* I2S_REG_INT_STATUS */
++#define I2S_RX_AVCNT(x)		((x >> 4) & 0xf)
++#define I2S_TX_AVCNT(x)		(x & 0xf)
++/* MT7628 */
++#define MT7628_I2S_RX_AVCNT(x)	((x >> 8) & 0x1f)
++#define MT7628_I2S_TX_AVCNT(x)	(x & 0x1f)
++
++/* I2S_REG_CFG1 */
++#define I2S_REG_CFG1_LBK	BIT(31)
++#define I2S_REG_CFG1_EXTLBK	BIT(30)
++/* RT3883 */
++#define I2S_REG_CFG1_LEFT_J	BIT(0)
++#define I2S_REG_CFG1_RIGHT_J	BIT(1)
++#define I2S_REG_CFG1_FMT_MASK	0x3
++
++/* I2S_REG_DIVCMP */
++#define I2S_REG_DIVCMP_CLKEN	BIT(31)
++#define I2S_REG_DIVCMP_DIVCOMP_MASK	0x1ff
++
++/* I2S_REG_DIVINT */
++#define I2S_REG_DIVINT_MASK	0x3ff
++
++/* BCLK dividers */
++#define RALINK_I2S_DIVCMP	0
++#define RALINK_I2S_DIVINT	1
++
++/* FIFO */
++#define RALINK_I2S_FIFO_SIZE	32
++
++/* feature flags */
++#define RALINK_FLAGS_TXONLY	BIT(0)
++#define RALINK_FLAGS_LEFT_J	BIT(1)
++#define RALINK_FLAGS_RIGHT_J	BIT(2)
++#define RALINK_FLAGS_ENDIAN	BIT(3)
++#define RALINK_FLAGS_24BIT	BIT(4)
++
++#define RALINK_I2S_INT_EN	0
++
++struct ralink_i2s_stats {
++	u32 dmafault;
++	u32 overrun;
++	u32 underrun;
++	u32 belowthres;
++};
++
++struct ralink_i2s {
++	struct device *dev;
++	void __iomem *regs;
++	struct clk *clk;
++	struct regmap *regmap;
++	u32 flags;
++	unsigned int fmt;
++	u16 txdma_req;
++	u16 rxdma_req;
++
++	struct snd_dmaengine_dai_dma_data playback_dma_data;
++	struct snd_dmaengine_dai_dma_data capture_dma_data;
++
++	struct dentry *dbg_dir;
++        struct dentry *dbg_stats;
++	struct ralink_i2s_stats txstats;
++	struct ralink_i2s_stats rxstats;
++};
++
++static void ralink_i2s_dump_regs(struct ralink_i2s *i2s)
++{
++	u32 buf[10];
++	int ret;
++
++	ret = regmap_bulk_read(i2s->regmap, I2S_REG_CFG0,
++			buf, ARRAY_SIZE(buf));
++
++	dev_dbg(i2s->dev, "CFG0: %08x, INTSTAT: %08x, INTEN: %08x, " \
++			"FFSTAT: %08x, WREG: %08x, RREG: %08x, " \
++			"CFG1: %08x, DIVCMP: %08x, DIVINT: %08x\n",
++			buf[0], buf[1], buf[2], buf[3], buf[4],
++			buf[5], buf[6], buf[8], buf[9]);
++}
++
++static int ralink_i2s_set_sysclk(struct snd_soc_dai *dai,
++                              int clk_id, unsigned int freq, int dir)
++{
++	return 0;
++}
++
++static int ralink_i2s_set_sys_bclk(struct snd_soc_dai *dai, int width, int rate)
++{
++	struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai);
++	unsigned long clk = clk_get_rate(i2s->clk);
++	int div;
++	uint32_t data;
++
++	/* disable clock at slave mode */
++	if ((i2s->fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
++			SND_SOC_DAIFMT_CBM_CFM) {
++		regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++				I2S_REG_CFG0_CLK_DIS,
++				I2S_REG_CFG0_CLK_DIS);
++		return 0;
++	}
++
++	/* FREQOUT = FREQIN / (I2S_CLK_DIV + 1) */
++	div = (clk / rate ) - 1;
++
++	data = rt_sysc_r32(0x30);
++	data &= (0xff << 8);
++	data |= (0x1 << 15) | (div << 8);
++	rt_sysc_w32(data, 0x30);
++
++	/* enable clock */
++	regmap_update_bits(i2s->regmap, I2S_REG_CFG0, I2S_REG_CFG0_CLK_DIS, 0);
++
++	dev_dbg(i2s->dev, "clk: %lu, rate: %u, div: %d\n",
++			clk, rate, div);
++
++	return 0;
++}
++
++static int ralink_i2s_set_bclk(struct snd_soc_dai *dai, int width, int rate)
++{
++	struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai);
++	unsigned long clk = clk_get_rate(i2s->clk);
++	int divint, divcomp;
++
++	/* disable clock at slave mode */
++	if ((i2s->fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
++			SND_SOC_DAIFMT_CBM_CFM) {
++		regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP,
++				I2S_REG_DIVCMP_CLKEN, 0);
++		return 0;
++	}
++
++	/* FREQOUT = FREQIN * (1/2) * (1/(DIVINT + DIVCOMP/512)) */
++	clk = clk / (2 * 2 * width);
++	divint = clk / rate;
++	divcomp = ((clk % rate) * 512) / rate;
++
++	if ((divint > I2S_REG_DIVINT_MASK) ||
++			(divcomp > I2S_REG_DIVCMP_DIVCOMP_MASK))
++		return -EINVAL;
++
++	regmap_update_bits(i2s->regmap, I2S_REG_DIVINT,
++			I2S_REG_DIVINT_MASK, divint);
++	regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP,
++			I2S_REG_DIVCMP_DIVCOMP_MASK, divcomp);
++
++	/* enable clock */
++	regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, I2S_REG_DIVCMP_CLKEN,
++			I2S_REG_DIVCMP_CLKEN);
++
++	dev_dbg(i2s->dev, "clk: %lu, rate: %u, int: %d, comp: %d\n",
++			clk_get_rate(i2s->clk), rate, divint, divcomp);
++
++	return 0;
++}
++
++static int ralink_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
++{
++	struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai);
++	unsigned int cfg0 = 0, cfg1 = 0;
++
++	/* set master/slave audio interface */
++	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++	case SND_SOC_DAIFMT_CBM_CFM:
++		if (i2s->flags & RALINK_FLAGS_TXONLY)
++			cfg0 |= I2S_REG_CFG0_SLAVE_EN;
++		else
++			cfg0 |= I2S_REG_CFG0_SLAVE;
++		break;
++	case SND_SOC_DAIFMT_CBS_CFS:
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	/* interface format */
++	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++	case SND_SOC_DAIFMT_I2S:
++		break;
++	case SND_SOC_DAIFMT_RIGHT_J:
++		if (i2s->flags & RALINK_FLAGS_RIGHT_J) {
++			cfg1 |= I2S_REG_CFG1_RIGHT_J;
++			break;
++		}
++		return -EINVAL;
++	case SND_SOC_DAIFMT_LEFT_J:
++		if (i2s->flags & RALINK_FLAGS_LEFT_J) {
++			cfg1 |= I2S_REG_CFG1_LEFT_J;
++			break;
++		}
++		return -EINVAL;
++	default:
++		return -EINVAL;
++	}
++
++	/* clock inversion */
++	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++	case SND_SOC_DAIFMT_NB_NF:
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	if (i2s->flags & RALINK_FLAGS_TXONLY) {
++		regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++				I2S_REG_CFG0_SLAVE_EN, cfg0);
++	} else {
++		regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++				I2S_REG_CFG0_SLAVE, cfg0);
++	}
++	regmap_update_bits(i2s->regmap, I2S_REG_CFG1,
++			I2S_REG_CFG1_FMT_MASK, cfg1);
++	i2s->fmt = fmt;
++
++	return 0;
++}
++
++static int ralink_i2s_startup(struct snd_pcm_substream *substream,
++		struct snd_soc_dai *dai)
++{
++	struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai);
++
++	if (dai->active)
++		return 0;
++
++	/* setup status interrupt */
++#if (RALINK_I2S_INT_EN)
++	regmap_write(i2s->regmap, I2S_REG_INT_EN, 0xff);
++#else
++	regmap_write(i2s->regmap, I2S_REG_INT_EN, 0x0);
++#endif
++
++	/* enable */
++	regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++			I2S_REG_CFG0_EN | I2S_REG_CFG0_DMA_EN |
++			I2S_REG_CFG0_THRES_MASK,
++			I2S_REG_CFG0_EN | I2S_REG_CFG0_DMA_EN |
++			I2S_REG_CFG0_DFT_THRES);
++
++	return 0;
++}
++
++static void ralink_i2s_shutdown(struct snd_pcm_substream *substream,
++		struct snd_soc_dai *dai)
++{
++	struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai);
++
++	/* If both streams are stopped, disable module and clock */
++	if (dai->active)
++		return;
++
++	/*
++	 * datasheet mention when disable all control regs are cleared
++	 * to initial values. need reinit at startup.
++	 */
++	regmap_update_bits(i2s->regmap, I2S_REG_CFG0, I2S_REG_CFG0_EN, 0);
++}
++
++static int ralink_i2s_hw_params(struct snd_pcm_substream *substream,
++		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
++{
++	struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai);
++	int width;
++	int ret;
++
++	width = params_width(params);
++	switch (width) {
++	case 16:
++		if (i2s->flags & RALINK_FLAGS_24BIT)
++			regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++					I2S_REG_CFG0_DATA_24, 0);
++		break;
++	case 24:
++		if (i2s->flags & RALINK_FLAGS_24BIT) {
++			regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++					I2S_REG_CFG0_DATA_24,
++					I2S_REG_CFG0_DATA_24);
++			break;
++		}
++		return -EINVAL;
++	default:
++		return -EINVAL;
++	}
++
++	switch (params_channels(params)) {
++	case 2:
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	if (i2s->flags & RALINK_FLAGS_ENDIAN) {
++		/* system endian */
++#ifdef SNDRV_LITTLE_ENDIAN
++		regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++				I2S_REG_CFG0_SYS_BE, 0);
++#else
++		regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++				I2S_REG_CFG0_SYS_BE,
++				I2S_REG_CFG0_SYS_BE);
++#endif
++
++		/* data endian */
++		switch (params_format(params)) {
++		case SNDRV_PCM_FORMAT_S16_LE:
++		case SNDRV_PCM_FORMAT_S24_LE:
++			regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++					I2S_REG_CFG0_FMT_LE,
++					I2S_REG_CFG0_FMT_LE);
++			break;
++		case SNDRV_PCM_FORMAT_S16_BE:
++		case SNDRV_PCM_FORMAT_S24_BE:
++			regmap_update_bits(i2s->regmap, I2S_REG_CFG0,
++					I2S_REG_CFG0_FMT_LE, 0);
++			break;
++		default:
++			return -EINVAL;
++		}
++	}
++
++	/* setup bclk rate */
++	if (i2s->flags & RALINK_FLAGS_TXONLY)
++		ret = ralink_i2s_set_sys_bclk(dai, width, params_rate(params));
++	else
++		ret = ralink_i2s_set_bclk(dai, width, params_rate(params));
++
++	return ret;
++}
++
++static int ralink_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
++		struct snd_soc_dai *dai)
++{
++	struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai);
++	unsigned int mask, val;
++
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		mask = I2S_REG_CFG0_TX_EN;
++	else
++		mask = I2S_REG_CFG0_RX_EN;
++
++	switch (cmd) {
++	case SNDRV_PCM_TRIGGER_START:
++	case SNDRV_PCM_TRIGGER_RESUME:
++	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++		val = mask;
++		break;
++	case SNDRV_PCM_TRIGGER_STOP:
++	case SNDRV_PCM_TRIGGER_SUSPEND:
++	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++		val = 0;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	regmap_update_bits(i2s->regmap, I2S_REG_CFG0, mask, val);
++
++	return 0;
++}
++
++static void ralink_i2s_init_dma_data(struct ralink_i2s *i2s,
++		struct resource *res)
++{
++	struct snd_dmaengine_dai_dma_data *dma_data;
++
++	/* Playback */
++	dma_data = &i2s->playback_dma_data;
++	dma_data->addr = res->start + I2S_REG_WREG;
++	dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++	dma_data->maxburst = 1;
++	dma_data->slave_id = i2s->txdma_req;
++
++	if (i2s->flags & RALINK_FLAGS_TXONLY)
++		return;
++
++	/* Capture */
++	dma_data = &i2s->capture_dma_data;
++	dma_data->addr = res->start + I2S_REG_RREG;
++	dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++	dma_data->maxburst = 1;
++	dma_data->slave_id = i2s->rxdma_req;
++}
++
++static int ralink_i2s_dai_probe(struct snd_soc_dai *dai)
++{
++	struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai);
++
++	snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
++			&i2s->capture_dma_data);
++
++	return 0;
++}
++
++static int ralink_i2s_dai_remove(struct snd_soc_dai *dai)
++{
++	return 0;
++}
++
++static const struct snd_soc_dai_ops ralink_i2s_dai_ops = {
++	.set_sysclk = ralink_i2s_set_sysclk,
++	.set_fmt = ralink_i2s_set_fmt,
++	.startup = ralink_i2s_startup,
++	.shutdown = ralink_i2s_shutdown,
++	.hw_params = ralink_i2s_hw_params,
++	.trigger = ralink_i2s_trigger,
++};
++
++static struct snd_soc_dai_driver ralink_i2s_dai = {
++	.name = DRV_NAME,
++	.probe = ralink_i2s_dai_probe,
++	.remove = ralink_i2s_dai_remove,
++	.ops = &ralink_i2s_dai_ops,
++	.capture = {
++		.stream_name = "I2S Capture",
++		.channels_min = 2,
++		.channels_max = 2,
++		.rate_min = 5512,
++		.rate_max = 192000,
++		.rates = SNDRV_PCM_RATE_CONTINUOUS,
++		.formats = SNDRV_PCM_FMTBIT_S16_LE,
++	},
++	.playback = {
++		.stream_name = "I2S Playback",
++		.channels_min = 2,
++		.channels_max = 2,
++		.rate_min = 5512,
++		.rate_max = 192000,
++		.rates = SNDRV_PCM_RATE_CONTINUOUS,
++		.formats = SNDRV_PCM_FMTBIT_S16_LE,
++	},
++	.symmetric_rates = 1,
++};
++
++static struct snd_pcm_hardware ralink_pcm_hardware = {
++	.info = SNDRV_PCM_INFO_MMAP |
++		SNDRV_PCM_INFO_MMAP_VALID |
++		SNDRV_PCM_INFO_INTERLEAVED |
++		SNDRV_PCM_INFO_BLOCK_TRANSFER,
++	.formats = SNDRV_PCM_FMTBIT_S16_LE,
++	.channels_min		= 2,
++	.channels_max		= 2,
++	.period_bytes_min	= PAGE_SIZE,
++	.period_bytes_max	= PAGE_SIZE * 2,
++	.periods_min		= 2,
++	.periods_max		= 128,
++	.buffer_bytes_max	= 128 * 1024,
++	.fifo_size		= RALINK_I2S_FIFO_SIZE,
++};
++
++static const struct snd_dmaengine_pcm_config ralink_dmaengine_pcm_config = {
++	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
++	.pcm_hardware = &ralink_pcm_hardware,
++	.prealloc_buffer_size = 256 * PAGE_SIZE,
++};
++
++static const struct snd_soc_component_driver ralink_i2s_component = {
++	.name = DRV_NAME,
++};
++
++static bool ralink_i2s_readable_reg(struct device *dev, unsigned int reg)
++{
++	return true;
++}
++
++static bool ralink_i2s_volatile_reg(struct device *dev, unsigned int reg)
++{
++	switch (reg) {
++	case I2S_REG_INT_STATUS:
++	case I2S_REG_FF_STATUS:
++		return true;
++	}
++	return false;
++}
++
++static bool ralink_i2s_writeable_reg(struct device *dev, unsigned int reg)
++{
++	switch (reg) {
++	case I2S_REG_FF_STATUS:
++	case I2S_REG_RREG:
++		return false;
++	}
++	return true;
++}
++
++static const struct regmap_config ralink_i2s_regmap_config = {
++	.reg_bits = 32,
++	.reg_stride = 4,
++	.val_bits = 32,
++	.writeable_reg = ralink_i2s_writeable_reg,
++	.readable_reg = ralink_i2s_readable_reg,
++	.volatile_reg = ralink_i2s_volatile_reg,
++	.max_register = I2S_REG_DIVINT,
++};
++
++#if (RALINK_I2S_INT_EN)
++static irqreturn_t ralink_i2s_irq(int irq, void *devid)
++{
++	struct ralink_i2s *i2s = devid;
++	u32 status;
++
++	regmap_read(i2s->regmap, I2S_REG_INT_STATUS, &status);
++	if (unlikely(!status))
++		return IRQ_NONE;
++
++	/* tx stats */
++	if (status & I2S_REG_INT_TX_MASK) {
++		if (status & I2S_REG_INT_TX_THRES)
++			i2s->txstats.belowthres++;
++		if (status & I2S_REG_INT_TX_UNRUN)
++			i2s->txstats.underrun++;
++		if (status & I2S_REG_INT_TX_OVRUN)
++			i2s->txstats.overrun++;
++		if (status & I2S_REG_INT_TX_FAULT)
++			i2s->txstats.dmafault++;
++	}
++
++	/* rx stats */
++	if (status & I2S_REG_INT_RX_MASK) {
++		if (status & I2S_REG_INT_RX_THRES)
++			i2s->rxstats.belowthres++;
++		if (status & I2S_REG_INT_RX_UNRUN)
++			i2s->rxstats.underrun++;
++		if (status & I2S_REG_INT_RX_OVRUN)
++			i2s->rxstats.overrun++;
++		if (status & I2S_REG_INT_RX_FAULT)
++			i2s->rxstats.dmafault++;
++	}
++
++	/* clean status bits */
++	regmap_write(i2s->regmap, I2S_REG_INT_STATUS, status);
++
++	return IRQ_HANDLED;
++}
++#endif
++
++#if IS_ENABLED(CONFIG_DEBUG_FS)
++static int ralink_i2s_stats_show(struct seq_file *s, void *unused)
++{
++        struct ralink_i2s *i2s = s->private;
++
++	seq_printf(s, "tx stats\n");
++	seq_printf(s, "\tbelow threshold\t%u\n", i2s->txstats.belowthres);
++	seq_printf(s, "\tunder run\t%u\n", i2s->txstats.underrun);
++	seq_printf(s, "\tover run\t%u\n", i2s->txstats.overrun);
++	seq_printf(s, "\tdma fault\t%u\n", i2s->txstats.dmafault);
++
++	seq_printf(s, "rx stats\n");
++	seq_printf(s, "\tbelow threshold\t%u\n", i2s->rxstats.belowthres);
++	seq_printf(s, "\tunder run\t%u\n", i2s->rxstats.underrun);
++	seq_printf(s, "\tover run\t%u\n", i2s->rxstats.overrun);
++	seq_printf(s, "\tdma fault\t%u\n", i2s->rxstats.dmafault);
++
++	ralink_i2s_dump_regs(i2s);
++
++	return 0;
++}
++
++static int ralink_i2s_stats_open(struct inode *inode, struct file *file)
++{
++        return single_open(file, ralink_i2s_stats_show, inode->i_private);
++}
++
++static const struct file_operations ralink_i2s_stats_ops = {
++        .open = ralink_i2s_stats_open,
++        .read = seq_read,
++        .llseek = seq_lseek,
++        .release = single_release,
++};
++
++static inline int ralink_i2s_debugfs_create(struct ralink_i2s *i2s)
++{
++        i2s->dbg_dir = debugfs_create_dir(dev_name(i2s->dev), NULL);
++        if (!i2s->dbg_dir)
++                return -ENOMEM;
++
++        i2s->dbg_stats = debugfs_create_file("stats", S_IRUGO,
++                        i2s->dbg_dir, i2s, &ralink_i2s_stats_ops);
++        if (!i2s->dbg_stats) {
++                debugfs_remove(i2s->dbg_dir);
++                return -ENOMEM;
++        }
++
++        return 0;
++}
++
++static inline void ralink_i2s_debugfs_remove(struct ralink_i2s *i2s)
++{
++	debugfs_remove(i2s->dbg_stats);
++	debugfs_remove(i2s->dbg_dir);
++}
++#else
++static inline int ralink_i2s_debugfs_create(struct ralink_i2s *i2s)
++{
++	return 0;
++}
++
++static inline void ralink_i2s_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
++{
++}
++#endif
++
++/*
++ * TODO: these refclk setup functions should use
++ * clock framework instead. hardcode it now.
++ */
++static void rt3350_refclk_setup(void)
++{
++	uint32_t data;
++
++	/* set refclk output 12Mhz clock */
++	data = rt_sysc_r32(0x2c);
++	data |= (0x1 << 8);
++	rt_sysc_w32(data, 0x2c);
++}
++
++static void rt3883_refclk_setup(void)
++{
++	uint32_t data;
++
++	/* set refclk output 12Mhz clock */
++	data = rt_sysc_r32(0x2c);
++	data &= ~(0x3 << 13);
++	data |= (0x1 << 13);
++	rt_sysc_w32(data, 0x2c);
++}
++
++static void rt3552_refclk_setup(void)
++{
++	uint32_t data;
++
++	/* set refclk output 12Mhz clock */
++	data = rt_sysc_r32(0x2c);
++	data &= ~(0xf << 8);
++	data |= (0x3 << 8);
++	rt_sysc_w32(data, 0x2c);
++}
++
++static void mt7620_refclk_setup(void)
++{
++	uint32_t data;
++
++	/* set refclk output 12Mhz clock */
++	data = rt_sysc_r32(0x2c);
++	data &= ~(0x7 << 9);
++	data |= 0x1 << 9;
++	rt_sysc_w32(data, 0x2c);
++}
++
++static void mt7621_refclk_setup(void)
++{
++	uint32_t data;
++
++	/* set refclk output 12Mhz clock */
++	data = rt_sysc_r32(0x2c);
++	data &= ~(0x1f << 18);
++	data |= (0x19 << 18);
++	data &= ~(0x1f << 12);
++	data |= (0x1 << 12);
++	data &= ~(0x7 << 9);
++	data |= (0x5 << 9);
++	rt_sysc_w32(data, 0x2c);
++}
++
++static void mt7628_refclk_setup(void)
++{
++	uint32_t data;
++
++	/* set i2s and refclk digital pad */
++	data = rt_sysc_r32(0x3c);
++	data |= 0x1f;
++	rt_sysc_w32(data, 0x3c);
++
++	/* Adjust REFCLK0's driving strength */
++	data = rt_sysc_r32(0x1354);
++	data &= ~(0x1 << 5);
++	rt_sysc_w32(data, 0x1354);
++	data = rt_sysc_r32(0x1364);
++	data |= ~(0x1 << 5);
++	rt_sysc_w32(data, 0x1364);
++
++	/* set refclk output 12Mhz clock */
++	data = rt_sysc_r32(0x2c);
++	data &= ~(0x7 << 9);
++	data |= 0x1 << 9;
++	rt_sysc_w32(data, 0x2c);
++}
++
++struct rt_i2s_data {
++	u32 flags;
++	void (*refclk_setup)(void);
++};
++
++struct rt_i2s_data rt3050_i2s_data = { .flags = RALINK_FLAGS_TXONLY };
++struct rt_i2s_data rt3350_i2s_data = { .flags = RALINK_FLAGS_TXONLY,
++	.refclk_setup = rt3350_refclk_setup };
++struct rt_i2s_data rt3883_i2s_data = {
++	.flags = (RALINK_FLAGS_LEFT_J | RALINK_FLAGS_RIGHT_J),
++	.refclk_setup = rt3883_refclk_setup };
++struct rt_i2s_data rt3352_i2s_data = { .refclk_setup = rt3552_refclk_setup};
++struct rt_i2s_data mt7620_i2s_data = { .refclk_setup = mt7620_refclk_setup};
++struct rt_i2s_data mt7621_i2s_data = { .refclk_setup = mt7621_refclk_setup};
++struct rt_i2s_data mt7628_i2s_data = {
++	.flags = (RALINK_FLAGS_ENDIAN | RALINK_FLAGS_24BIT |
++			RALINK_FLAGS_LEFT_J),
++	.refclk_setup = mt7628_refclk_setup};
++
++static const struct of_device_id ralink_i2s_match_table[] = {
++	{ .compatible = "ralink,rt3050-i2s",
++		.data = (void *)&rt3050_i2s_data },
++	{ .compatible = "ralink,rt3350-i2s",
++		.data = (void *)&rt3350_i2s_data },
++	{ .compatible = "ralink,rt3883-i2s",
++		.data = (void *)&rt3883_i2s_data },
++	{ .compatible = "ralink,rt3352-i2s",
++		.data = (void *)&rt3352_i2s_data },
++	{ .compatible = "mediatek,mt7620-i2s",
++		.data = (void *)&mt7620_i2s_data },
++	{ .compatible = "mediatek,mt7621-i2s",
++		.data = (void *)&mt7621_i2s_data },
++	{ .compatible = "mediatek,mt7628-i2s",
++		.data = (void *)&mt7628_i2s_data },
++};
++MODULE_DEVICE_TABLE(of, ralink_i2s_match_table);
++
++static int ralink_i2s_probe(struct platform_device *pdev)
++{
++	const struct of_device_id *match;
++	struct device_node *np = pdev->dev.of_node;
++	struct ralink_i2s *i2s;
++	struct resource *res;
++	int irq, ret;
++	u32 dma_req;
++	struct rt_i2s_data *data;
++
++	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
++	if (!i2s)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, i2s);
++	i2s->dev = &pdev->dev;
++
++	match = of_match_device(ralink_i2s_match_table, &pdev->dev);
++	if (!match)
++		return -EINVAL;
++	data = (struct rt_i2s_data *)match->data;
++	i2s->flags = data->flags;
++	/* setup out 12Mhz refclk to codec as mclk */
++	if (data->refclk_setup)
++		data->refclk_setup();
++
++	if (of_property_read_u32(np, "txdma-req", &dma_req)) {
++		dev_err(&pdev->dev, "no txdma-req define\n");
++		return -EINVAL;
++	}
++	i2s->txdma_req = (u16)dma_req;
++	if (!(i2s->flags & RALINK_FLAGS_TXONLY)) {
++		if (of_property_read_u32(np, "rxdma-req", &dma_req)) {
++			dev_err(&pdev->dev, "no rxdma-req define\n");
++			return -EINVAL;
++		}
++		i2s->rxdma_req = (u16)dma_req;
++	}
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	i2s->regs = devm_ioremap_resource(&pdev->dev, res);
++	if (IS_ERR(i2s->regs))
++		return PTR_ERR(i2s->regs);
++
++	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs,
++			&ralink_i2s_regmap_config);
++	if (IS_ERR(i2s->regmap)) {
++		dev_err(&pdev->dev, "regmap init failed\n");
++		return PTR_ERR(i2s->regmap);
++	}
++
++        irq = platform_get_irq(pdev, 0);
++        if (irq < 0) {
++                dev_err(&pdev->dev, "failed to get irq\n");
++                return -EINVAL;
++        }
++
++#if (RALINK_I2S_INT_EN)
++	ret = devm_request_irq(&pdev->dev, irq, ralink_i2s_irq,
++			0, dev_name(&pdev->dev), i2s);
++	if (ret) {
++		dev_err(&pdev->dev, "failed to request irq\n");
++		return ret;
++	}
++#endif
++
++	i2s->clk = devm_clk_get(&pdev->dev, NULL);
++	if (IS_ERR(i2s->clk)) {
++		dev_err(&pdev->dev, "no clock defined\n");
++		return PTR_ERR(i2s->clk);
++	}
++
++	ret = clk_prepare_enable(i2s->clk);
++	if (ret)
++		return ret;
++
++	ralink_i2s_init_dma_data(i2s, res);
++
++	device_reset(&pdev->dev);
++
++	ret = ralink_i2s_debugfs_create(i2s);
++	if (ret) {
++		dev_err(&pdev->dev, "create debugfs failed\n");
++		goto err_clk_disable;
++	}
++
++	/* enable 24bits support */
++	if (i2s->flags & RALINK_FLAGS_24BIT) {
++		ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S24_LE;
++		ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S24_LE;
++	}
++
++	/* enable big endian support */
++	if (i2s->flags & RALINK_FLAGS_ENDIAN) {
++		ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S16_BE;
++		ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S16_BE;
++		ralink_pcm_hardware.formats |= SNDRV_PCM_FMTBIT_S16_BE;
++		if (i2s->flags & RALINK_FLAGS_24BIT) {
++			ralink_i2s_dai.capture.formats |=
++				SNDRV_PCM_FMTBIT_S24_BE;
++			ralink_i2s_dai.playback.formats |=
++				SNDRV_PCM_FMTBIT_S24_BE;
++			ralink_pcm_hardware.formats |=
++				SNDRV_PCM_FMTBIT_S24_BE;
++		}
++	}
++
++	/* disable capture support */
++	if (i2s->flags & RALINK_FLAGS_TXONLY)
++		memset(&ralink_i2s_dai.capture, sizeof(ralink_i2s_dai.capture),
++				0);
++
++	ret = devm_snd_soc_register_component(&pdev->dev, &ralink_i2s_component,
++			&ralink_i2s_dai, 1);
++	if (ret)
++		goto err_debugfs;
++
++	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
++			&ralink_dmaengine_pcm_config,
++			SND_DMAENGINE_PCM_FLAG_COMPAT);
++	if (ret)
++		goto err_debugfs;
++
++	dev_info(i2s->dev, "mclk %luMHz\n", clk_get_rate(i2s->clk) / 1000000);
++
++	return 0;
++
++err_debugfs:
++	ralink_i2s_debugfs_remove(i2s);
++
++err_clk_disable:
++	clk_disable_unprepare(i2s->clk);
++
++	return ret;
++}
++
++static int ralink_i2s_remove(struct platform_device *pdev)
++{
++	struct ralink_i2s *i2s = platform_get_drvdata(pdev);
++
++	ralink_i2s_debugfs_remove(i2s);
++	clk_disable_unprepare(i2s->clk);
++
++	return 0;
++}
++
++static struct platform_driver ralink_i2s_driver = {
++	.probe = ralink_i2s_probe,
++	.remove = ralink_i2s_remove,
++	.driver = {
++		.name = DRV_NAME,
++		.of_match_table = ralink_i2s_match_table,
++	},
++};
++module_platform_driver(ralink_i2s_driver);
++
++MODULE_AUTHOR("Lars-Peter Clausen, <lars at metafoo.de>");
++MODULE_DESCRIPTION("Ralink/MediaTek I2S driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/target/linux/ramips/patches-5.10/0051-serial-add-ugly-custom-baud-rate-hack.patch b/target/linux/ramips/patches-5.10/0051-serial-add-ugly-custom-baud-rate-hack.patch
new file mode 100644
index 0000000000..4822226610
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0051-serial-add-ugly-custom-baud-rate-hack.patch
@@ -0,0 +1,22 @@
+From a7eb46e0ea4a11e4dfb56ab129bf816d1059a6c5 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Mon, 7 Dec 2015 17:31:08 +0100
+Subject: [PATCH 51/53] serial: add ugly custom baud rate hack
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ drivers/tty/serial/serial_core.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/tty/serial/serial_core.c
++++ b/drivers/tty/serial/serial_core.c
+@@ -416,6 +416,9 @@ uart_get_baud_rate(struct uart_port *por
+ 		break;
+ 	}
+ 
++	if (tty_termios_baud_rate(termios) == 2500000)
++		return 250000;
++
+ 	for (try = 0; try < 2; try++) {
+ 		baud = tty_termios_baud_rate(termios);
+ 
diff --git a/target/linux/ramips/patches-5.10/0052-pwm-add-mediatek-support.patch b/target/linux/ramips/patches-5.10/0052-pwm-add-mediatek-support.patch
new file mode 100644
index 0000000000..d2c5724c06
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0052-pwm-add-mediatek-support.patch
@@ -0,0 +1,217 @@
+From fc8f96309c21c1bc3276427309cd7d361347d66e Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Mon, 7 Dec 2015 17:16:50 +0100
+Subject: [PATCH 52/53] pwm: add mediatek support
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ drivers/pwm/Kconfig        |    9 +++
+ drivers/pwm/Makefile       |    1 +
+ drivers/pwm/pwm-mediatek.c |  173 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 183 insertions(+)
+ create mode 100644 drivers/pwm/pwm-mediatek.c
+
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -316,6 +316,15 @@ config PWM_MEDIATEK
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called pwm-mediatek.
+ 
++config PWM_MEDIATEK_RAMIPS
++	tristate "Mediatek PWM support"
++	depends on RALINK && OF
++	help
++	  Generic PWM framework driver for Mediatek ARM SoC.
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called pwm-mxs.
++
+ config PWM_MXS
+ 	tristate "Freescale MXS PWM support"
+ 	depends on ARCH_MXS && OF
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -29,6 +29,7 @@ obj-$(CONFIG_PWM_LPSS_PCI)	+= pwm-lpss-p
+ obj-$(CONFIG_PWM_LPSS_PLATFORM)	+= pwm-lpss-platform.o
+ obj-$(CONFIG_PWM_MESON)		+= pwm-meson.o
+ obj-$(CONFIG_PWM_MEDIATEK)	+= pwm-mediatek.o
++obj-$(CONFIG_PWM_MEDIATEK_RAMIPS)	+= pwm-mediatek-ramips.o
+ obj-$(CONFIG_PWM_MTK_DISP)	+= pwm-mtk-disp.o
+ obj-$(CONFIG_PWM_MXS)		+= pwm-mxs.o
+ obj-$(CONFIG_PWM_OMAP_DMTIMER)	+= pwm-omap-dmtimer.o
+--- /dev/null
++++ b/drivers/pwm/pwm-mediatek-ramips.c
+@@ -0,0 +1,173 @@
++/*
++ * Mediatek Pulse Width Modulator driver
++ *
++ * Copyright (C) 2015 John Crispin <blogic at openwrt.org>
++ *
++ * This file is licensed under the terms of the GNU General Public
++ * License version 2. This program is licensed "as is" without any
++ * warranty of any kind, whether express or implied.
++ */
++
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#define NUM_PWM		4
++
++/* PWM registers and bits definitions */
++#define PWMCON			0x00
++#define PWMHDUR			0x04
++#define PWMLDUR			0x08
++#define PWMGDUR			0x0c
++#define PWMWAVENUM		0x28
++#define PWMDWIDTH		0x2c
++#define PWMTHRES		0x30
++
++/**
++ * struct mtk_pwm_chip - struct representing pwm chip
++ *
++ * @mmio_base: base address of pwm chip
++ * @chip: linux pwm chip representation
++ */
++struct mtk_pwm_chip {
++	void __iomem *mmio_base;
++	struct pwm_chip chip;
++};
++
++static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip)
++{
++	return container_of(chip, struct mtk_pwm_chip, chip);
++}
++
++static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num,
++				  unsigned long offset)
++{
++	return ioread32(chip->mmio_base + 0x10 + (num * 0x40) + offset);
++}
++
++static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip,
++				    unsigned int num, unsigned long offset,
++				    unsigned long val)
++{
++	iowrite32(val, chip->mmio_base + 0x10 + (num * 0x40) + offset);
++}
++
++static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
++			    int duty_ns, int period_ns)
++{
++	struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
++	u32 resolution = 100 / 4;
++	u32 clkdiv = 0;
++
++	while (period_ns / resolution  > 8191) {
++		clkdiv++;
++		resolution *= 2;
++	}
++
++	if (clkdiv > 7)
++		return -1;
++
++	mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | clkdiv);
++	mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution);
++	mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution);
++	return 0;
++}
++
++static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++	struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
++	u32 val;
++
++	val = ioread32(pc->mmio_base);
++	val |= BIT(pwm->hwpwm);
++	iowrite32(val, pc->mmio_base);
++
++	return 0;
++}
++
++static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++	struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
++	u32 val;
++
++	val = ioread32(pc->mmio_base);
++	val &= ~BIT(pwm->hwpwm);
++	iowrite32(val, pc->mmio_base);
++}
++
++static const struct pwm_ops mtk_pwm_ops = {
++	.config = mtk_pwm_config,
++	.enable = mtk_pwm_enable,
++	.disable = mtk_pwm_disable,
++	.owner = THIS_MODULE,
++};
++
++static int mtk_pwm_probe(struct platform_device *pdev)
++{
++	struct mtk_pwm_chip *pc;
++	struct resource *r;
++	int ret;
++
++	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
++	if (!pc)
++		return -ENOMEM;
++
++	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	pc->mmio_base = devm_ioremap_resource(&pdev->dev, r);
++	if (IS_ERR(pc->mmio_base))
++		return PTR_ERR(pc->mmio_base);
++
++	platform_set_drvdata(pdev, pc);
++
++	pc->chip.dev = &pdev->dev;
++	pc->chip.ops = &mtk_pwm_ops;
++	pc->chip.base = -1;
++	pc->chip.npwm = NUM_PWM;
++
++	ret = pwmchip_add(&pc->chip);
++	if (ret < 0)
++		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
++
++	return ret;
++}
++
++static int mtk_pwm_remove(struct platform_device *pdev)
++{
++	struct mtk_pwm_chip *pc = platform_get_drvdata(pdev);
++	int i;
++
++	for (i = 0; i < NUM_PWM; i++)
++		pwm_disable(&pc->chip.pwms[i]);
++
++	return pwmchip_remove(&pc->chip);
++}
++
++static const struct of_device_id mtk_pwm_of_match[] = {
++	{ .compatible = "mediatek,mt7628-pwm" },
++	{ }
++};
++
++MODULE_DEVICE_TABLE(of, mtk_pwm_of_match);
++
++static struct platform_driver mtk_pwm_driver = {
++	.driver = {
++		.name = "mtk-pwm",
++		.owner = THIS_MODULE,
++		.of_match_table = mtk_pwm_of_match,
++	},
++	.probe = mtk_pwm_probe,
++	.remove = mtk_pwm_remove,
++};
++
++module_platform_driver(mtk_pwm_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("John Crispin <blogic at openwrt.org>");
++MODULE_ALIAS("platform:mtk-pwm");
diff --git a/target/linux/ramips/patches-5.10/0069-awake-rt305x-dwc2-controller.patch b/target/linux/ramips/patches-5.10/0069-awake-rt305x-dwc2-controller.patch
new file mode 100644
index 0000000000..7110a5b808
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0069-awake-rt305x-dwc2-controller.patch
@@ -0,0 +1,15 @@
+--- a/drivers/usb/dwc2/platform.c
++++ b/drivers/usb/dwc2/platform.c
+@@ -431,6 +431,12 @@ static int dwc2_driver_probe(struct plat
+ 	if (retval)
+ 		return retval;
+ 
++	/* Enable USB port before any regs access */
++	if (readl(hsotg->regs + PCGCTL) & 0x0f) {
++		writel(0x00, hsotg->regs + PCGCTL);
++		/* TODO: mdelay(25) here? vendor driver don't use it */
++	}
++
+ 	hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg);
+ 
+ 	retval = dwc2_get_dr_mode(hsotg);
diff --git a/target/linux/ramips/patches-5.10/0070-weak_reordering.patch b/target/linux/ramips/patches-5.10/0070-weak_reordering.patch
new file mode 100644
index 0000000000..fe3fdec289
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0070-weak_reordering.patch
@@ -0,0 +1,10 @@
+--- a/arch/mips/ralink/Kconfig
++++ b/arch/mips/ralink/Kconfig
+@@ -57,6 +57,7 @@ choice
+ 		select COMMON_CLK
+ 		select CLKSRC_MIPS_GIC
+ 		select HAVE_PCI if PCI_MT7621
++		select WEAK_REORDERING_BEYOND_LLSC
+ endchoice
+ 
+ choice
diff --git a/target/linux/ramips/patches-5.10/0098-disable_cm.patch b/target/linux/ramips/patches-5.10/0098-disable_cm.patch
new file mode 100644
index 0000000000..9b280aef99
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0098-disable_cm.patch
@@ -0,0 +1,19 @@
+--- a/arch/mips/kernel/mips-cm.c
++++ b/arch/mips/kernel/mips-cm.c
+@@ -233,6 +233,7 @@ int mips_cm_probe(void)
+ 
+ 	/* disable CM regions */
+ 	write_gcr_reg0_base(CM_GCR_REGn_BASE_BASEADDR);
++	/*
+ 	write_gcr_reg0_mask(CM_GCR_REGn_MASK_ADDRMASK);
+ 	write_gcr_reg1_base(CM_GCR_REGn_BASE_BASEADDR);
+ 	write_gcr_reg1_mask(CM_GCR_REGn_MASK_ADDRMASK);
+@@ -240,7 +241,7 @@ int mips_cm_probe(void)
+ 	write_gcr_reg2_mask(CM_GCR_REGn_MASK_ADDRMASK);
+ 	write_gcr_reg3_base(CM_GCR_REGn_BASE_BASEADDR);
+ 	write_gcr_reg3_mask(CM_GCR_REGn_MASK_ADDRMASK);
+-
++*/
+ 	/* probe for an L2-only sync region */
+ 	mips_cm_probe_l2sync();
+ 
diff --git a/target/linux/ramips/patches-5.10/0100-staging-mt7621-pci-simplify-mt7621_pcie_init_virtual.patch b/target/linux/ramips/patches-5.10/0100-staging-mt7621-pci-simplify-mt7621_pcie_init_virtual.patch
new file mode 100644
index 0000000000..b9bcd63488
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0100-staging-mt7621-pci-simplify-mt7621_pcie_init_virtual.patch
@@ -0,0 +1,133 @@
+From b327cd58c3fec1c6382128e929eab9bc0d68e912 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Sun, 8 Mar 2020 10:19:27 +0100
+Subject: [PATCH] staging: mt7621-pci: simplify
+ 'mt7621_pcie_init_virtual_bridges' function
+
+Function 'mt7621_pcie_init_virtual_bridges' is a bit mess and can be
+refactorized properly in a cleaner way. Introduce new 'pcie_rmw' inline
+function helper to do clear and set the correct bits this function needs
+to work.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200308091928.17177-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 85 +++++++++++++--------------------
+ 1 file changed, 33 insertions(+), 52 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -57,13 +57,13 @@
+ #define RALINK_PCI_IOBASE		0x002C
+ 
+ /* PCICFG virtual bridges */
+-#define MT7621_BR0_MASK			GENMASK(19, 16)
+-#define MT7621_BR1_MASK			GENMASK(23, 20)
+-#define MT7621_BR2_MASK			GENMASK(27, 24)
+-#define MT7621_BR_ALL_MASK		GENMASK(27, 16)
+-#define MT7621_BR0_SHIFT		16
+-#define MT7621_BR1_SHIFT		20
+-#define MT7621_BR2_SHIFT		24
++#define PCIE_P2P_MAX			3
++#define PCIE_P2P_BR_DEVNUM_SHIFT(p)	(16 + (p) * 4)
++#define PCIE_P2P_BR_DEVNUM0_SHIFT	PCIE_P2P_BR_DEVNUM_SHIFT(0)
++#define PCIE_P2P_BR_DEVNUM1_SHIFT	PCIE_P2P_BR_DEVNUM_SHIFT(1)
++#define PCIE_P2P_BR_DEVNUM2_SHIFT	PCIE_P2P_BR_DEVNUM_SHIFT(2)
++#define PCIE_P2P_BR_DEVNUM_MASK		0xf
++#define PCIE_P2P_BR_DEVNUM_MASK_FULL	(0xfff << PCIE_P2P_BR_DEVNUM0_SHIFT)
+ 
+ /* PCIe RC control registers */
+ #define MT7621_PCIE_OFFSET		0x2000
+@@ -154,6 +154,15 @@ static inline void pcie_write(struct mt7
+ 	writel(val, pcie->base + reg);
+ }
+ 
++static inline void pcie_rmw(struct mt7621_pcie *pcie, u32 reg, u32 clr, u32 set)
++{
++	u32 val = readl(pcie->base + reg);
++
++	val &= ~clr;
++	val |= set;
++	writel(val, pcie->base + reg);
++}
++
+ static inline u32 pcie_port_read(struct mt7621_pcie_port *port, u32 reg)
+ {
+ 	return readl(port->base + reg);
+@@ -554,7 +563,9 @@ static void mt7621_pcie_enable_ports(str
+ static int mt7621_pcie_init_virtual_bridges(struct mt7621_pcie *pcie)
+ {
+ 	u32 pcie_link_status = 0;
+-	u32 val = 0;
++	u32 n;
++	int i;
++	u32 p2p_br_devnum[PCIE_P2P_MAX];
+ 	struct mt7621_pcie_port *port;
+ 
+ 	list_for_each_entry(port, &pcie->ports, list) {
+@@ -567,50 +578,20 @@ static int mt7621_pcie_init_virtual_brid
+ 	if (pcie_link_status == 0)
+ 		return -1;
+ 
+-	/*
+-	 * pcie(2/1/0) link status	pcie2_num	pcie1_num	pcie0_num
+-	 * 3'b000			x		x		x
+-	 * 3'b001			x		x		0
+-	 * 3'b010			x		0		x
+-	 * 3'b011			x		1		0
+-	 * 3'b100			0		x		x
+-	 * 3'b101			1		x		0
+-	 * 3'b110			1		0		x
+-	 * 3'b111			2		1		0
+-	 */
+-	switch (pcie_link_status) {
+-	case 2:
+-		val = pcie_read(pcie, RALINK_PCI_PCICFG_ADDR);
+-		val &= ~(MT7621_BR0_MASK | MT7621_BR1_MASK);
+-		val |= 0x1 << MT7621_BR0_SHIFT;
+-		val |= 0x0 << MT7621_BR1_SHIFT;
+-		pcie_write(pcie, val, RALINK_PCI_PCICFG_ADDR);
+-		break;
+-	case 4:
+-		val = pcie_read(pcie, RALINK_PCI_PCICFG_ADDR);
+-		val &= ~MT7621_BR_ALL_MASK;
+-		val |= 0x1 << MT7621_BR0_SHIFT;
+-		val |= 0x2 << MT7621_BR1_SHIFT;
+-		val |= 0x0 << MT7621_BR2_SHIFT;
+-		pcie_write(pcie, val, RALINK_PCI_PCICFG_ADDR);
+-		break;
+-	case 5:
+-		val = pcie_read(pcie, RALINK_PCI_PCICFG_ADDR);
+-		val &= ~MT7621_BR_ALL_MASK;
+-		val |= 0x0 << MT7621_BR0_SHIFT;
+-		val |= 0x2 << MT7621_BR1_SHIFT;
+-		val |= 0x1 << MT7621_BR2_SHIFT;
+-		pcie_write(pcie, val, RALINK_PCI_PCICFG_ADDR);
+-		break;
+-	case 6:
+-		val = pcie_read(pcie, RALINK_PCI_PCICFG_ADDR);
+-		val &= ~MT7621_BR_ALL_MASK;
+-		val |= 0x2 << MT7621_BR0_SHIFT;
+-		val |= 0x0 << MT7621_BR1_SHIFT;
+-		val |= 0x1 << MT7621_BR2_SHIFT;
+-		pcie_write(pcie, val, RALINK_PCI_PCICFG_ADDR);
+-		break;
+-	}
++	n = 0;
++	for (i = 0; i < PCIE_P2P_MAX; i++)
++		if (pcie_link_status & BIT(i))
++			p2p_br_devnum[i] = n++;
++
++	for (i = 0; i < PCIE_P2P_MAX; i++)
++		if ((pcie_link_status & BIT(i)) == 0)
++			p2p_br_devnum[i] = n++;
++
++	pcie_rmw(pcie, RALINK_PCI_CONFIG_ADDR,
++		 PCIE_P2P_BR_DEVNUM_MASK_FULL,
++		 (p2p_br_devnum[0] << PCIE_P2P_BR_DEVNUM0_SHIFT) |
++		 (p2p_br_devnum[1] << PCIE_P2P_BR_DEVNUM1_SHIFT) |
++		 (p2p_br_devnum[2] << PCIE_P2P_BR_DEVNUM2_SHIFT));
+ 
+ 	return 0;
+ }
diff --git a/target/linux/ramips/patches-5.10/0101-staging-mt7621-pci-enable-clock-bit-for-each-port.patch b/target/linux/ramips/patches-5.10/0101-staging-mt7621-pci-enable-clock-bit-for-each-port.patch
new file mode 100644
index 0000000000..3939280a16
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0101-staging-mt7621-pci-enable-clock-bit-for-each-port.patch
@@ -0,0 +1,74 @@
+From 550fabd71d7fcdfe099bbf41e00e28719737161e Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Tue, 10 Mar 2020 12:34:59 +0100
+Subject: [PATCH] staging: mt7621-pci: enable clock bit for each port
+
+The clock related code concerns me from the very beginning because
+there are some set ups got from legacy driver that are not documented
+anywhere. According to the programming guide 0x7c is 'CPE_ROSC_SEL1'
+register and 0x80 is 'CPU_CPE_CN'. I do think this set up is not needed
+at all and the proper thing to do is just enable the clock bit for each
+pcie port. Hence remove useless code and do the right thing which is
+setting up the clock bit for each port enabled.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200310113459.30539-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 17 ++++++-----------
+ 1 file changed, 6 insertions(+), 11 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -45,8 +45,6 @@
+ 
+ /* rt_sysc_membase relative registers */
+ #define RALINK_CLKCFG1			0x30
+-#define RALINK_PCIE_CLK_GEN		0x7c
+-#define RALINK_PCIE_CLK_GEN1		0x80
+ 
+ /* Host-PCI bridge registers */
+ #define RALINK_PCI_PCICFG_ADDR		0x0000
+@@ -85,10 +83,6 @@
+ #define PCIE_PORT_CLK_EN(x)		BIT(24 + (x))
+ #define PCIE_PORT_LINKUP		BIT(0)
+ 
+-#define PCIE_CLK_GEN_EN			BIT(31)
+-#define PCIE_CLK_GEN_DIS		0
+-#define PCIE_CLK_GEN1_DIS		GENMASK(30, 24)
+-#define PCIE_CLK_GEN1_EN		(BIT(27) | BIT(25))
+ #define MEMORY_BASE			0x0
+ #define PERST_MODE_MASK			GENMASK(11, 10)
+ #define PERST_MODE_GPIO			BIT(10)
+@@ -233,6 +227,11 @@ static inline bool mt7621_pcie_port_is_l
+ 	return (pcie_port_read(port, RALINK_PCI_STATUS) & PCIE_PORT_LINKUP) != 0;
+ }
+ 
++static inline void mt7621_pcie_port_clk_enable(struct mt7621_pcie_port *port)
++{
++	rt_sysc_m32(0, PCIE_PORT_CLK_EN(port->slot), RALINK_CLKCFG1);
++}
++
+ static inline void mt7621_pcie_port_clk_disable(struct mt7621_pcie_port *port)
+ {
+ 	rt_sysc_m32(PCIE_PORT_CLK_EN(port->slot), 0, RALINK_CLKCFG1);
+@@ -501,11 +500,6 @@ static void mt7621_pcie_init_ports(struc
+ 		}
+ 	}
+ 
+-	rt_sysc_m32(0x30, 2 << 4, SYSC_REG_SYSTEM_CONFIG1);
+-	rt_sysc_m32(PCIE_CLK_GEN_EN, PCIE_CLK_GEN_DIS, RALINK_PCIE_CLK_GEN);
+-	rt_sysc_m32(PCIE_CLK_GEN1_DIS, PCIE_CLK_GEN1_EN, RALINK_PCIE_CLK_GEN1);
+-	rt_sysc_m32(PCIE_CLK_GEN_DIS, PCIE_CLK_GEN_EN, RALINK_PCIE_CLK_GEN);
+-	msleep(50);
+ 	reset_control_deassert(pcie->rst);
+ }
+ 
+@@ -542,6 +536,7 @@ static void mt7621_pcie_enable_ports(str
+ 
+ 	list_for_each_entry(port, &pcie->ports, list) {
+ 		if (port->enabled) {
++			mt7621_pcie_port_clk_enable(port);
+ 			mt7621_pcie_enable_port(port);
+ 			dev_info(dev, "PCIE%d enabled\n", num_slots_enabled);
+ 			num_slots_enabled++;
diff --git a/target/linux/ramips/patches-5.10/0102-staging-mt7621-pci-use-gpios-for-properly-reset.patch b/target/linux/ramips/patches-5.10/0102-staging-mt7621-pci-use-gpios-for-properly-reset.patch
new file mode 100644
index 0000000000..1b7828f1e6
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0102-staging-mt7621-pci-use-gpios-for-properly-reset.patch
@@ -0,0 +1,222 @@
+From 227a8bf421ff8b085444e51e471ef06a87228cfd Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 13 Mar 2020 21:09:08 +0100
+Subject: [PATCH] staging: mt7621-pci: use gpios for properly reset
+
+Original driver code was using three gpio's for reset
+asserts and deasserts the pcis. Instead of using that
+a general reset control with a perst gpio was introduced
+and it seems it is partially working but sometimes there
+are some unexpected hangs on boot. This commit make use of
+the three original gpios using 'reset-gpios' property of
+the device tree and removes the reset line and perst gpio.
+According to the mediatek aplication note v0.1 there are
+three gpios used for pcie ports reset control: gpio#19,
+gpio#8 and gpio#7 for slots 0, 1 and 2 respectively.
+This schema can be used separately for mt7621A but in some
+boards due to pin share issue, if the PCM and I2S function
+are enable at the same time, there are no enough GPIO to
+control per-port PCIe reset. In those cases gpio#19 is enought
+for reset the three ports together. Because of this we just
+try to get the three gpios but if some of them fail we are not
+failing in boot process, just prints a kernel notice and take
+after into account if the descriptor is or not valid in order
+to use it. All of them are set as GPIO output low configuration.
+The gpio descriptor's API takes device tree property into account
+and invert value if the pin is configured as active low.
+So we also have to properly request pins from device tree
+and set values correct in assert and deassert functions.
+After this changes the order to make all assert and
+deassert in the 'probe' process makes more sense:
+* Parse device tree.
+* make assert of the RC's and EP's before doing anything else.
+* make deassert of the RC's before initializing the phy.
+* Init the phy.
+* make deassert of the EP's before initialize pci ports.
+* Normal PCI initialization.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200313200913.24321-2-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 84 ++++++++++++++++++++-------------
+ 1 file changed, 51 insertions(+), 33 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -95,6 +95,7 @@
+  * @pcie: pointer to PCIe host info
+  * @phy: pointer to PHY control block
+  * @pcie_rst: pointer to port reset control
++ * @gpio_rst: gpio reset
+  * @slot: port slot
+  * @enabled: indicates if port is enabled
+  */
+@@ -104,6 +105,7 @@ struct mt7621_pcie_port {
+ 	struct mt7621_pcie *pcie;
+ 	struct phy *phy;
+ 	struct reset_control *pcie_rst;
++	struct gpio_desc *gpio_rst;
+ 	u32 slot;
+ 	bool enabled;
+ };
+@@ -117,8 +119,6 @@ struct mt7621_pcie_port {
+  * @offset: IO / Memory offset
+  * @dev: Pointer to PCIe device
+  * @ports: pointer to PCIe port information
+- * @perst: gpio reset
+- * @rst: pointer to pcie reset
+  * @resets_inverted: depends on chip revision
+  * reset lines are inverted.
+  */
+@@ -133,8 +133,6 @@ struct mt7621_pcie {
+ 		resource_size_t io;
+ 	} offset;
+ 	struct list_head ports;
+-	struct gpio_desc *perst;
+-	struct reset_control *rst;
+ 	bool resets_inverted;
+ };
+ 
+@@ -210,16 +208,16 @@ static void write_config(struct mt7621_p
+ 	pcie_write(pcie, val, RALINK_PCI_CONFIG_DATA);
+ }
+ 
+-static inline void mt7621_perst_gpio_pcie_assert(struct mt7621_pcie *pcie)
++static inline void mt7621_rst_gpio_pcie_assert(struct mt7621_pcie_port *port)
+ {
+-	gpiod_set_value(pcie->perst, 0);
+-	mdelay(PERST_DELAY_US);
++	if (port->gpio_rst)
++		gpiod_set_value(port->gpio_rst, 1);
+ }
+ 
+-static inline void mt7621_perst_gpio_pcie_deassert(struct mt7621_pcie *pcie)
++static inline void mt7621_rst_gpio_pcie_deassert(struct mt7621_pcie_port *port)
+ {
+-	gpiod_set_value(pcie->perst, 1);
+-	mdelay(PERST_DELAY_US);
++	if (port->gpio_rst)
++		gpiod_set_value(port->gpio_rst, 0);
+ }
+ 
+ static inline bool mt7621_pcie_port_is_linkup(struct mt7621_pcie_port *port)
+@@ -367,6 +365,13 @@ static int mt7621_pcie_parse_port(struct
+ 	if (IS_ERR(port->phy))
+ 		return PTR_ERR(port->phy);
+ 
++	port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot,
++						       GPIOD_OUT_LOW);
++	if (IS_ERR(port->gpio_rst)) {
++		dev_err(dev, "Failed to get GPIO for PCIe%d\n", slot);
++		return PTR_ERR(port->gpio_rst);
++	}
++
+ 	port->slot = slot;
+ 	port->pcie = pcie;
+ 
+@@ -383,12 +388,6 @@ static int mt7621_pcie_parse_dt(struct m
+ 	struct resource regs;
+ 	int err;
+ 
+-	pcie->perst = devm_gpiod_get(dev, "perst", GPIOD_OUT_HIGH);
+-	if (IS_ERR(pcie->perst)) {
+-		dev_err(dev, "failed to get gpio perst\n");
+-		return PTR_ERR(pcie->perst);
+-	}
+-
+ 	err = of_address_to_resource(node, 0, &regs);
+ 	if (err) {
+ 		dev_err(dev, "missing \"reg\" property\n");
+@@ -399,12 +398,6 @@ static int mt7621_pcie_parse_dt(struct m
+ 	if (IS_ERR(pcie->base))
+ 		return PTR_ERR(pcie->base);
+ 
+-	pcie->rst = devm_reset_control_get_exclusive(dev, "pcie");
+-	if (PTR_ERR(pcie->rst) == -EPROBE_DEFER) {
+-		dev_err(dev, "failed to get pcie reset control\n");
+-		return PTR_ERR(pcie->rst);
+-	}
+-
+ 	for_each_available_child_of_node(node, child) {
+ 		int slot;
+ 
+@@ -458,16 +451,49 @@ static int mt7621_pcie_init_port(struct
+ 	return 0;
+ }
+ 
++static void mt7621_pcie_reset_assert(struct mt7621_pcie *pcie)
++{
++	struct mt7621_pcie_port *port;
++
++	list_for_each_entry(port, &pcie->ports, list) {
++		/* PCIe RC reset assert */
++		mt7621_control_assert(port);
++
++		/* PCIe EP reset assert */
++		mt7621_rst_gpio_pcie_assert(port);
++	}
++
++	mdelay(PERST_DELAY_US);
++}
++
++static void mt7621_pcie_reset_rc_deassert(struct mt7621_pcie *pcie)
++{
++	struct mt7621_pcie_port *port;
++
++	list_for_each_entry(port, &pcie->ports, list)
++		mt7621_control_deassert(port);
++}
++
++static void mt7621_pcie_reset_ep_deassert(struct mt7621_pcie *pcie)
++{
++	struct mt7621_pcie_port *port;
++
++	list_for_each_entry(port, &pcie->ports, list)
++		mt7621_rst_gpio_pcie_deassert(port);
++
++	mdelay(PERST_DELAY_US);
++}
++
+ static void mt7621_pcie_init_ports(struct mt7621_pcie *pcie)
+ {
+ 	struct device *dev = pcie->dev;
+ 	struct mt7621_pcie_port *port, *tmp;
+-	u32 val = 0;
+ 	int err;
+ 
+ 	rt_sysc_m32(PERST_MODE_MASK, PERST_MODE_GPIO, MT7621_GPIO_MODE);
+ 
+-	mt7621_perst_gpio_pcie_assert(pcie);
++	mt7621_pcie_reset_assert(pcie);
++	mt7621_pcie_reset_rc_deassert(pcie);
+ 
+ 	list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ 		u32 slot = port->slot;
+@@ -476,16 +502,10 @@ static void mt7621_pcie_init_ports(struc
+ 		if (err) {
+ 			dev_err(dev, "Initiating port %d failed\n", slot);
+ 			list_del(&port->list);
+-		} else {
+-			val = read_config(pcie, slot, PCIE_FTS_NUM);
+-			dev_info(dev, "Port %d N_FTS = %x\n", slot,
+-				 (unsigned int)val);
+ 		}
+ 	}
+ 
+-	reset_control_assert(pcie->rst);
+-
+-	mt7621_perst_gpio_pcie_deassert(pcie);
++	mt7621_pcie_reset_ep_deassert(pcie);
+ 
+ 	list_for_each_entry(port, &pcie->ports, list) {
+ 		u32 slot = port->slot;
+@@ -499,8 +519,6 @@ static void mt7621_pcie_init_ports(struc
+ 			port->enabled = false;
+ 		}
+ 	}
+-
+-	reset_control_deassert(pcie->rst);
+ }
+ 
+ static void mt7621_pcie_enable_port(struct mt7621_pcie_port *port)
diff --git a/target/linux/ramips/patches-5.10/0103-staging-mt7621-pci-change-value-for-PERST_DELAY_MS.patch b/target/linux/ramips/patches-5.10/0103-staging-mt7621-pci-change-value-for-PERST_DELAY_MS.patch
new file mode 100644
index 0000000000..3d86355b29
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0103-staging-mt7621-pci-change-value-for-PERST_DELAY_MS.patch
@@ -0,0 +1,45 @@
+From e462e7d3211479df42357a620fa788a2257556b7 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 13 Mar 2020 21:09:09 +0100
+Subject: [PATCH] staging: mt7621-pci: change value for 'PERST_DELAY_MS'
+
+Value of 'PERST_DELAY_MS' is too high and it is ok just
+to set up to 100 ms. Update also define name from
+'PERST_DELAY_US' into 'PERST_DELAY_MS'
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200313200913.24321-3-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -86,7 +86,7 @@
+ #define MEMORY_BASE			0x0
+ #define PERST_MODE_MASK			GENMASK(11, 10)
+ #define PERST_MODE_GPIO			BIT(10)
+-#define PERST_DELAY_US			1000
++#define PERST_DELAY_MS			100
+ 
+ /**
+  * struct mt7621_pcie_port - PCIe port information
+@@ -463,7 +463,7 @@ static void mt7621_pcie_reset_assert(str
+ 		mt7621_rst_gpio_pcie_assert(port);
+ 	}
+ 
+-	mdelay(PERST_DELAY_US);
++	mdelay(PERST_DELAY_MS);
+ }
+ 
+ static void mt7621_pcie_reset_rc_deassert(struct mt7621_pcie *pcie)
+@@ -481,7 +481,7 @@ static void mt7621_pcie_reset_ep_deasser
+ 	list_for_each_entry(port, &pcie->ports, list)
+ 		mt7621_rst_gpio_pcie_deassert(port);
+ 
+-	mdelay(PERST_DELAY_US);
++	mdelay(PERST_DELAY_MS);
+ }
+ 
+ static void mt7621_pcie_init_ports(struct mt7621_pcie *pcie)
diff --git a/target/linux/ramips/patches-5.10/0104-staging-mt7621-pci-release-gpios-after-pci-initializ.patch b/target/linux/ramips/patches-5.10/0104-staging-mt7621-pci-release-gpios-after-pci-initializ.patch
new file mode 100644
index 0000000000..f24bf833c9
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0104-staging-mt7621-pci-release-gpios-after-pci-initializ.patch
@@ -0,0 +1,76 @@
+From 4d6a758f2cd2122a7d895f913854c13da62ba6df Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 13 Mar 2020 21:09:12 +0100
+Subject: [PATCH] staging: mt7621-pci: release gpios after pci initialization
+
+R3G's LEDs fail to initialize because one of them uses GPIO8
+Hence, release the GPIO resources after PCIe initialization
+and properly release also in driver error path.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200313200913.24321-6-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 23 ++++++++++++++++++-----
+ 1 file changed, 18 insertions(+), 5 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -484,6 +484,15 @@ static void mt7621_pcie_reset_ep_deasser
+ 	mdelay(PERST_DELAY_MS);
+ }
+ 
++static void mt7621_pcie_release_gpios(struct mt7621_pcie *pcie)
++{
++	struct mt7621_pcie_port *port;
++
++	list_for_each_entry(port, &pcie->ports, list)
++		if (port->gpio_rst)
++			gpiod_put(port->gpio_rst);
++}
++
+ static void mt7621_pcie_init_ports(struct mt7621_pcie *pcie)
+ {
+ 	struct device *dev = pcie->dev;
+@@ -683,7 +692,8 @@ static int mt7621_pci_probe(struct platf
+ 	err = mt7621_pcie_init_virtual_bridges(pcie);
+ 	if (err) {
+ 		dev_err(dev, "Nothing is connected in virtual bridges. Exiting...");
+-		return 0;
++		err = 0;
++		goto out_release_gpios;
+ 	}
+ 
+ 	mt7621_pcie_enable_ports(pcie);
+@@ -691,7 +701,7 @@ static int mt7621_pci_probe(struct platf
+ 	err = mt7621_pci_parse_request_of_pci_ranges(pcie);
+ 	if (err) {
+ 		dev_err(dev, "Error requesting pci resources from ranges");
+-		return err;
++		goto out_release_gpios;
+ 	}
+ 
+ 	setup_cm_memory_region(pcie);
+@@ -699,16 +709,19 @@ static int mt7621_pci_probe(struct platf
+ 	err = mt7621_pcie_request_resources(pcie, &res);
+ 	if (err) {
+ 		dev_err(dev, "Error requesting resources\n");
+-		return err;
++		goto out_release_gpios;
+ 	}
+ 
+ 	err = mt7621_pcie_register_host(bridge, &res);
+ 	if (err) {
+ 		dev_err(dev, "Error registering host\n");
+-		return err;
++		goto out_release_gpios;
+ 	}
+ 
+-	return 0;
++out_release_gpios:
++	mt7621_pcie_release_gpios(pcie);
++
++	return err;
+ }
+ 
+ static const struct of_device_id mt7621_pci_ids[] = {
diff --git a/target/linux/ramips/patches-5.10/0105-staging-mt7621-pci-delete-no-more-needed-mt7621_rese.patch b/target/linux/ramips/patches-5.10/0105-staging-mt7621-pci-delete-no-more-needed-mt7621_rese.patch
new file mode 100644
index 0000000000..fc509e945b
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0105-staging-mt7621-pci-delete-no-more-needed-mt7621_rese.patch
@@ -0,0 +1,46 @@
+From 4be54c3a495f08c05a8e485566e5b88cd3537f16 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 13 Mar 2020 21:09:13 +0100
+Subject: [PATCH] staging: mt7621-pci: delete no more needed
+ 'mt7621_reset_port'
+
+After review all the resets at the beggining the function
+'mt7621_reset_port' is not needed anymore. Hence delete it
+and its uses.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200313200913.24321-7-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -255,13 +255,6 @@ static inline void mt7621_control_deasse
+ 		reset_control_assert(port->pcie_rst);
+ }
+ 
+-static void mt7621_reset_port(struct mt7621_pcie_port *port)
+-{
+-	mt7621_control_assert(port);
+-	msleep(100);
+-	mt7621_control_deassert(port);
+-}
+-
+ static void setup_cm_memory_region(struct mt7621_pcie *pcie)
+ {
+ 	struct resource *mem_resource = &pcie->mem;
+@@ -427,12 +420,6 @@ static int mt7621_pcie_init_port(struct
+ 	u32 slot = port->slot;
+ 	int err;
+ 
+-	/*
+-	 * Any MT7621 Ralink pcie controller that doesn't have 0x0101 at
+-	 * the end of the chip_id has inverted PCI resets.
+-	 */
+-	mt7621_reset_port(port);
+-
+ 	err = phy_init(port->phy);
+ 	if (err) {
+ 		dev_err(dev, "failed to initialize port%d phy\n", slot);
diff --git a/target/linux/ramips/patches-5.10/0106-staging-mt7621-pci-phy-add-mt7621_phy_rmw-to-simplif.patch b/target/linux/ramips/patches-5.10/0106-staging-mt7621-pci-phy-add-mt7621_phy_rmw-to-simplif.patch
new file mode 100644
index 0000000000..234af32308
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0106-staging-mt7621-pci-phy-add-mt7621_phy_rmw-to-simplif.patch
@@ -0,0 +1,234 @@
+From bf0c6782e5b9a6deee4e223655325dc004fae8dd Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Sun, 15 Mar 2020 17:01:54 +0100
+Subject: [PATCH] staging: mt7621-pci-phy: add 'mt7621_phy_rmw' to simplify
+ code
+
+In order to simplify driver code and decrease a bit LOC add new
+function 'mt7621_phy_rmw' where clear and set bits are passed as
+arguments.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200315160154.10292-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 158 +++++++++++-------------
+ 1 file changed, 71 insertions(+), 87 deletions(-)
+
+--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
++++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
+@@ -120,17 +120,25 @@ static inline void phy_write(struct mt76
+ 	regmap_write(phy->regmap, reg, val);
+ }
+ 
++static inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy,
++				  u32 reg, u32 clr, u32 set)
++{
++	u32 val = phy_read(phy, reg);
++
++	val &= ~clr;
++	val |= set;
++	phy_write(phy, val, reg);
++}
++
+ static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy,
+ 				   struct mt7621_pci_phy_instance *instance)
+ {
+ 	u32 offset = (instance->index != 1) ?
+ 		RG_PE1_PIPE_REG : RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH;
+-	u32 reg;
+ 
+-	reg = phy_read(phy, offset);
+-	reg &= ~(RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC);
+-	reg |= (RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC);
+-	phy_write(phy, reg, offset);
++	mt7621_phy_rmw(phy, offset,
++		       RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC,
++		       RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC);
+ }
+ 
+ static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy,
+@@ -139,97 +147,77 @@ static void mt7621_set_phy_for_ssc(struc
+ 	struct device *dev = phy->dev;
+ 	u32 reg = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0);
+ 	u32 offset;
+-	u32 val;
+ 
+ 	reg = (reg >> 6) & 0x7;
+ 	/* Set PCIe Port PHY to disable SSC */
+ 	/* Debug Xtal Type */
+-	val = phy_read(phy, RG_PE1_FRC_H_XTAL_REG);
+-	val &= ~(RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE);
+-	val |= RG_PE1_FRC_H_XTAL_TYPE;
+-	val |= RG_PE1_H_XTAL_TYPE_VAL(0x00);
+-	phy_write(phy, val, RG_PE1_FRC_H_XTAL_REG);
++	mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG,
++		       RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE,
++		       RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE_VAL(0x00));
+ 
+ 	/* disable port */
+ 	offset = (instance->index != 1) ?
+ 		RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
+-	val = phy_read(phy, offset);
+-	val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
+-	val |= RG_PE1_FRC_PHY_EN;
+-	phy_write(phy, val, offset);
+-
+-	/* Set Pre-divider ratio (for host mode) */
+-	val = phy_read(phy, RG_PE1_H_PLL_REG);
+-	val &= ~(RG_PE1_H_PLL_PREDIV);
++	mt7621_phy_rmw(phy, offset,
++		       RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
+ 
+ 	if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */
+-		val |= RG_PE1_H_PLL_PREDIV_VAL(0x01);
+-		phy_write(phy, val, RG_PE1_H_PLL_REG);
++		/* Set Pre-divider ratio (for host mode) */
++		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG,
++			       RG_PE1_H_PLL_PREDIV,
++			       RG_PE1_H_PLL_PREDIV_VAL(0x01));
+ 		dev_info(dev, "Xtal is 40MHz\n");
+-	} else { /* 25MHz | 20MHz Xtal */
+-		val |= RG_PE1_H_PLL_PREDIV_VAL(0x00);
+-		phy_write(phy, val, RG_PE1_H_PLL_REG);
+-		if (reg >= 6) {
+-			dev_info(dev, "Xtal is 25MHz\n");
+-
+-			/* Select feedback clock */
+-			val = phy_read(phy, RG_PE1_H_PLL_FBKSEL_REG);
+-			val &= ~(RG_PE1_H_PLL_FBKSEL);
+-			val |= RG_PE1_H_PLL_FBKSEL_VAL(0x01);
+-			phy_write(phy, val, RG_PE1_H_PLL_FBKSEL_REG);
+-
+-			/* DDS NCPO PCW (for host mode) */
+-			val = phy_read(phy, RG_PE1_H_LCDDS_SSC_PRD_REG);
+-			val &= ~(RG_PE1_H_LCDDS_SSC_PRD);
+-			val |= RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18000000);
+-			phy_write(phy, val, RG_PE1_H_LCDDS_SSC_PRD_REG);
+-
+-			/* DDS SSC dither period control */
+-			val = phy_read(phy, RG_PE1_H_LCDDS_SSC_PRD_REG);
+-			val &= ~(RG_PE1_H_LCDDS_SSC_PRD);
+-			val |= RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18d);
+-			phy_write(phy, val, RG_PE1_H_LCDDS_SSC_PRD_REG);
+-
+-			/* DDS SSC dither amplitude control */
+-			val = phy_read(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG);
+-			val &= ~(RG_PE1_H_LCDDS_SSC_DELTA |
+-				 RG_PE1_H_LCDDS_SSC_DELTA1);
+-			val |= RG_PE1_H_LCDDS_SSC_DELTA_VAL(0x4a);
+-			val |= RG_PE1_H_LCDDS_SSC_DELTA1_VAL(0x4a);
+-			phy_write(phy, val, RG_PE1_H_LCDDS_SSC_DELTA_REG);
+-		} else {
+-			dev_info(dev, "Xtal is 20MHz\n");
+-		}
++	} else if (reg >= 6) { /* 25MHz Xal */
++		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG,
++			       RG_PE1_H_PLL_PREDIV,
++			       RG_PE1_H_PLL_PREDIV_VAL(0x00));
++		/* Select feedback clock */
++		mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG,
++			       RG_PE1_H_PLL_FBKSEL,
++			       RG_PE1_H_PLL_FBKSEL_VAL(0x01));
++		/* DDS NCPO PCW (for host mode) */
++		mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG,
++			       RG_PE1_H_LCDDS_SSC_PRD,
++			       RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18000000));
++		/* DDS SSC dither period control */
++		mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG,
++			       RG_PE1_H_LCDDS_SSC_PRD,
++			       RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18d));
++		/* DDS SSC dither amplitude control */
++		mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG,
++			       RG_PE1_H_LCDDS_SSC_DELTA |
++			       RG_PE1_H_LCDDS_SSC_DELTA1,
++			       RG_PE1_H_LCDDS_SSC_DELTA_VAL(0x4a) |
++			       RG_PE1_H_LCDDS_SSC_DELTA1_VAL(0x4a));
++		dev_info(dev, "Xtal is 25MHz\n");
++	} else { /* 20MHz Xtal */
++		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG,
++			       RG_PE1_H_PLL_PREDIV,
++			       RG_PE1_H_PLL_PREDIV_VAL(0x00));
++
++		dev_info(dev, "Xtal is 20MHz\n");
+ 	}
+ 
+ 	/* DDS clock inversion */
+-	val = phy_read(phy, RG_PE1_LCDDS_CLK_PH_INV_REG);
+-	val &= ~(RG_PE1_LCDDS_CLK_PH_INV);
+-	val |= RG_PE1_LCDDS_CLK_PH_INV;
+-	phy_write(phy, val, RG_PE1_LCDDS_CLK_PH_INV_REG);
++	mt7621_phy_rmw(phy, RG_PE1_LCDDS_CLK_PH_INV_REG,
++		       RG_PE1_LCDDS_CLK_PH_INV, RG_PE1_LCDDS_CLK_PH_INV);
+ 
+ 	/* Set PLL bits */
+-	val = phy_read(phy, RG_PE1_H_PLL_REG);
+-	val &= ~(RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR |
+-		 RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN);
+-	val |= RG_PE1_H_PLL_BC_VAL(0x02);
+-	val |= RG_PE1_H_PLL_BP_VAL(0x06);
+-	val |= RG_PE1_H_PLL_IR_VAL(0x02);
+-	val |= RG_PE1_H_PLL_IC_VAL(0x01);
+-	val |= RG_PE1_PLL_DIVEN_VAL(0x02);
+-	phy_write(phy, val, RG_PE1_H_PLL_REG);
+-
+-	val = phy_read(phy, RG_PE1_H_PLL_BR_REG);
+-	val &= ~(RG_PE1_H_PLL_BR);
+-	val |= RG_PE1_H_PLL_BR_VAL(0x00);
+-	phy_write(phy, val, RG_PE1_H_PLL_BR_REG);
++	mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG,
++		       RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR |
++		       RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN,
++		       RG_PE1_H_PLL_BC_VAL(0x02) | RG_PE1_H_PLL_BP_VAL(0x06) |
++		       RG_PE1_H_PLL_IR_VAL(0x02) | RG_PE1_H_PLL_IC_VAL(0x01) |
++		       RG_PE1_PLL_DIVEN_VAL(0x02));
++
++	mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG,
++		       RG_PE1_H_PLL_BR, RG_PE1_H_PLL_BR_VAL(0x00));
+ 
+ 	if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */
+ 		/* set force mode enable of da_pe1_mstckdiv */
+-		val = phy_read(phy, RG_PE1_MSTCKDIV_REG);
+-		val &= ~(RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV);
+-		val |= (RG_PE1_MSTCKDIV_VAL(0x01) | RG_PE1_FRC_MSTCKDIV);
+-		phy_write(phy, val, RG_PE1_MSTCKDIV_REG);
++		mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG,
++			       RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV,
++			       RG_PE1_MSTCKDIV_VAL(0x01) | RG_PE1_FRC_MSTCKDIV);
+ 	}
+ }
+ 
+@@ -252,13 +240,11 @@ static int mt7621_pci_phy_power_on(struc
+ 	struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent);
+ 	u32 offset = (instance->index != 1) ?
+ 		RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
+-	u32 val;
+ 
+ 	/* Enable PHY and disable force mode */
+-	val = phy_read(mphy, offset);
+-	val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
+-	val |= (RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
+-	phy_write(mphy, val, offset);
++	mt7621_phy_rmw(mphy, offset,
++		       RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN,
++		       RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
+ 
+ 	return 0;
+ }
+@@ -269,13 +255,11 @@ static int mt7621_pci_phy_power_off(stru
+ 	struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent);
+ 	u32 offset = (instance->index != 1) ?
+ 		RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
+-	u32 val;
+ 
+ 	/* Disable PHY */
+-	val = phy_read(mphy, offset);
+-	val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
+-	val |= RG_PE1_FRC_PHY_EN;
+-	phy_write(mphy, val, offset);
++	mt7621_phy_rmw(mphy, offset,
++		       RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN,
++		       RG_PE1_FRC_PHY_EN);
+ 
+ 	return 0;
+ }
diff --git a/target/linux/ramips/patches-5.10/0107-staging-mt7621-pci-fix-io-space-and-properly-set-res.patch b/target/linux/ramips/patches-5.10/0107-staging-mt7621-pci-fix-io-space-and-properly-set-res.patch
new file mode 100644
index 0000000000..393fd4d5dd
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0107-staging-mt7621-pci-fix-io-space-and-properly-set-res.patch
@@ -0,0 +1,131 @@
+From 3faf4e1c537de86058fc22a851cd979489b9185e Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Wed, 18 Mar 2020 10:44:45 +0100
+Subject: [PATCH] staging: mt7621-pci: fix io space and properly set resource
+ limits
+
+Function 'mt7621_pci_parse_request_of_pci_ranges' is using
+'of_pci_range_to_resource' to get both mem and io resources.
+Internally this function calls to 'pci_address_to_pio' which
+returns -1 if io space address is an address > IO_SPACE_LIMIT
+which is 0xFFFF for mips. This mt7621 soc has io space in physical
+address 0x1e160000. In order to fix this, overwrite invalid io
+0xffffffff  with properly values from the device tree and set
+mapped address of this resource as io port base memory address
+calling 'set_io_port_base' function. There is also need to properly
+setup resource limits and io and memory windows with properly
+parsed values instead of set them as 'no limit' which it is wrong.
+For any reason I don't really know legacy driver sets up mem window
+as 0xFFFFFFFF and any other value seems to does not work as expected,
+so set up also here with same values.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200318094445.19669-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 43 +++++++++++++++++++--------------
+ 1 file changed, 25 insertions(+), 18 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -118,6 +118,7 @@ struct mt7621_pcie_port {
+  * @busn: bus range
+  * @offset: IO / Memory offset
+  * @dev: Pointer to PCIe device
++ * @io_map_base: virtual memory base address for io
+  * @ports: pointer to PCIe port information
+  * @resets_inverted: depends on chip revision
+  * reset lines are inverted.
+@@ -132,6 +133,7 @@ struct mt7621_pcie {
+ 		resource_size_t mem;
+ 		resource_size_t io;
+ 	} offset;
++	unsigned long io_map_base;
+ 	struct list_head ports;
+ 	bool resets_inverted;
+ };
+@@ -291,22 +293,21 @@ static int mt7621_pci_parse_request_of_p
+ 	}
+ 
+ 	for_each_of_pci_range(&parser, &range) {
+-		struct resource *res = NULL;
+-
+ 		switch (range.flags & IORESOURCE_TYPE_BITS) {
+ 		case IORESOURCE_IO:
+-			ioremap(range.cpu_addr, range.size);
+-			res = &pcie->io;
++			pcie->io_map_base =
++				(unsigned long)ioremap(range.cpu_addr,
++						       range.size);
++			of_pci_range_to_resource(&range, node, &pcie->io);
++			pcie->io.start = range.cpu_addr;
++			pcie->io.end = range.cpu_addr + range.size - 1;
+ 			pcie->offset.io = 0x00000000UL;
+ 			break;
+ 		case IORESOURCE_MEM:
+-			res = &pcie->mem;
++			of_pci_range_to_resource(&range, node, &pcie->mem);
+ 			pcie->offset.mem = 0x00000000UL;
+ 			break;
+ 		}
+-
+-		if (res)
+-			of_pci_range_to_resource(&range, node, res);
+ 	}
+ 
+ 	err = of_pci_parse_bus_range(node, &pcie->busn);
+@@ -318,6 +319,8 @@ static int mt7621_pci_parse_request_of_p
+ 		pcie->busn.flags = IORESOURCE_BUS;
+ 	}
+ 
++	set_io_port_base(pcie->io_map_base);
++
+ 	return 0;
+ }
+ 
+@@ -548,6 +551,10 @@ static void mt7621_pcie_enable_ports(str
+ 	u32 slot;
+ 	u32 val;
+ 
++	/* Setup MEMWIN and IOWIN */
++	pcie_write(pcie, 0xffffffff, RALINK_PCI_MEMBASE);
++	pcie_write(pcie, pcie->io.start, RALINK_PCI_IOBASE);
++
+ 	list_for_each_entry(port, &pcie->ports, list) {
+ 		if (port->enabled) {
+ 			mt7621_pcie_port_clk_enable(port);
+@@ -668,11 +675,17 @@ static int mt7621_pci_probe(struct platf
+ 		return err;
+ 	}
+ 
++	err = mt7621_pci_parse_request_of_pci_ranges(pcie);
++	if (err) {
++		dev_err(dev, "Error requesting pci resources from ranges");
++		goto out_release_gpios;
++	}
++
+ 	/* set resources limits */
+-	iomem_resource.start = 0;
+-	iomem_resource.end = ~0UL; /* no limit */
+-	ioport_resource.start = 0;
+-	ioport_resource.end = ~0UL; /* no limit */
++	iomem_resource.start = pcie->mem.start;
++	iomem_resource.end = pcie->mem.end;
++	ioport_resource.start = pcie->io.start;
++	ioport_resource.end = pcie->io.end;
+ 
+ 	mt7621_pcie_init_ports(pcie);
+ 
+@@ -685,12 +698,6 @@ static int mt7621_pci_probe(struct platf
+ 
+ 	mt7621_pcie_enable_ports(pcie);
+ 
+-	err = mt7621_pci_parse_request_of_pci_ranges(pcie);
+-	if (err) {
+-		dev_err(dev, "Error requesting pci resources from ranges");
+-		goto out_release_gpios;
+-	}
+-
+ 	setup_cm_memory_region(pcie);
+ 
+ 	err = mt7621_pcie_request_resources(pcie, &res);
diff --git a/target/linux/ramips/patches-5.10/0108-staging-mt7621-pci-fix-register-to-set-up-virtual-br.patch b/target/linux/ramips/patches-5.10/0108-staging-mt7621-pci-fix-register-to-set-up-virtual-br.patch
new file mode 100644
index 0000000000..5b4c461b2f
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0108-staging-mt7621-pci-fix-register-to-set-up-virtual-br.patch
@@ -0,0 +1,28 @@
+From 0a3085ae142d8f5cf905b104bc66db3721a2fa33 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Thu, 19 Mar 2020 10:57:33 +0100
+Subject: [PATCH] staging: mt7621-pci: fix register to set up virtual bridges
+
+Instead of being using PCI Configuration and Status Register to
+set up virtual bridges we are using CONFIG_ADDR Register which is
+wrong. Hence, set the correct value.
+
+Fixes: 9a5e71a68d20 ("staging: mt7621-pci: simplify 'mt7621_pcie_init_virtual_bridges' function")
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200319095733.1557-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -603,7 +603,7 @@ static int mt7621_pcie_init_virtual_brid
+ 		if ((pcie_link_status & BIT(i)) == 0)
+ 			p2p_br_devnum[i] = n++;
+ 
+-	pcie_rmw(pcie, RALINK_PCI_CONFIG_ADDR,
++	pcie_rmw(pcie, RALINK_PCI_PCICFG_ADDR,
+ 		 PCIE_P2P_BR_DEVNUM_MASK_FULL,
+ 		 (p2p_br_devnum[0] << PCIE_P2P_BR_DEVNUM0_SHIFT) |
+ 		 (p2p_br_devnum[1] << PCIE_P2P_BR_DEVNUM1_SHIFT) |
diff --git a/target/linux/ramips/patches-5.10/0109-staging-mt7621-pci-don-t-return-if-get-gpio-fails.patch b/target/linux/ramips/patches-5.10/0109-staging-mt7621-pci-don-t-return-if-get-gpio-fails.patch
new file mode 100644
index 0000000000..55abb8999f
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0109-staging-mt7621-pci-don-t-return-if-get-gpio-fails.patch
@@ -0,0 +1,34 @@
+From 23a788c23ed10e0d79092fcb693dcf0e357e1f7e Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Thu, 19 Mar 2020 17:14:16 +0100
+Subject: [PATCH] staging: mt7621-pci: don't return if get gpio fails
+
+In some platforms gpio's are not used for reset but
+for other purposes. Because of that when we try to
+get them are valid gpio's but are already assigned
+to do other function. To avoid those kind of problems
+in those platforms just notice the fail in the kernel
+but continue doing normal boot.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200319161416.19033-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -363,10 +363,8 @@ static int mt7621_pcie_parse_port(struct
+ 
+ 	port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot,
+ 						       GPIOD_OUT_LOW);
+-	if (IS_ERR(port->gpio_rst)) {
+-		dev_err(dev, "Failed to get GPIO for PCIe%d\n", slot);
+-		return PTR_ERR(port->gpio_rst);
+-	}
++	if (IS_ERR(port->gpio_rst))
++		dev_notice(dev, "Failed to get GPIO for PCIe%d\n", slot);
+ 
+ 	port->slot = slot;
+ 	port->pcie = pcie;
diff --git a/target/linux/ramips/patches-5.10/0110-staging-mt7621-pci-phy-avoid-to-create-to-different-.patch b/target/linux/ramips/patches-5.10/0110-staging-mt7621-pci-phy-avoid-to-create-to-different-.patch
new file mode 100644
index 0000000000..6d84bfdd5b
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0110-staging-mt7621-pci-phy-avoid-to-create-to-different-.patch
@@ -0,0 +1,272 @@
+From 91eb47531421f0e8c9bc4594b4a7caa0e59dc83e Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 20 Mar 2020 12:01:19 +0100
+Subject: [PATCH] staging: mt7621-pci-phy: avoid to create to different phys
+ for a dual port one
+
+This soc has two phy's for the pcie one of them using just a different
+register for settig it up but sharing all the rest of the config. Until
+now we was presenting this schema as three different phy's in the device
+tree using the 'phy-cells' node property to discriminate an index and
+setting up a complete phy for the dual port index. This sometimes worked
+properly but reconfiguring the same registers twice presents sometimes
+some unstable pcie links and the ports was not properly being detected.
+The problems only appears on hard resets and soft resets was properly
+working. Instead of having this schema just set two phy's in the device
+ree and use the 'phy-cells' property to say if the port has or not a dual
+port. Doing this configuration and set up becomes easier, LOC is decreased
+and the behaviour also gets deterministic with properly and stable pcie
+links in both hard and soft resets.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200320110123.9907-2-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 144 ++++++++++--------------
+ 1 file changed, 59 insertions(+), 85 deletions(-)
+
+--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
++++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
+@@ -78,31 +78,21 @@
+ #define MAX_PHYS	2
+ 
+ /**
+- * struct mt7621_pci_phy_instance - Mt7621 Pcie PHY device
+- * @phy: pointer to the kernel PHY device
+- * @port_base: base register
+- * @index: internal ID to identify the Mt7621 PCIe PHY
+- */
+-struct mt7621_pci_phy_instance {
+-	struct phy *phy;
+-	void __iomem *port_base;
+-	u32 index;
+-};
+-
+-/**
+  * struct mt7621_pci_phy - Mt7621 Pcie PHY core
+  * @dev: pointer to device
+  * @regmap: kernel regmap pointer
+- * @phys: pointer to Mt7621 PHY device
+- * @nphys: number of PHY devices for this core
++ * @phy: pointer to the kernel PHY device
++ * @port_base: base register
++ * @has_dual_port: if the phy has dual ports.
+  * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst'
+  * needs to be executed. Depends on chip revision.
+  */
+ struct mt7621_pci_phy {
+ 	struct device *dev;
+ 	struct regmap *regmap;
+-	struct mt7621_pci_phy_instance **phys;
+-	int nphys;
++	struct phy *phy;
++	void __iomem *port_base;
++	bool has_dual_port;
+ 	bool bypass_pipe_rst;
+ };
+ 
+@@ -130,23 +120,23 @@ static inline void mt7621_phy_rmw(struct
+ 	phy_write(phy, val, reg);
+ }
+ 
+-static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy,
+-				   struct mt7621_pci_phy_instance *instance)
++static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy)
+ {
+-	u32 offset = (instance->index != 1) ?
+-		RG_PE1_PIPE_REG : RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH;
++	mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST);
++	mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC);
+ 
+-	mt7621_phy_rmw(phy, offset,
+-		       RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC,
+-		       RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC);
++	if (phy->has_dual_port) {
++		mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH,
++			       0, RG_PE1_PIPE_RST);
++		mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH,
++			       0, RG_PE1_PIPE_CMD_FRC);
++	}
+ }
+ 
+-static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy,
+-				   struct mt7621_pci_phy_instance *instance)
++static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy)
+ {
+ 	struct device *dev = phy->dev;
+ 	u32 reg = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0);
+-	u32 offset;
+ 
+ 	reg = (reg >> 6) & 0x7;
+ 	/* Set PCIe Port PHY to disable SSC */
+@@ -156,10 +146,13 @@ static void mt7621_set_phy_for_ssc(struc
+ 		       RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE_VAL(0x00));
+ 
+ 	/* disable port */
+-	offset = (instance->index != 1) ?
+-		RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
+-	mt7621_phy_rmw(phy, offset,
+-		       RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
++	mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG,
++		       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
++
++	if (phy->has_dual_port) {
++		mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,
++			       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
++	}
+ 
+ 	if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */
+ 		/* Set Pre-divider ratio (for host mode) */
+@@ -223,43 +216,44 @@ static void mt7621_set_phy_for_ssc(struc
+ 
+ static int mt7621_pci_phy_init(struct phy *phy)
+ {
+-	struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy);
+-	struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent);
++	struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);
+ 
+ 	if (mphy->bypass_pipe_rst)
+-		mt7621_bypass_pipe_rst(mphy, instance);
++		mt7621_bypass_pipe_rst(mphy);
+ 
+-	mt7621_set_phy_for_ssc(mphy, instance);
++	mt7621_set_phy_for_ssc(mphy);
+ 
+ 	return 0;
+ }
+ 
+ static int mt7621_pci_phy_power_on(struct phy *phy)
+ {
+-	struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy);
+-	struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent);
+-	u32 offset = (instance->index != 1) ?
+-		RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
++	struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);
+ 
+ 	/* Enable PHY and disable force mode */
+-	mt7621_phy_rmw(mphy, offset,
+-		       RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN,
+-		       RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
++	mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG,
++		       RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN);
++
++	if (mphy->has_dual_port) {
++		mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,
++			       RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN);
++	}
+ 
+ 	return 0;
+ }
+ 
+ static int mt7621_pci_phy_power_off(struct phy *phy)
+ {
+-	struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy);
+-	struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent);
+-	u32 offset = (instance->index != 1) ?
+-		RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
++	struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);
+ 
+ 	/* Disable PHY */
+-	mt7621_phy_rmw(mphy, offset,
+-		       RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN,
+-		       RG_PE1_FRC_PHY_EN);
++	mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG,
++		       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
++
++	if (mphy->has_dual_port) {
++		mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,
++			       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
++	}
+ 
+ 	return 0;
+ }
+@@ -282,13 +276,15 @@ static struct phy *mt7621_pcie_phy_of_xl
+ {
+ 	struct mt7621_pci_phy *mt7621_phy = dev_get_drvdata(dev);
+ 
+-	if (args->args_count == 0)
+-		return mt7621_phy->phys[0]->phy;
+-
+ 	if (WARN_ON(args->args[0] >= MAX_PHYS))
+ 		return ERR_PTR(-ENODEV);
+ 
+-	return mt7621_phy->phys[args->args[0]]->phy;
++	mt7621_phy->has_dual_port = args->args[0];
++
++	dev_info(dev, "PHY for 0x%08x (dual port = %d)\n",
++		 (unsigned int)mt7621_phy->port_base, mt7621_phy->has_dual_port);
++
++	return mt7621_phy->phy;
+ }
+ 
+ static const struct soc_device_attribute mt7621_pci_quirks_match[] = {
+@@ -309,19 +305,11 @@ static int mt7621_pci_phy_probe(struct p
+ 	struct phy_provider *provider;
+ 	struct mt7621_pci_phy *phy;
+ 	struct resource *res;
+-	int port;
+-	void __iomem *port_base;
+ 
+ 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ 	if (!phy)
+ 		return -ENOMEM;
+ 
+-	phy->nphys = MAX_PHYS;
+-	phy->phys = devm_kcalloc(dev, phy->nphys,
+-				 sizeof(*phy->phys), GFP_KERNEL);
+-	if (!phy->phys)
+-		return -ENOMEM;
+-
+ 	attr = soc_device_match(mt7621_pci_quirks_match);
+ 	if (attr)
+ 		phy->bypass_pipe_rst = true;
+@@ -335,39 +323,25 @@ static int mt7621_pci_phy_probe(struct p
+ 		return -ENXIO;
+ 	}
+ 
+-	port_base = devm_ioremap_resource(dev, res);
+-	if (IS_ERR(port_base)) {
++	phy->port_base = devm_ioremap_resource(dev, res);
++	if (IS_ERR(phy->port_base)) {
+ 		dev_err(dev, "failed to remap phy regs\n");
+-		return PTR_ERR(port_base);
++		return PTR_ERR(phy->port_base);
+ 	}
+ 
+-	phy->regmap = devm_regmap_init_mmio(phy->dev, port_base,
++	phy->regmap = devm_regmap_init_mmio(phy->dev, phy->port_base,
+ 					    &mt7621_pci_phy_regmap_config);
+ 	if (IS_ERR(phy->regmap))
+ 		return PTR_ERR(phy->regmap);
+ 
+-	for (port = 0; port < MAX_PHYS; port++) {
+-		struct mt7621_pci_phy_instance *instance;
+-		struct phy *pphy;
+-
+-		instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
+-		if (!instance)
+-			return -ENOMEM;
+-
+-		phy->phys[port] = instance;
+-
+-		pphy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops);
+-		if (IS_ERR(phy)) {
+-			dev_err(dev, "failed to create phy\n");
+-			return PTR_ERR(phy);
+-		}
+-
+-		instance->port_base = port_base;
+-		instance->phy = pphy;
+-		instance->index = port;
+-		phy_set_drvdata(pphy, instance);
++	phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops);
++	if (IS_ERR(phy)) {
++		dev_err(dev, "failed to create phy\n");
++		return PTR_ERR(phy);
+ 	}
+ 
++	phy_set_drvdata(phy->phy, phy);
++
+ 	provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate);
+ 
+ 	return PTR_ERR_OR_ZERO(provider);
diff --git a/target/linux/ramips/patches-5.10/0111-staging-mt7621-pci-use-only-two-phys-from-device-tre.patch b/target/linux/ramips/patches-5.10/0111-staging-mt7621-pci-use-only-two-phys-from-device-tre.patch
new file mode 100644
index 0000000000..61aa80eb76
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0111-staging-mt7621-pci-use-only-two-phys-from-device-tre.patch
@@ -0,0 +1,42 @@
+From c752b54bda4d772426c5eeb56978d2e41bd525b4 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 20 Mar 2020 12:01:21 +0100
+Subject: [PATCH] staging: mt7621-pci: use only two phys from device tree
+
+In order to align work with the mt7621-pci-phy part of
+the driver and device tree which is now using only two
+real phys one of them dual ported properly parse the
+device tree and don't call phy initialization for the
+slot 1 because is being taking into account when the
+phy for the slot 0 is instantiated.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200320110123.9907-4-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -358,7 +358,7 @@ static int mt7621_pcie_parse_port(struct
+ 
+ 	snprintf(name, sizeof(name), "pcie-phy%d", slot);
+ 	port->phy = devm_phy_get(dev, name);
+-	if (IS_ERR(port->phy))
++	if (IS_ERR(port->phy) && slot != 1)
+ 		return PTR_ERR(port->phy);
+ 
+ 	port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot,
+@@ -495,6 +495,11 @@ static void mt7621_pcie_init_ports(struc
+ 	list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ 		u32 slot = port->slot;
+ 
++		if (slot == 1) {
++			port->enabled = true;
++			continue;
++		}
++
+ 		err = mt7621_pcie_init_port(port);
+ 		if (err) {
+ 			dev_err(dev, "Initiating port %d failed\n", slot);
diff --git a/target/linux/ramips/patches-5.10/0112-staging-mt7621-pci-change-variable-to-print-for-slot.patch b/target/linux/ramips/patches-5.10/0112-staging-mt7621-pci-change-variable-to-print-for-slot.patch
new file mode 100644
index 0000000000..383c896a3e
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0112-staging-mt7621-pci-change-variable-to-print-for-slot.patch
@@ -0,0 +1,26 @@
+From b59343b7de448c30e5b098484a7c7c5cb300df2f Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 20 Mar 2020 12:01:22 +0100
+Subject: [PATCH] staging: mt7621-pci: change variable to print for slot
+
+We are using the counter to print the slot which has been
+enabled. Use the correct associated slot for the port instead.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200320110123.9907-5-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -562,7 +562,7 @@ static void mt7621_pcie_enable_ports(str
+ 		if (port->enabled) {
+ 			mt7621_pcie_port_clk_enable(port);
+ 			mt7621_pcie_enable_port(port);
+-			dev_info(dev, "PCIE%d enabled\n", num_slots_enabled);
++			dev_info(dev, "PCIE%d enabled\n", port->slot);
+ 			num_slots_enabled++;
+ 		}
+ 	}
diff --git a/target/linux/ramips/patches-5.10/0113-staging-mt7621-pci-be-sure-gpio-descriptor-is-null-o.patch b/target/linux/ramips/patches-5.10/0113-staging-mt7621-pci-be-sure-gpio-descriptor-is-null-o.patch
new file mode 100644
index 0000000000..bf6fdf1d49
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0113-staging-mt7621-pci-be-sure-gpio-descriptor-is-null-o.patch
@@ -0,0 +1,33 @@
+From 87068309300c707d659ce79232eae827604804a4 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 20 Mar 2020 12:01:23 +0100
+Subject: [PATCH] staging: mt7621-pci: be sure gpio descriptor is null on fails
+
+Function 'devm_gpiod_get_index_optional' returns NULL if the
+descriptor is invalid and the error associated for the error
+pointer is ENOENT. Sometimes if the pin is just assigned the
+error associated for the pointer might not be ENOENT but other.
+In order to avoid weirds behaviours if this happen set descriptor
+to NULL in the driver port structure.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200320110123.9907-6-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -363,8 +363,10 @@ static int mt7621_pcie_parse_port(struct
+ 
+ 	port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot,
+ 						       GPIOD_OUT_LOW);
+-	if (IS_ERR(port->gpio_rst))
++	if (IS_ERR(port->gpio_rst)) {
+ 		dev_notice(dev, "Failed to get GPIO for PCIe%d\n", slot);
++		port->gpio_rst = NULL;
++	}
+ 
+ 	port->slot = slot;
+ 	port->pcie = pcie;
diff --git a/target/linux/ramips/patches-5.10/0114-staging-mt7621-pci-avoid-to-poweroff-the-phy-for-slo.patch b/target/linux/ramips/patches-5.10/0114-staging-mt7621-pci-avoid-to-poweroff-the-phy-for-slo.patch
new file mode 100644
index 0000000000..d733e58537
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0114-staging-mt7621-pci-avoid-to-poweroff-the-phy-for-slo.patch
@@ -0,0 +1,79 @@
+From d81fe3c13aa6f4ab1ec318212d2007175e6d05aa Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Fri, 20 Mar 2020 16:38:37 +0100
+Subject: [PATCH] staging: mt7621-pci: avoid to poweroff the phy for slot one
+
+Phy for slot 0 and 1 is shared and handled properly in slot 0.
+If there is only one port in use,(slot 0) we shall not call the
+'phy_power_off' function with an invalid slot because kernel
+will crash with an unaligned access fault like the following:
+
+mt7621-pci 1e140000.pcie: Error applying setting, reverse things back
+mt7621-pci-phy 1e149000.pcie-phy: PHY for 0xbe149000 (dual port = 1)
+mt7621-pci-phy 1e14a000.pcie-phy: PHY for 0xbe14a000 (dual port = 0)
+mt7621-pci-phy 1e149000.pcie-phy: Xtal is 40MHz
+mt7621-pci-phy 1e14a000.pcie-phy: Xtal is 40MHz
+mt7621-pci 1e140000.pcie: pcie1 no card, disable it (RST & CLK)
+Unhandled kernel unaligned access[#1]:
+CPU: 3 PID: 111 Comm: kworker/3:2 Not tainted 5.6.0-rc3-00347-g825c6f470c62-dirty #9
+Workqueue: events deferred_probe_work_func
+$ 0   : 00000000 00000001 5f60d043 8fe1ba80
+$ 4   : 0000010d 01eb9000 00000000 00000000
+$ 8   : 294b4c00 80940000 00000008 000000ce
+$12   : 2e303030 00000000 00000000 65696370
+$16   : ffffffed 0000010d 8e373cd0 8214c1e0
+$20   : 00000000 82144c80 82144680 8214c250
+$24   : 00000018 803ef8f4
+$28   : 8e372000 8e373c60 8214c080 803940e8
+Hi    : 00000125
+Lo    : 122f2000
+epc   : 807b3328 mutex_lock+0x8/0x44
+ra    : 803940e8 phy_power_off+0x28/0xb0
+Status: 1100fc03        KERNEL EXL IE
+Cause : 00800010 (ExcCode 04)
+BadVA : 0000010d
+PrId  : 0001992f (MIPS 1004Kc)
+Modules linked in:
+Process kworker/3:2 (pid: 111, threadinfo=(ptrval), task=(ptrval), tls=00000000)
+Stack : 8e373cd0 803fe4f4 8e372000 8e373c90 8214c080 804fde1c 8e373c98 808d62f4
+         8e373c78 00000000 8214c254 804fe648 1e160000 804f27b8 00000001 808d62f4
+         00000000 00000001 8214c228 808d62f4 80930000 809a0000 8fd47e10 808d63d4
+         808d62d4 8fd47e10 808d0000 808d0000 8e373cd0 8e373cd0 809e2a74 809db510
+         809db510 00000006 00000001 00000000 00000000 00000000 01000000 1e1440ff
+         ...
+Call Trace:
+[<807b3328>] mutex_lock+0x8/0x44
+[<803940e8>] phy_power_off+0x28/0xb0
+[<804fe648>] mt7621_pci_probe+0xc20/0xd18
+[<80402ab8>] platform_drv_probe+0x40/0x94
+[<80400a74>] really_probe+0x104/0x364
+[<803feb74>] bus_for_each_drv+0x84/0xdc
+[<80400924>] __device_attach+0xdc/0x120
+[<803ffb5c>] bus_probe_device+0xa0/0xbc
+[<80400124>] deferred_probe_work_func+0x7c/0xbc
+[<800420e8>] process_one_work+0x230/0x450
+[<80042638>] worker_thread+0x330/0x5fc
+[<80048eb0>] kthread+0x12c/0x134
+[<80007438>] ret_from_kernel_thread+0x14/0x1c
+Code: 24050002  27bdfff8  8f830000 <c0850000> 14a00005  00000000  00600825  e0810000  1020fffa
+
+Fixes: bf516f413f4e ("staging: mt7621-pci: use only two phys from device tree")
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200320153837.20415-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -517,7 +517,8 @@ static void mt7621_pcie_init_ports(struc
+ 		if (!mt7621_pcie_port_is_linkup(port)) {
+ 			dev_err(dev, "pcie%d no card, disable it (RST & CLK)\n",
+ 				slot);
+-			phy_power_off(port->phy);
++			if (slot != 1)
++				phy_power_off(port->phy);
+ 			mt7621_control_assert(port);
+ 			mt7621_pcie_port_clk_disable(port);
+ 			port->enabled = false;
diff --git a/target/linux/ramips/patches-5.10/0115-staging-mt7621-pci-delete-release-gpios-related-code.patch b/target/linux/ramips/patches-5.10/0115-staging-mt7621-pci-delete-release-gpios-related-code.patch
new file mode 100644
index 0000000000..5bfd205691
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0115-staging-mt7621-pci-delete-release-gpios-related-code.patch
@@ -0,0 +1,91 @@
+From 9d789a7728c37e8730b6a9cca60cf155f18537ea Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Sat, 21 Mar 2020 08:26:50 +0100
+Subject: [PATCH] staging: mt7621-pci: delete release gpios related code
+
+Making gpio8 and gpio9 vendor specific and putting them
+into the specific dts file makes not needed to release
+gpios anymore because we are not occupying those pins
+in the first place if it is not necessary. When the
+device tree is parsed we can also check and return for
+the error because we rely in the fact that the related
+device for the board is correct.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200321072650.7784-3-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 27 +++++++--------------------
+ 1 file changed, 7 insertions(+), 20 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -364,8 +364,8 @@ static int mt7621_pcie_parse_port(struct
+ 	port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot,
+ 						       GPIOD_OUT_LOW);
+ 	if (IS_ERR(port->gpio_rst)) {
+-		dev_notice(dev, "Failed to get GPIO for PCIe%d\n", slot);
+-		port->gpio_rst = NULL;
++		dev_err(dev, "Failed to get GPIO for PCIe%d\n", slot);
++		return PTR_ERR(port->gpio_rst);
+ 	}
+ 
+ 	port->slot = slot;
+@@ -474,15 +474,6 @@ static void mt7621_pcie_reset_ep_deasser
+ 	mdelay(PERST_DELAY_MS);
+ }
+ 
+-static void mt7621_pcie_release_gpios(struct mt7621_pcie *pcie)
+-{
+-	struct mt7621_pcie_port *port;
+-
+-	list_for_each_entry(port, &pcie->ports, list)
+-		if (port->gpio_rst)
+-			gpiod_put(port->gpio_rst);
+-}
+-
+ static void mt7621_pcie_init_ports(struct mt7621_pcie *pcie)
+ {
+ 	struct device *dev = pcie->dev;
+@@ -684,7 +675,7 @@ static int mt7621_pci_probe(struct platf
+ 	err = mt7621_pci_parse_request_of_pci_ranges(pcie);
+ 	if (err) {
+ 		dev_err(dev, "Error requesting pci resources from ranges");
+-		goto out_release_gpios;
++		return err;
+ 	}
+ 
+ 	/* set resources limits */
+@@ -698,8 +689,7 @@ static int mt7621_pci_probe(struct platf
+ 	err = mt7621_pcie_init_virtual_bridges(pcie);
+ 	if (err) {
+ 		dev_err(dev, "Nothing is connected in virtual bridges. Exiting...");
+-		err = 0;
+-		goto out_release_gpios;
++		return 0;
+ 	}
+ 
+ 	mt7621_pcie_enable_ports(pcie);
+@@ -709,19 +699,16 @@ static int mt7621_pci_probe(struct platf
+ 	err = mt7621_pcie_request_resources(pcie, &res);
+ 	if (err) {
+ 		dev_err(dev, "Error requesting resources\n");
+-		goto out_release_gpios;
++		return err;
+ 	}
+ 
+ 	err = mt7621_pcie_register_host(bridge, &res);
+ 	if (err) {
+ 		dev_err(dev, "Error registering host\n");
+-		goto out_release_gpios;
++		return err;
+ 	}
+ 
+-out_release_gpios:
+-	mt7621_pcie_release_gpios(pcie);
+-
+-	return err;
++	return 0;
+ }
+ 
+ static const struct of_device_id mt7621_pci_ids[] = {
diff --git a/target/linux/ramips/patches-5.10/0116-staging-mt7621-pci-use-builtin_platform_driver.patch b/target/linux/ramips/patches-5.10/0116-staging-mt7621-pci-use-builtin_platform_driver.patch
new file mode 100644
index 0000000000..b333bb326b
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0116-staging-mt7621-pci-use-builtin_platform_driver.patch
@@ -0,0 +1,29 @@
+From 60a15339ceab9fc2a6cdc85fd54b66b2c947ab4e Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Sat, 21 Mar 2020 14:36:21 +0100
+Subject: [PATCH] staging: mt7621-pci: use builtin_platform_driver()
+
+Macro builtin_platform_driver can be used for builtin drivers
+that don't do anything in driver init. So, use the macro
+builtin_platform_driver and remove some boilerplate code.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200321133624.31388-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -725,9 +725,4 @@ static struct platform_driver mt7621_pci
+ 	},
+ };
+ 
+-static int __init mt7621_pci_init(void)
+-{
+-	return platform_driver_register(&mt7621_pci_driver);
+-}
+-
+-module_init(mt7621_pci_init);
++builtin_platform_driver(mt7621_pci_driver);
diff --git a/target/linux/ramips/patches-5.10/0117-staging-mt7621-pci-phy-use-builtin_platform_driver.patch b/target/linux/ramips/patches-5.10/0117-staging-mt7621-pci-phy-use-builtin_platform_driver.patch
new file mode 100644
index 0000000000..e55def40f5
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0117-staging-mt7621-pci-phy-use-builtin_platform_driver.patch
@@ -0,0 +1,32 @@
+From ffe3dee4081055b4f58bc50dd3f5c97de42cf126 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Sat, 21 Mar 2020 14:36:23 +0100
+Subject: [PATCH] staging: mt7621-pci-phy: use builtin_platform_driver()
+
+Macro builtin_platform_driver can be used for builtin drivers
+that don't do anything in driver init. So, use the macro
+builtin_platform_driver and remove some boilerplate code.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200321133624.31388-3-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
++++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
+@@ -361,12 +361,7 @@ static struct platform_driver mt7621_pci
+ 	},
+ };
+ 
+-static int __init mt7621_pci_phy_drv_init(void)
+-{
+-	return platform_driver_register(&mt7621_pci_phy_driver);
+-}
+-
+-module_init(mt7621_pci_phy_drv_init);
++builtin_platform_driver(mt7621_pci_phy_driver);
+ 
+ MODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos at gmail.com>");
+ MODULE_DESCRIPTION("MediaTek MT7621 PCIe PHY driver");
diff --git a/target/linux/ramips/patches-5.10/0118-staging-mt7621-pci-phy-re-do-xtal_mode-detection.patch b/target/linux/ramips/patches-5.10/0118-staging-mt7621-pci-phy-re-do-xtal_mode-detection.patch
new file mode 100644
index 0000000000..9eb8345144
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0118-staging-mt7621-pci-phy-re-do-xtal_mode-detection.patch
@@ -0,0 +1,68 @@
+From ff83e3023cb8fc3b5dfc12e0c91bf1eb9dc4c4c6 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Sat, 21 Mar 2020 14:36:24 +0100
+Subject: [PATCH] staging: mt7621-pci-phy: re-do 'xtal_mode' detection
+
+Detection of the Xtal mode is using magic numbers that
+can be avoided using properly some definitions and a more
+accurate variable name from 'reg' into 'xtal_mode'. This
+increase readability.
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200321133624.31388-4-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
++++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
+@@ -75,6 +75,9 @@
+ 
+ #define RG_PE1_FRC_MSTCKDIV			BIT(5)
+ 
++#define XTAL_MODE_SEL_SHIFT			6
++#define XTAL_MODE_SEL_MASK			0x7
++
+ #define MAX_PHYS	2
+ 
+ /**
+@@ -136,9 +139,11 @@ static void mt7621_bypass_pipe_rst(struc
+ static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy)
+ {
+ 	struct device *dev = phy->dev;
+-	u32 reg = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0);
++	u32 xtal_mode;
++
++	xtal_mode = (rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0)
++		     >> XTAL_MODE_SEL_SHIFT) & XTAL_MODE_SEL_MASK;
+ 
+-	reg = (reg >> 6) & 0x7;
+ 	/* Set PCIe Port PHY to disable SSC */
+ 	/* Debug Xtal Type */
+ 	mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG,
+@@ -154,13 +159,13 @@ static void mt7621_set_phy_for_ssc(struc
+ 			       RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
+ 	}
+ 
+-	if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */
++	if (xtal_mode <= 5 && xtal_mode >= 3) { /* 40MHz Xtal */
+ 		/* Set Pre-divider ratio (for host mode) */
+ 		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG,
+ 			       RG_PE1_H_PLL_PREDIV,
+ 			       RG_PE1_H_PLL_PREDIV_VAL(0x01));
+ 		dev_info(dev, "Xtal is 40MHz\n");
+-	} else if (reg >= 6) { /* 25MHz Xal */
++	} else if (xtal_mode >= 6) { /* 25MHz Xal */
+ 		mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG,
+ 			       RG_PE1_H_PLL_PREDIV,
+ 			       RG_PE1_H_PLL_PREDIV_VAL(0x00));
+@@ -206,7 +211,7 @@ static void mt7621_set_phy_for_ssc(struc
+ 	mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG,
+ 		       RG_PE1_H_PLL_BR, RG_PE1_H_PLL_BR_VAL(0x00));
+ 
+-	if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */
++	if (xtal_mode <= 5 && xtal_mode >= 3) { /* 40MHz Xtal */
+ 		/* set force mode enable of da_pe1_mstckdiv */
+ 		mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG,
+ 			       RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV,
diff --git a/target/linux/ramips/patches-5.10/0119-staging-mt7621-pci-avoid-to-set-iomem_resource-addre.patch b/target/linux/ramips/patches-5.10/0119-staging-mt7621-pci-avoid-to-set-iomem_resource-addre.patch
new file mode 100644
index 0000000000..9af46f4196
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0119-staging-mt7621-pci-avoid-to-set-iomem_resource-addre.patch
@@ -0,0 +1,35 @@
+From 4f0f36b67564311a4ce4441510ef94848febbab2 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Sun, 22 Mar 2020 08:21:28 +0100
+Subject: [PATCH] staging: mt7621-pci: avoid to set 'iomem_resource' addresses
+
+Setting up kernel resource 'iomem_resource' for PCI with
+addresses parsed from device tree gots into a conflict within
+the usb xhci driver:
+
+xhci-mtk 1e1c0000.xhci: can't request region for resource [mem 0x1e1c0000-0x1e1c0fff]
+xhci-mtk: probe of 1e1c0000.xhci failed with error -16
+
+Don't assign it and maintain the default addresses for this
+resource seems to fix the problem. Checking legacy driver it
+is being only  setting the 'ioport_resource'.
+
+Fixes: 09dd629eeabb ("staging: mt7621-pci: fix io space and properly set resource limits")
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200322072128.4454-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -679,8 +679,6 @@ static int mt7621_pci_probe(struct platf
+ 	}
+ 
+ 	/* set resources limits */
+-	iomem_resource.start = pcie->mem.start;
+-	iomem_resource.end = pcie->mem.end;
+ 	ioport_resource.start = pcie->io.start;
+ 	ioport_resource.end = pcie->io.end;
+ 
diff --git a/target/linux/ramips/patches-5.10/0120-staging-mt7621-pci-properly-power-off-dual-ported-pc.patch b/target/linux/ramips/patches-5.10/0120-staging-mt7621-pci-properly-power-off-dual-ported-pc.patch
new file mode 100644
index 0000000000..9efcb8011a
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0120-staging-mt7621-pci-properly-power-off-dual-ported-pc.patch
@@ -0,0 +1,65 @@
+From 5fcded5e857cf66c9592e4be28c4dab4520c9177 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Thu, 9 Apr 2020 13:16:52 +0200
+Subject: [PATCH] staging: mt7621-pci: properly power off dual-ported pcie phy
+
+Pcie phy for pcie0 and pcie1 is shared using a dual ported
+one. Current code was assuming that if nothing is connected
+in pcie0 it won't be also nothing connected in pcie1. This
+assumtion is wrong for some devices such us 'Mikrotik rbm33g'
+and 'ZyXEL LTE3301-PLUS' where only connecting a card to the
+second bus on the phy is possible. For such devices kernel
+hangs in the same point because of the wrong poweroff of the
+phy getting the following trace:
+
+mt7621-pci-phy 1e149000.pcie-phy: PHY for 0xbe149000 (dual port = 1)
+mt7621-pci-phy 1e14a000.pcie-phy: PHY for 0xbe14a000 (dual port = 0)
+mt7621-pci-phy 1e149000.pcie-phy: Xtal is 40MHz
+mt7621-pci-phy 1e14a000.pcie-phy: Xtal is 40MHz
+mt7621-pci 1e140000.pcie: pcie0 no card, disable it (RST & CLK)
+[hangs]
+
+The wrong assumption is located in the 'mt7621_pcie_init_ports'
+function where we are just making a power off of the phy for
+slots 0 and 2 if nothing is connected in them. Hence, only
+poweroff the phy if nothing is connected in both slot 0 and
+slot 1 avoiding the kernel to hang.
+
+Fixes: 5737cfe87a9c ("staging: mt7621-pci: avoid to poweroff the phy for slot one")
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200409111652.30964-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-pci/pci-mt7621.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -502,17 +502,25 @@ static void mt7621_pcie_init_ports(struc
+ 
+ 	mt7621_pcie_reset_ep_deassert(pcie);
+ 
++	tmp = NULL;
+ 	list_for_each_entry(port, &pcie->ports, list) {
+ 		u32 slot = port->slot;
+ 
+ 		if (!mt7621_pcie_port_is_linkup(port)) {
+ 			dev_err(dev, "pcie%d no card, disable it (RST & CLK)\n",
+ 				slot);
+-			if (slot != 1)
+-				phy_power_off(port->phy);
+ 			mt7621_control_assert(port);
+ 			mt7621_pcie_port_clk_disable(port);
+ 			port->enabled = false;
++
++			if (slot == 0) {
++				tmp = port;
++				continue;
++			}
++
++			if (slot == 1 && tmp && !tmp->enabled)
++				phy_power_off(tmp->phy);
++
+ 		}
+ 	}
+ }
diff --git a/target/linux/ramips/patches-5.10/0121-staging-mt7621-pci-fix-PCIe-interrupt-mapping.patch b/target/linux/ramips/patches-5.10/0121-staging-mt7621-pci-fix-PCIe-interrupt-mapping.patch
new file mode 100644
index 0000000000..68de6df2db
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0121-staging-mt7621-pci-fix-PCIe-interrupt-mapping.patch
@@ -0,0 +1,157 @@
+From fab6710e4c51f4eb622f95a08322ab5fdbe3f295 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Mon, 13 Apr 2020 07:59:42 +0200
+Subject: [PATCH] staging: mt7621-pci: fix PCIe interrupt mapping
+
+MT7621 has three assigned interrupts for the pcie. This
+interrupts should properly being mapped taking into account
+which devices are finally connected in which bus according
+to link status. So the irq mappings should be as follows
+according to link status (three bits indicating which devices
+are link up):
+
+* For PCIe Bus 1 slot 0:
+  - status = 0x2 || status = 0x6 => IRQ = pcie1_irq (24).
+  - status = 0x4 => IRQ = pcie2_irq (25).
+  - default => IRQ = pcie0_irq (23).
+* For PCIe Bus 2 slot 0:
+  - status = 0x5 || status = 0x6 => IRQ = pcie2_irq (25).
+  - default => IRQ = pcie1_irq (24).
+* For PCIe Bus 2 slot 1:
+  - status = 0x5 || status = 0x6 => IRQ = pcie2_irq (25).
+  - default => IRQ = pcie1_irq (24).
+* For PCIe Bus 3 any slot:
+  - default => IRQ = pcie2_irq (25).
+
+Because of this, the function 'of_irq_parse_and_map_pci' cannot
+be used and we need to change device tree information from using
+the 'interrupt-map' and 'interrupt-map-mask' properties into an
+'interrupts' property to be able to get irq information from the
+ports using the 'platform_get_irq' and storing an 'irq-map' into
+the pcie driver data node to properly map correct irq using a
+new 'mt7621_map_irq' function where this map will be read and the
+correct irq returned.
+
+Fixes: 46d093124df4 ("staging: mt7621-pci: improve interrupt mapping")
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Link: https://lore.kernel.org/r/20200413055942.2714-1-sergio.paracuellos@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/staging/mt7621-dts/mt7621.dtsi  |  9 +++----
+ drivers/staging/mt7621-pci/pci-mt7621.c | 36 +++++++++++++++++++++++--
+ 2 files changed, 38 insertions(+), 7 deletions(-)
+
+--- a/drivers/staging/mt7621-pci/pci-mt7621.c
++++ b/drivers/staging/mt7621-pci/pci-mt7621.c
+@@ -97,6 +97,7 @@
+  * @pcie_rst: pointer to port reset control
+  * @gpio_rst: gpio reset
+  * @slot: port slot
++ * @irq: GIC irq
+  * @enabled: indicates if port is enabled
+  */
+ struct mt7621_pcie_port {
+@@ -107,6 +108,7 @@ struct mt7621_pcie_port {
+ 	struct reset_control *pcie_rst;
+ 	struct gpio_desc *gpio_rst;
+ 	u32 slot;
++	int irq;
+ 	bool enabled;
+ };
+ 
+@@ -120,6 +122,7 @@ struct mt7621_pcie_port {
+  * @dev: Pointer to PCIe device
+  * @io_map_base: virtual memory base address for io
+  * @ports: pointer to PCIe port information
++ * @irq_map: irq mapping info according pcie link status
+  * @resets_inverted: depends on chip revision
+  * reset lines are inverted.
+  */
+@@ -135,6 +138,7 @@ struct mt7621_pcie {
+ 	} offset;
+ 	unsigned long io_map_base;
+ 	struct list_head ports;
++	int irq_map[PCIE_P2P_MAX];
+ 	bool resets_inverted;
+ };
+ 
+@@ -279,6 +283,16 @@ static void setup_cm_memory_region(struc
+ 	}
+ }
+ 
++static int mt7621_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
++{
++	struct mt7621_pcie *pcie = pdev->bus->sysdata;
++	struct device *dev = pcie->dev;
++	int irq = pcie->irq_map[slot];
++
++	dev_info(dev, "bus=%d slot=%d irq=%d\n", pdev->bus->number, slot, irq);
++	return irq;
++}
++
+ static int mt7621_pci_parse_request_of_pci_ranges(struct mt7621_pcie *pcie)
+ {
+ 	struct device *dev = pcie->dev;
+@@ -330,6 +344,7 @@ static int mt7621_pcie_parse_port(struct
+ {
+ 	struct mt7621_pcie_port *port;
+ 	struct device *dev = pcie->dev;
++	struct platform_device *pdev = to_platform_device(dev);
+ 	struct device_node *pnode = dev->of_node;
+ 	struct resource regs;
+ 	char name[10];
+@@ -371,6 +386,12 @@ static int mt7621_pcie_parse_port(struct
+ 	port->slot = slot;
+ 	port->pcie = pcie;
+ 
++	port->irq = platform_get_irq(pdev, slot);
++	if (port->irq < 0) {
++		dev_err(dev, "Failed to get IRQ for PCIe%d\n", slot);
++		return -ENXIO;
++	}
++
+ 	INIT_LIST_HEAD(&port->list);
+ 	list_add_tail(&port->list, &pcie->ports);
+ 
+@@ -585,13 +606,15 @@ static int mt7621_pcie_init_virtual_brid
+ {
+ 	u32 pcie_link_status = 0;
+ 	u32 n;
+-	int i;
++	int i = 0;
+ 	u32 p2p_br_devnum[PCIE_P2P_MAX];
++	int irqs[PCIE_P2P_MAX];
+ 	struct mt7621_pcie_port *port;
+ 
+ 	list_for_each_entry(port, &pcie->ports, list) {
+ 		u32 slot = port->slot;
+ 
++		irqs[i++] = port->irq;
+ 		if (port->enabled)
+ 			pcie_link_status |= BIT(slot);
+ 	}
+@@ -614,6 +637,15 @@ static int mt7621_pcie_init_virtual_brid
+ 		 (p2p_br_devnum[1] << PCIE_P2P_BR_DEVNUM1_SHIFT) |
+ 		 (p2p_br_devnum[2] << PCIE_P2P_BR_DEVNUM2_SHIFT));
+ 
++	/* Assign IRQs */
++	n = 0;
++	for (i = 0; i < PCIE_P2P_MAX; i++)
++		if (pcie_link_status & BIT(i))
++			pcie->irq_map[n++] = irqs[i];
++
++	for (i = n; i < PCIE_P2P_MAX; i++)
++		pcie->irq_map[i] = -1;
++
+ 	return 0;
+ }
+ 
+@@ -638,7 +670,7 @@ static int mt7621_pcie_register_host(str
+ 	host->busnr = pcie->busn.start;
+ 	host->dev.parent = pcie->dev;
+ 	host->ops = &mt7621_pci_ops;
+-	host->map_irq = of_irq_parse_and_map_pci;
++	host->map_irq = mt7621_map_irq;
+ 	host->swizzle_irq = pci_common_swizzle;
+ 	host->sysdata = pcie;
+ 
diff --git a/target/linux/ramips/patches-5.10/0122-mips-ralink-enable-zboot-support.patch b/target/linux/ramips/patches-5.10/0122-mips-ralink-enable-zboot-support.patch
new file mode 100644
index 0000000000..76615ed491
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0122-mips-ralink-enable-zboot-support.patch
@@ -0,0 +1,26 @@
+From 1f0400d0e2c410b04f246aefb2e9b5155eb4b0bf Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213 at gmail.com>
+Date: Tue, 13 Oct 2020 10:05:47 +0800
+Subject: mips: ralink: enable zboot support
+
+Some of these ralink devices come with an ancient u-boot which can't
+extract LZMA properly when image gets too big.
+Enable zboot support to get a self-extracting kernel instead of relying
+on broken u-boot support.
+
+Signed-off-by: Chuanhong Guo <gch981213 at gmail.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend at alpha.franken.de>
+---
+ arch/mips/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -625,6 +625,7 @@ config RALINK
+ 	select SYS_SUPPORTS_32BIT_KERNEL
+ 	select SYS_SUPPORTS_LITTLE_ENDIAN
+ 	select SYS_SUPPORTS_MIPS16
++	select SYS_SUPPORTS_ZBOOT
+ 	select SYS_HAS_EARLY_PRINTK
+ 	select CLKDEV_LOOKUP
+ 	select ARCH_HAS_RESET_CONTROLLER
diff --git a/target/linux/ramips/patches-5.10/0123-mips-ralink-manage-low-reset-lines.patch b/target/linux/ramips/patches-5.10/0123-mips-ralink-manage-low-reset-lines.patch
new file mode 100644
index 0000000000..bdf98f223c
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0123-mips-ralink-manage-low-reset-lines.patch
@@ -0,0 +1,45 @@
+From 3f9ef7785a9cd69cb75f5e2ea4ca79a24752e496 Mon Sep 17 00:00:00 2001
+From: Sander Vanheule <sander at svanheule.net>
+Date: Wed, 3 Feb 2021 10:21:41 +0100
+Subject: MIPS: ralink: manage low reset lines
+
+Reset lines with indices smaller than 8 are currently considered invalid
+by the rt2880-reset reset controller.
+
+The MT7621 SoC uses a number of these low reset lines. The DTS defines
+reset lines "hsdma", "fe", and "mcm" with respective values 5, 6, and 2.
+As a result of the above restriction, these resets cannot be asserted or
+de-asserted by the reset controller. In cases where the bootloader does
+not de-assert these lines, this results in e.g. the MT7621's internal
+switch staying in reset.
+
+Change the reset controller to only ignore the system reset, so all
+reset lines with index greater than 0 are considered valid.
+
+Signed-off-by: Sander Vanheule <sander at svanheule.net>
+Acked-by: John Crispin <john at phrozen.org>
+Signed-off-by: Thomas Bogendoerfer <tsbogend at alpha.franken.de>
+---
+ arch/mips/ralink/reset.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/mips/ralink/reset.c
++++ b/arch/mips/ralink/reset.c
+@@ -27,7 +27,7 @@ static int ralink_assert_device(struct r
+ {
+ 	u32 val;
+ 
+-	if (id < 8)
++	if (id == 0)
+ 		return -1;
+ 
+ 	val = rt_sysc_r32(SYSC_REG_RESET_CTRL);
+@@ -42,7 +42,7 @@ static int ralink_deassert_device(struct
+ {
+ 	u32 val;
+ 
+-	if (id < 8)
++	if (id == 0)
+ 		return -1;
+ 
+ 	val = rt_sysc_r32(SYSC_REG_RESET_CTRL);
diff --git a/target/linux/ramips/patches-5.10/0200-linkit_bootstrap.patch b/target/linux/ramips/patches-5.10/0200-linkit_bootstrap.patch
new file mode 100644
index 0000000000..9142faaf43
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0200-linkit_bootstrap.patch
@@ -0,0 +1,97 @@
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -52,6 +52,7 @@ obj-$(CONFIG_ECHO)		+= echo/
+ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
+ obj-$(CONFIG_CXL_BASE)		+= cxl/
+ obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
++obj-$(CONFIG_SOC_MT7620)	+= linkit.o
+ obj-$(CONFIG_OCXL)		+= ocxl/
+ obj-y				+= cardreader/
+ obj-$(CONFIG_PVPANIC)   	+= pvpanic.o
+--- /dev/null
++++ b/drivers/misc/linkit.c
+@@ -0,0 +1,84 @@
++/*
++ *  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
++ *  publishhed by the Free Software Foundation.
++ *
++ *  Copyright (C) 2015 John Crispin <blogic at openwrt.org>
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/mtd/mtd.h>
++#include <linux/gpio.h>
++
++#define LINKIT_LATCH_GPIO	11
++
++struct linkit_hw_data {
++	char board[16];
++	char rev[16];
++};
++
++static void sanify_string(char *s)
++{
++	int i;
++
++	for (i = 0; i < 15; i++)
++		if (s[i] <= 0x20)
++			s[i] = '\0';
++	s[15] = '\0';
++}
++
++static int linkit_probe(struct platform_device *pdev)
++{
++	struct linkit_hw_data hw;
++	struct mtd_info *mtd;
++	size_t retlen;
++	int ret;
++
++	mtd = get_mtd_device_nm("factory");
++	if (IS_ERR(mtd))
++		return PTR_ERR(mtd);
++
++	ret = mtd_read(mtd, 0x400, sizeof(hw), &retlen, (u_char *) &hw);
++	put_mtd_device(mtd);
++
++	sanify_string(hw.board);
++	sanify_string(hw.rev);
++
++	dev_info(&pdev->dev, "Version  : %s\n", hw.board);
++	dev_info(&pdev->dev, "Revision : %s\n", hw.rev);
++
++	if (!strcmp(hw.board, "LINKITS7688")) {
++		dev_info(&pdev->dev, "setting up bootstrap latch\n");
++
++		if (devm_gpio_request(&pdev->dev, LINKIT_LATCH_GPIO, "bootstrap")) {
++			dev_err(&pdev->dev, "failed to setup bootstrap gpio\n");
++			return -1;
++		}
++		gpio_direction_output(LINKIT_LATCH_GPIO, 0);
++	}
++
++	return 0;
++}
++
++static const struct of_device_id linkit_match[] = {
++	{ .compatible = "mediatek,linkit" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, linkit_match);
++
++static struct platform_driver linkit_driver = {
++	.probe = linkit_probe,
++	.driver = {
++		.name = "mtk-linkit",
++		.owner = THIS_MODULE,
++		.of_match_table = linkit_match,
++	},
++};
++
++int __init linkit_init(void)
++{
++	return platform_driver_register(&linkit_driver);
++}
++late_initcall_sync(linkit_init);
diff --git a/target/linux/ramips/patches-5.10/0300-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch b/target/linux/ramips/patches-5.10/0300-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch
new file mode 100644
index 0000000000..ba844fed0f
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0300-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch
@@ -0,0 +1,1400 @@
+From e84e2430ee0e483842b4ff013ae8a6e7e2fa2734 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao at mediatek.com>
+Date: Wed, 1 Apr 2020 02:07:58 +0800
+Subject: [PATCH 1/2] mtd: rawnand: add driver support for MT7621 nand
+ flash controller
+
+This patch adds NAND flash controller driver for MediaTek MT7621 SoC.
+
+The NAND flash controller is similar with controllers described in
+mtk_nand.c, except that the controller from MT7621 doesn't support DMA
+transmission, and some registers' offset and fields are different.
+
+Signed-off-by: Weijie Gao <weijie.gao at mediatek.com>
+---
+ drivers/mtd/nand/raw/Kconfig       |    8 +
+ drivers/mtd/nand/raw/Makefile      |    1 +
+ drivers/mtd/nand/raw/mt7621_nand.c | 1348 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1357 insertions(+)
+ create mode 100644 drivers/mtd/nand/raw/mt7621_nand.c
+
+--- a/drivers/mtd/nand/raw/Kconfig
++++ b/drivers/mtd/nand/raw/Kconfig
+@@ -391,6 +391,14 @@ config MTD_NAND_QCOM
+ 	  Enables support for NAND flash chips on SoCs containing the EBI2 NAND
+ 	  controller. This controller is found on IPQ806x SoC.
+ 
++config MTD_NAND_MT7621
++	tristate "MT7621 NAND controller"
++	depends on SOC_MT7621 || COMPILE_TEST
++	depends on HAS_IOMEM
++	help
++	  Enables support for NAND controller on MT7621 SoC.
++	  This driver uses PIO mode for data transmission instead of DMA mode.
++
+ config MTD_NAND_MTK
+ 	tristate "MTK NAND controller"
+ 	depends on ARCH_MEDIATEK || COMPILE_TEST
+--- a/drivers/mtd/nand/raw/Makefile
++++ b/drivers/mtd/nand/raw/Makefile
+@@ -52,6 +52,7 @@ obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_n
+ obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
+ obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
++obj-$(CONFIG_MTD_NAND_MT7621)		+= mt7621_nand.o
+ obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_ecc.o mtk_nand.o
+ obj-$(CONFIG_MTD_NAND_MXIC)		+= mxic_nand.o
+ obj-$(CONFIG_MTD_NAND_TEGRA)		+= tegra_nand.o
+--- /dev/null
++++ b/drivers/mtd/nand/raw/mt7621_nand.c
+@@ -0,0 +1,1350 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * MediaTek MT7621 NAND Flash Controller driver
++ *
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao at mediatek.com>
++ */
++
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/sizes.h>
++#include <linux/iopoll.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/rawnand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/platform_device.h>
++#include <asm/addrspace.h>
++
++/* NFI core registers */
++#define NFI_CNFG			0x000
++#define   CNFG_OP_MODE_S		12
++#define   CNFG_OP_MODE_M		GENMASK(14, 12)
++#define     CNFG_OP_CUSTOM		6
++#define   CNFG_AUTO_FMT_EN		BIT(9)
++#define   CNFG_HW_ECC_EN		BIT(8)
++#define   CNFG_BYTE_RW			BIT(6)
++#define   CNFG_READ_MODE		BIT(1)
++
++#define NFI_PAGEFMT			0x004
++#define   PAGEFMT_FDM_ECC_S		12
++#define   PAGEFMT_FDM_ECC_M		GENMASK(15, 12)
++#define   PAGEFMT_FDM_S			8
++#define   PAGEFMT_FDM_M			GENMASK(11, 8)
++#define   PAGEFMT_SPARE_S		4
++#define   PAGEFMT_SPARE_M		GENMASK(5, 4)
++#define   PAGEFMT_PAGE_S		0
++#define   PAGEFMT_PAGE_M		GENMASK(1, 0)
++
++#define NFI_CON				0x008
++#define   CON_NFI_SEC_S			12
++#define   CON_NFI_SEC_M			GENMASK(15, 12)
++#define   CON_NFI_BWR			BIT(9)
++#define   CON_NFI_BRD			BIT(8)
++#define   CON_NFI_RST			BIT(1)
++#define   CON_FIFO_FLUSH		BIT(0)
++
++#define NFI_ACCCON			0x00c
++#define   ACCCON_POECS_S		28
++#define   ACCCON_POECS_MAX		0x0f
++#define   ACCCON_POECS_DEF		3
++#define   ACCCON_PRECS_S		22
++#define   ACCCON_PRECS_MAX		0x3f
++#define   ACCCON_PRECS_DEF		3
++#define   ACCCON_C2R_S			16
++#define   ACCCON_C2R_MAX		0x3f
++#define   ACCCON_C2R_DEF		7
++#define   ACCCON_W2R_S			12
++#define   ACCCON_W2R_MAX		0x0f
++#define   ACCCON_W2R_DEF		7
++#define   ACCCON_WH_S			8
++#define   ACCCON_WH_MAX			0x0f
++#define   ACCCON_WH_DEF			15
++#define   ACCCON_WST_S			4
++#define   ACCCON_WST_MAX		0x0f
++#define   ACCCON_WST_DEF		15
++#define   ACCCON_WST_MIN		3
++#define   ACCCON_RLT_S			0
++#define   ACCCON_RLT_MAX		0x0f
++#define   ACCCON_RLT_DEF		15
++#define   ACCCON_RLT_MIN		3
++
++#define NFI_CMD				0x020
++
++#define NFI_ADDRNOB			0x030
++#define   ADDR_ROW_NOB_S		4
++#define   ADDR_ROW_NOB_M		GENMASK(6, 4)
++#define   ADDR_COL_NOB_S		0
++#define   ADDR_COL_NOB_M		GENMASK(2, 0)
++
++#define NFI_COLADDR			0x034
++#define NFI_ROWADDR			0x038
++
++#define NFI_STRDATA			0x040
++#define   STR_DATA			BIT(0)
++
++#define NFI_CNRNB			0x044
++#define   CB2R_TIME_S			4
++#define   CB2R_TIME_M			GENMASK(7, 4)
++#define   STR_CNRNB			BIT(0)
++
++#define NFI_DATAW			0x050
++#define NFI_DATAR			0x054
++
++#define NFI_PIO_DIRDY			0x058
++#define   PIO_DIRDY			BIT(0)
++
++#define NFI_STA				0x060
++#define   STA_NFI_FSM_S			16
++#define   STA_NFI_FSM_M			GENMASK(19, 16)
++#define     STA_FSM_CUSTOM_DATA		14
++#define   STA_BUSY			BIT(8)
++#define   STA_ADDR			BIT(1)
++#define   STA_CMD			BIT(0)
++
++#define NFI_ADDRCNTR			0x070
++#define   SEC_CNTR_S			12
++#define   SEC_CNTR_M			GENMASK(15, 12)
++#define   SEC_ADDR_S			0
++#define   SEC_ADDR_M			GENMASK(9, 0)
++
++#define NFI_CSEL			0x090
++#define   CSEL_S			0
++#define   CSEL_M			GENMASK(1, 0)
++
++#define NFI_FDM0L			0x0a0
++#define NFI_FDML(n)			(0x0a0 + ((n) << 3))
++
++#define NFI_FDM0M			0x0a4
++#define NFI_FDMM(n)			(0x0a4 + ((n) << 3))
++
++#define NFI_MASTER_STA			0x210
++#define   MAS_ADDR			GENMASK(11, 9)
++#define   MAS_RD			GENMASK(8, 6)
++#define   MAS_WR			GENMASK(5, 3)
++#define   MAS_RDDLY			GENMASK(2, 0)
++
++/* ECC engine registers */
++#define ECC_ENCCON			0x000
++#define   ENC_EN			BIT(0)
++
++#define ECC_ENCCNFG			0x004
++#define   ENC_CNFG_MSG_S		16
++#define   ENC_CNFG_MSG_M		GENMASK(28, 16)
++#define   ENC_MODE_S			4
++#define   ENC_MODE_M			GENMASK(5, 4)
++#define     ENC_MODE_NFI		1
++#define   ENC_TNUM_S			0
++#define   ENC_TNUM_M			GENMASK(2, 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_CS_M			GENMASK(28, 16)
++#define   DEC_CON_S			12
++#define   DEC_CON_M			GENMASK(13, 12)
++#define     DEC_CON_EL			2
++#define   DEC_MODE_S			4
++#define   DEC_MODE_M			GENMASK(5, 4)
++#define     DEC_MODE_NFI		1
++#define   DEC_TNUM_S			0
++#define   DEC_TNUM_M			GENMASK(2, 0)
++
++#define ECC_DECIDLE			0x10c
++#define   DEC_IDLE			BIT(1)
++
++#define ECC_DECENUM			0x114
++#define   ERRNUM_S			2
++#define   ERRNUM_M			GENMASK(3, 0)
++
++#define ECC_DECDONE			0x118
++#define   DEC_DONE7			BIT(7)
++#define   DEC_DONE6			BIT(6)
++#define   DEC_DONE5			BIT(5)
++#define   DEC_DONE4			BIT(4)
++#define   DEC_DONE3			BIT(3)
++#define   DEC_DONE2			BIT(2)
++#define   DEC_DONE1			BIT(1)
++#define   DEC_DONE0			BIT(0)
++
++#define ECC_DECEL(n)			(0x11c + (n) * 4)
++#define   DEC_EL_ODD_S			16
++#define   DEC_EL_EVEN_S			0
++#define   DEC_EL_M			0x1fff
++#define   DEC_EL_BYTE_POS_S		3
++#define   DEC_EL_BIT_POS_M		GENMASK(3, 0)
++
++#define ECC_FDMADDR			0x13c
++
++/* ENCIDLE and DECIDLE */
++#define   ECC_IDLE			BIT(0)
++
++#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \
++	((tpoecs) << ACCCON_POECS_S | (tprecs) << ACCCON_PRECS_S | \
++	(tc2r) << ACCCON_C2R_S | (tw2r) << ACCCON_W2R_S | \
++	(twh) << ACCCON_WH_S | (twst) << ACCCON_WST_S | (trlt))
++
++#define MASTER_STA_MASK			(MAS_ADDR | MAS_RD | MAS_WR | \
++					 MAS_RDDLY)
++#define NFI_RESET_TIMEOUT		1000000
++#define NFI_CORE_TIMEOUT		500000
++#define ECC_ENGINE_TIMEOUT		500000
++
++#define ECC_SECTOR_SIZE			512
++#define ECC_PARITY_BITS			13
++
++#define NFI_FDM_SIZE		8
++
++#define MT7621_NFC_NAME			"mt7621-nand"
++
++struct mt7621_nfc {
++	struct nand_controller controller;
++	struct nand_chip nand;
++	struct clk *nfi_clk;
++	struct device *dev;
++
++	void __iomem *nfi_regs;
++	void __iomem *ecc_regs;
++
++	u32 spare_per_sector;
++};
++
++static const u16 mt7621_nfi_page_size[] = { SZ_512, SZ_2K, SZ_4K };
++static const u8 mt7621_nfi_spare_size[] = { 16, 26, 27, 28 };
++static const u8 mt7621_ecc_strength[] = { 4, 6, 8, 10, 12 };
++
++static inline u32 nfi_read32(struct mt7621_nfc *nfc, u32 reg)
++{
++	return readl(nfc->nfi_regs + reg);
++}
++
++static inline void nfi_write32(struct mt7621_nfc *nfc, u32 reg, u32 val)
++{
++	writel(val, nfc->nfi_regs + reg);
++}
++
++static inline u16 nfi_read16(struct mt7621_nfc *nfc, u32 reg)
++{
++	return readw(nfc->nfi_regs + reg);
++}
++
++static inline void nfi_write16(struct mt7621_nfc *nfc, u32 reg, u16 val)
++{
++	writew(val, nfc->nfi_regs + reg);
++}
++
++static inline void ecc_write16(struct mt7621_nfc *nfc, u32 reg, u16 val)
++{
++	writew(val, nfc->ecc_regs + reg);
++}
++
++static inline u32 ecc_read32(struct mt7621_nfc *nfc, u32 reg)
++{
++	return readl(nfc->ecc_regs + reg);
++}
++
++static inline void ecc_write32(struct mt7621_nfc *nfc, u32 reg, u32 val)
++{
++	return writel(val, nfc->ecc_regs + reg);
++}
++
++static inline u8 *oob_fdm_ptr(struct nand_chip *nand, int sect)
++{
++	return nand->oob_poi + sect * NFI_FDM_SIZE;
++}
++
++static inline u8 *oob_ecc_ptr(struct mt7621_nfc *nfc, int sect)
++{
++	struct nand_chip *nand = &nfc->nand;
++
++	return nand->oob_poi + nand->ecc.steps * NFI_FDM_SIZE +
++		sect * (nfc->spare_per_sector - NFI_FDM_SIZE);
++}
++
++static inline u8 *page_data_ptr(struct nand_chip *nand, const u8 *buf,
++				int sect)
++{
++	return (u8 *)buf + sect * nand->ecc.size;
++}
++
++static int mt7621_ecc_wait_idle(struct mt7621_nfc *nfc, u32 reg)
++{
++	struct device *dev = nfc->dev;
++	u32 val;
++	int ret;
++
++	ret = readw_poll_timeout_atomic(nfc->ecc_regs + reg, val,
++					val & ECC_IDLE, 10,
++					ECC_ENGINE_TIMEOUT);
++	if (ret) {
++		dev_warn(dev, "ECC engine timed out entering idle mode\n");
++		return -EIO;
++	}
++
++	return 0;
++}
++
++static int mt7621_ecc_decoder_wait_done(struct mt7621_nfc *nfc, u32 sect)
++{
++	struct device *dev = nfc->dev;
++	u32 val;
++	int ret;
++
++	ret = readw_poll_timeout_atomic(nfc->ecc_regs + ECC_DECDONE, val,
++					val & (1 << sect), 10,
++					ECC_ENGINE_TIMEOUT);
++
++	if (ret) {
++		dev_warn(dev, "ECC decoder for sector %d timed out\n",
++			 sect);
++		return -ETIMEDOUT;
++	}
++
++	return 0;
++}
++
++static void mt7621_ecc_encoder_op(struct mt7621_nfc *nfc, bool enable)
++{
++	mt7621_ecc_wait_idle(nfc, ECC_ENCIDLE);
++	ecc_write16(nfc, ECC_ENCCON, enable ? ENC_EN : 0);
++}
++
++static void mt7621_ecc_decoder_op(struct mt7621_nfc *nfc, bool enable)
++{
++	mt7621_ecc_wait_idle(nfc, ECC_DECIDLE);
++	ecc_write16(nfc, ECC_DECCON, enable ? DEC_EN : 0);
++}
++
++static int mt7621_ecc_correct_check(struct mt7621_nfc *nfc, u8 *sector_buf,
++				   u8 *fdm_buf, u32 sect)
++{
++	struct nand_chip *nand = &nfc->nand;
++	u32 decnum, num_error_bits, fdm_end_bits;
++	u32 error_locations, error_bit_loc;
++	u32 error_byte_pos, error_bit_pos;
++	int bitflips = 0;
++	u32 i;
++
++	decnum = ecc_read32(nfc, ECC_DECENUM);
++	num_error_bits = (decnum >> (sect << ERRNUM_S)) & ERRNUM_M;
++	fdm_end_bits = (nand->ecc.size + NFI_FDM_SIZE) << 3;
++
++	if (!num_error_bits)
++		return 0;
++
++	if (num_error_bits == ERRNUM_M)
++		return -1;
++
++	for (i = 0; i < num_error_bits; i++) {
++		error_locations = ecc_read32(nfc, ECC_DECEL(i / 2));
++		error_bit_loc = (error_locations >> ((i % 2) * DEC_EL_ODD_S)) &
++				DEC_EL_M;
++		error_byte_pos = error_bit_loc >> DEC_EL_BYTE_POS_S;
++		error_bit_pos = error_bit_loc & DEC_EL_BIT_POS_M;
++
++		if (error_bit_loc < (nand->ecc.size << 3)) {
++			if (sector_buf) {
++				sector_buf[error_byte_pos] ^=
++					(1 << error_bit_pos);
++			}
++		} else if (error_bit_loc < fdm_end_bits) {
++			if (fdm_buf) {
++				fdm_buf[error_byte_pos - nand->ecc.size] ^=
++					(1 << error_bit_pos);
++			}
++		}
++
++		bitflips++;
++	}
++
++	return bitflips;
++}
++
++static int mt7621_nfc_wait_write_completion(struct mt7621_nfc *nfc,
++					    struct nand_chip *nand)
++{
++	struct device *dev = nfc->dev;
++	u16 val;
++	int ret;
++
++	ret = readw_poll_timeout_atomic(nfc->nfi_regs + NFI_ADDRCNTR, val,
++		((val & SEC_CNTR_M) >> SEC_CNTR_S) >= nand->ecc.steps, 10,
++		NFI_CORE_TIMEOUT);
++
++	if (ret) {
++		dev_warn(dev, "NFI core write operation timed out\n");
++		return -ETIMEDOUT;
++	}
++
++	return ret;
++}
++
++static void mt7621_nfc_hw_reset(struct mt7621_nfc *nfc)
++{
++	u32 val;
++	int ret;
++
++	/* reset all registers and force the NFI master to terminate */
++	nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
++
++	/* wait for the master to finish the last transaction */
++	ret = readw_poll_timeout(nfc->nfi_regs + NFI_MASTER_STA, val,
++				 !(val & MASTER_STA_MASK), 50,
++				 NFI_RESET_TIMEOUT);
++	if (ret) {
++		dev_warn(nfc->dev, "Failed to reset NFI master in %dms\n",
++			 NFI_RESET_TIMEOUT);
++	}
++
++	/* ensure any status register affected by the NFI master is reset */
++	nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
++	nfi_write16(nfc, NFI_STRDATA, 0);
++}
++
++static inline void mt7621_nfc_hw_init(struct mt7621_nfc *nfc)
++{
++	u32 acccon;
++
++	/*
++	 * CNRNB: nand ready/busy register
++	 * -------------------------------
++	 * 7:4: timeout register for polling the NAND busy/ready signal
++	 * 0  : poll the status of the busy/ready signal after [7:4]*16 cycles.
++	 */
++	nfi_write16(nfc, NFI_CNRNB, CB2R_TIME_M | STR_CNRNB);
++
++	mt7621_nfc_hw_reset(nfc);
++
++	/* Apply default access timing */
++	acccon = ACCTIMING(ACCCON_POECS_DEF, ACCCON_PRECS_DEF, ACCCON_C2R_DEF,
++			   ACCCON_W2R_DEF, ACCCON_WH_DEF, ACCCON_WST_DEF,
++			   ACCCON_RLT_DEF);
++
++	nfi_write32(nfc, NFI_ACCCON, acccon);
++}
++
++static int mt7621_nfc_send_command(struct mt7621_nfc *nfc, u8 command)
++{
++	struct device *dev = nfc->dev;
++	u32 val;
++	int ret;
++
++	nfi_write32(nfc, NFI_CMD, command);
++
++	ret = readl_poll_timeout_atomic(nfc->nfi_regs + NFI_STA, val,
++					!(val & STA_CMD), 10,
++					NFI_CORE_TIMEOUT);
++	if (ret) {
++		dev_warn(dev, "NFI core timed out entering command mode\n");
++		return -EIO;
++	}
++
++	return 0;
++}
++
++static int mt7621_nfc_send_address_byte(struct mt7621_nfc *nfc, int addr)
++{
++	struct device *dev = nfc->dev;
++	u32 val;
++	int ret;
++
++	nfi_write32(nfc, NFI_COLADDR, addr);
++	nfi_write32(nfc, NFI_ROWADDR, 0);
++	nfi_write16(nfc, NFI_ADDRNOB, 1);
++
++	ret = readl_poll_timeout_atomic(nfc->nfi_regs + NFI_STA, val,
++					!(val & STA_ADDR), 10,
++					NFI_CORE_TIMEOUT);
++	if (ret) {
++		dev_warn(dev, "NFI core timed out entering address mode\n");
++		return -EIO;
++	}
++
++	return 0;
++}
++
++static int mt7621_nfc_send_address(struct mt7621_nfc *nfc, const u8 *addr,
++				   unsigned int naddrs)
++{
++	int ret;
++
++	while (naddrs) {
++		ret = mt7621_nfc_send_address_byte(nfc, *addr);
++		if (ret)
++			return ret;
++
++		addr++;
++		naddrs--;
++	}
++
++	return 0;
++}
++
++static void mt7621_nfc_wait_pio_ready(struct mt7621_nfc *nfc)
++{
++	struct device *dev = nfc->dev;
++	int ret;
++	u16 val;
++
++	ret = readw_poll_timeout_atomic(nfc->nfi_regs + NFI_PIO_DIRDY, val,
++					val & PIO_DIRDY, 10,
++					NFI_CORE_TIMEOUT);
++	if (ret < 0)
++		dev_err(dev, "NFI core PIO mode not ready\n");
++}
++
++static u32 mt7621_nfc_pio_read(struct mt7621_nfc *nfc, bool br)
++{
++	u32 reg;
++
++	/* after each byte read, the NFI_STA reg is reset by the hardware */
++	reg = (nfi_read32(nfc, NFI_STA) & STA_NFI_FSM_M) >> STA_NFI_FSM_S;
++	if (reg != STA_FSM_CUSTOM_DATA) {
++		reg = nfi_read16(nfc, NFI_CNFG);
++		reg |= CNFG_READ_MODE | CNFG_BYTE_RW;
++		if (!br)
++			reg &= ~CNFG_BYTE_RW;
++		nfi_write16(nfc, NFI_CNFG, reg);
++
++		/*
++		 * set to max sector to allow the HW to continue reading over
++		 * unaligned accesses
++		 */
++		nfi_write16(nfc, NFI_CON, CON_NFI_SEC_M | CON_NFI_BRD);
++
++		/* trigger to fetch data */
++		nfi_write16(nfc, NFI_STRDATA, STR_DATA);
++	}
++
++	mt7621_nfc_wait_pio_ready(nfc);
++
++	return nfi_read32(nfc, NFI_DATAR);
++}
++
++static void mt7621_nfc_read_data(struct mt7621_nfc *nfc, u8 *buf, u32 len)
++{
++	while (((uintptr_t)buf & 3) && len) {
++		*buf = mt7621_nfc_pio_read(nfc, true);
++		buf++;
++		len--;
++	}
++
++	while (len >= 4) {
++		*(u32 *)buf = mt7621_nfc_pio_read(nfc, false);
++		buf += 4;
++		len -= 4;
++	}
++
++	while (len) {
++		*buf = mt7621_nfc_pio_read(nfc, true);
++		buf++;
++		len--;
++	}
++}
++
++static void mt7621_nfc_read_data_discard(struct mt7621_nfc *nfc, u32 len)
++{
++	while (len >= 4) {
++		mt7621_nfc_pio_read(nfc, false);
++		len -= 4;
++	}
++
++	while (len) {
++		mt7621_nfc_pio_read(nfc, true);
++		len--;
++	}
++}
++
++static void mt7621_nfc_pio_write(struct mt7621_nfc *nfc, u32 val, bool bw)
++{
++	u32 reg;
++
++	reg = (nfi_read32(nfc, NFI_STA) & STA_NFI_FSM_M) >> STA_NFI_FSM_S;
++	if (reg != STA_FSM_CUSTOM_DATA) {
++		reg = nfi_read16(nfc, NFI_CNFG);
++		reg &= ~(CNFG_READ_MODE | CNFG_BYTE_RW);
++		if (bw)
++			reg |= CNFG_BYTE_RW;
++		nfi_write16(nfc, NFI_CNFG, reg);
++
++		nfi_write16(nfc, NFI_CON, CON_NFI_SEC_M | CON_NFI_BWR);
++		nfi_write16(nfc, NFI_STRDATA, STR_DATA);
++	}
++
++	mt7621_nfc_wait_pio_ready(nfc);
++	nfi_write32(nfc, NFI_DATAW, val);
++}
++
++static void mt7621_nfc_write_data(struct mt7621_nfc *nfc, const u8 *buf,
++				  u32 len)
++{
++	while (((uintptr_t)buf & 3) && len) {
++		mt7621_nfc_pio_write(nfc, *buf, true);
++		buf++;
++		len--;
++	}
++
++	while (len >= 4) {
++		mt7621_nfc_pio_write(nfc, *(const u32 *)buf, false);
++		buf += 4;
++		len -= 4;
++	}
++
++	while (len) {
++		mt7621_nfc_pio_write(nfc, *buf, true);
++		buf++;
++		len--;
++	}
++}
++
++static void mt7621_nfc_write_data_empty(struct mt7621_nfc *nfc, u32 len)
++{
++	while (len >= 4) {
++		mt7621_nfc_pio_write(nfc, 0xffffffff, false);
++		len -= 4;
++	}
++
++	while (len) {
++		mt7621_nfc_pio_write(nfc, 0xff, true);
++		len--;
++	}
++}
++
++static int mt7621_nfc_dev_ready(struct mt7621_nfc *nfc,
++				unsigned int timeout_ms)
++{
++	u32 val;
++
++	return readl_poll_timeout_atomic(nfc->nfi_regs + NFI_STA, val,
++					 !(val & STA_BUSY), 10,
++					 timeout_ms * 1000);
++}
++
++static int mt7621_nfc_exec_instr(struct nand_chip *nand,
++				 const struct nand_op_instr *instr)
++{
++	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
++
++	switch (instr->type) {
++	case NAND_OP_CMD_INSTR:
++		mt7621_nfc_hw_reset(nfc);
++		nfi_write16(nfc, NFI_CNFG, CNFG_OP_CUSTOM << CNFG_OP_MODE_S);
++		return mt7621_nfc_send_command(nfc, instr->ctx.cmd.opcode);
++	case NAND_OP_ADDR_INSTR:
++		return mt7621_nfc_send_address(nfc, instr->ctx.addr.addrs,
++					       instr->ctx.addr.naddrs);
++	case NAND_OP_DATA_IN_INSTR:
++		mt7621_nfc_read_data(nfc, instr->ctx.data.buf.in,
++				     instr->ctx.data.len);
++		return 0;
++	case NAND_OP_DATA_OUT_INSTR:
++		mt7621_nfc_write_data(nfc, instr->ctx.data.buf.out,
++				      instr->ctx.data.len);
++		return 0;
++	case NAND_OP_WAITRDY_INSTR:
++		return mt7621_nfc_dev_ready(nfc,
++					    instr->ctx.waitrdy.timeout_ms);
++	default:
++		WARN_ONCE(1, "unsupported NAND instruction type: %d\n",
++			  instr->type);
++
++		return -EINVAL;
++	}
++}
++
++static int mt7621_nfc_exec_op(struct nand_chip *nand,
++			      const struct nand_operation *op, bool check_only)
++{
++	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
++	int i, ret;
++
++	if (check_only)
++		return 0;
++
++	/* Only CS0 available */
++	nfi_write16(nfc, NFI_CSEL, 0);
++
++	for (i = 0; i < op->ninstrs; i++) {
++		ret = mt7621_nfc_exec_instr(nand, &op->instrs[i]);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
++}
++
++static int mt7621_nfc_setup_data_interface(struct nand_chip *nand, int csline,
++					   const struct nand_data_interface *conf)
++{
++	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
++	const struct nand_sdr_timings *timings;
++	u32 acccon, temp, rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt;
++
++	if (!nfc->nfi_clk)
++		return -ENOTSUPP;
++
++	timings = nand_get_sdr_timings(conf);
++	if (IS_ERR(timings))
++		return -ENOTSUPP;
++
++	rate = clk_get_rate(nfc->nfi_clk);
++
++	/* turn clock rate into KHZ */
++	rate /= 1000;
++
++	tpoecs = max(timings->tALH_min, timings->tCLH_min) / 1000;
++	tpoecs = DIV_ROUND_UP(tpoecs * rate, 1000000);
++	tpoecs = min_t(u32, tpoecs, ACCCON_POECS_MAX);
++
++	tprecs = max(timings->tCLS_min, timings->tALS_min) / 1000;
++	tprecs = DIV_ROUND_UP(tprecs * rate, 1000000);
++	tprecs = min_t(u32, tprecs, ACCCON_PRECS_MAX);
++
++	/* sdr interface has no tCR which means CE# low to RE# low */
++	tc2r = 0;
++
++	tw2r = timings->tWHR_min / 1000;
++	tw2r = DIV_ROUND_UP(tw2r * rate, 1000000);
++	tw2r = DIV_ROUND_UP(tw2r - 1, 2);
++	tw2r = min_t(u32, tw2r, ACCCON_W2R_MAX);
++
++	twh = max(timings->tREH_min, timings->tWH_min) / 1000;
++	twh = DIV_ROUND_UP(twh * rate, 1000000) - 1;
++	twh = min_t(u32, twh, ACCCON_WH_MAX);
++
++	/* Calculate real WE#/RE# hold time in nanosecond */
++	temp = (twh + 1) * 1000000 / rate;
++	/* nanosecond to picosecond */
++	temp *= 1000;
++
++	/*
++	 * WE# low level time should be expaned to meet WE# pulse time
++	 * and WE# cycle time at the same time.
++	 */
++	if (temp < timings->tWC_min)
++		twst = timings->tWC_min - temp;
++	else
++		twst = 0;
++	twst = max(timings->tWP_min, twst) / 1000;
++	twst = DIV_ROUND_UP(twst * rate, 1000000) - 1;
++	twst = min_t(u32, twst, ACCCON_WST_MAX);
++
++	/*
++	 * RE# low level time should be expaned to meet RE# pulse time
++	 * and RE# cycle time at the same time.
++	 */
++	if (temp < timings->tRC_min)
++		trlt = timings->tRC_min - temp;
++	else
++		trlt = 0;
++	trlt = max(trlt, timings->tRP_min) / 1000;
++	trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1;
++	trlt = min_t(u32, trlt, ACCCON_RLT_MAX);
++
++	if (csline == NAND_DATA_IFACE_CHECK_ONLY) {
++		if (twst < ACCCON_WST_MIN || trlt < ACCCON_RLT_MIN)
++			return -ENOTSUPP;
++	}
++
++	acccon = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt);
++
++	dev_info(nfc->dev, "Using programmed access timing: %08x\n", acccon);
++
++	nfi_write32(nfc, NFI_ACCCON, acccon);
++
++	return 0;
++}
++
++static int mt7621_nfc_calc_ecc_strength(struct mt7621_nfc *nfc,
++					u32 avail_ecc_bytes)
++{
++	struct nand_chip *nand = &nfc->nand;
++	struct mtd_info *mtd = nand_to_mtd(nand);
++	u32 strength;
++	int i;
++
++	strength = avail_ecc_bytes * 8 / ECC_PARITY_BITS;
++
++	/* Find the closest supported ecc strength */
++	for (i = ARRAY_SIZE(mt7621_ecc_strength) - 1; i >= 0; i--) {
++		if (mt7621_ecc_strength[i] <= strength)
++			break;
++	}
++
++	if (unlikely(i < 0)) {
++		dev_err(nfc->dev, "OOB size (%u) is not supported\n",
++			mtd->oobsize);
++		return -EINVAL;
++	}
++
++	nand->ecc.strength = mt7621_ecc_strength[i];
++	nand->ecc.bytes =
++		DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8);
++
++	dev_info(nfc->dev, "ECC strength adjusted to %u bits\n",
++		 nand->ecc.strength);
++
++	return i;
++}
++
++static int mt7621_nfc_set_spare_per_sector(struct mt7621_nfc *nfc)
++{
++	struct nand_chip *nand = &nfc->nand;
++	struct mtd_info *mtd = nand_to_mtd(nand);
++	u32 size;
++	int i;
++
++	size = nand->ecc.bytes + NFI_FDM_SIZE;
++
++	/* Find the closest supported spare size */
++	for (i = 0; i < ARRAY_SIZE(mt7621_nfi_spare_size); i++) {
++		if (mt7621_nfi_spare_size[i] >= size)
++			break;
++	}
++
++	if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_spare_size))) {
++		dev_err(nfc->dev, "OOB size (%u) is not supported\n",
++			mtd->oobsize);
++		return -EINVAL;
++	}
++
++	nfc->spare_per_sector = mt7621_nfi_spare_size[i];
++
++	return i;
++}
++
++static int mt7621_nfc_ecc_init(struct mt7621_nfc *nfc)
++{
++	struct nand_chip *nand = &nfc->nand;
++	struct mtd_info *mtd = nand_to_mtd(nand);
++	u32 spare_per_sector, encode_block_size, decode_block_size;
++	u32 ecc_enccfg, ecc_deccfg;
++	int ecc_cap;
++
++	/* Only hardware ECC mode is supported */
++	if (nand->ecc.mode != NAND_ECC_HW_SYNDROME) {
++		dev_err(nfc->dev, "Only hardware ECC mode is supported\n");
++		return -EINVAL;
++	}
++
++	nand->ecc.size = ECC_SECTOR_SIZE;
++	nand->ecc.steps = mtd->writesize / nand->ecc.size;
++
++	spare_per_sector = mtd->oobsize / nand->ecc.steps;
++
++	ecc_cap = mt7621_nfc_calc_ecc_strength(nfc,
++		spare_per_sector - NFI_FDM_SIZE);
++	if (ecc_cap < 0)
++		return ecc_cap;
++
++	/* Sector + FDM */
++	encode_block_size = (nand->ecc.size + NFI_FDM_SIZE) * 8;
++	ecc_enccfg = ecc_cap | (ENC_MODE_NFI << ENC_MODE_S) |
++		     (encode_block_size << ENC_CNFG_MSG_S);
++
++	/* Sector + FDM + ECC parity bits */
++	decode_block_size = ((nand->ecc.size + NFI_FDM_SIZE) * 8) +
++			    nand->ecc.strength * ECC_PARITY_BITS;
++	ecc_deccfg = ecc_cap | (DEC_MODE_NFI << DEC_MODE_S) |
++		     (decode_block_size << DEC_CS_S) |
++		     (DEC_CON_EL << DEC_CON_S) | DEC_EMPTY_EN;
++
++	mt7621_ecc_encoder_op(nfc, false);
++	ecc_write32(nfc, ECC_ENCCNFG, ecc_enccfg);
++
++	mt7621_ecc_decoder_op(nfc, false);
++	ecc_write32(nfc, ECC_DECCNFG, ecc_deccfg);
++
++	return 0;
++}
++
++static int mt7621_nfc_set_page_format(struct mt7621_nfc *nfc)
++{
++	struct nand_chip *nand = &nfc->nand;
++	struct mtd_info *mtd = nand_to_mtd(nand);
++	int i, spare_size;
++	u32 pagefmt;
++
++	spare_size = mt7621_nfc_set_spare_per_sector(nfc);
++	if (spare_size < 0)
++		return spare_size;
++
++	for (i = 0; i < ARRAY_SIZE(mt7621_nfi_page_size); i++) {
++		if (mt7621_nfi_page_size[i] == mtd->writesize)
++			break;
++	}
++
++	if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_page_size))) {
++		dev_err(nfc->dev, "Page size (%u) is not supported\n",
++			mtd->writesize);
++		return -EINVAL;
++	}
++
++	pagefmt = i | (spare_size << PAGEFMT_SPARE_S) |
++		  (NFI_FDM_SIZE << PAGEFMT_FDM_S) |
++		  (NFI_FDM_SIZE << PAGEFMT_FDM_ECC_S);
++
++	nfi_write16(nfc, NFI_PAGEFMT, pagefmt);
++
++	return 0;
++}
++
++static int mt7621_nfc_attach_chip(struct nand_chip *nand)
++{
++	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
++	int ret;
++
++	if (nand->options & NAND_BUSWIDTH_16) {
++		dev_err(nfc->dev, "16-bit buswidth is not supported");
++		return -EINVAL;
++	}
++
++	ret = mt7621_nfc_ecc_init(nfc);
++	if (ret)
++		return ret;
++
++	return mt7621_nfc_set_page_format(nfc);
++}
++
++static const struct nand_controller_ops mt7621_nfc_controller_ops = {
++	.attach_chip = mt7621_nfc_attach_chip,
++	.exec_op = mt7621_nfc_exec_op,
++	.setup_data_interface = mt7621_nfc_setup_data_interface,
++};
++
++static int mt7621_nfc_ooblayout_free(struct mtd_info *mtd, int section,
++				     struct mtd_oob_region *oob_region)
++{
++	struct nand_chip *nand = mtd_to_nand(mtd);
++
++	if (section >= nand->ecc.steps)
++		return -ERANGE;
++
++	oob_region->length = NFI_FDM_SIZE - 1;
++	oob_region->offset = section * NFI_FDM_SIZE + 1;
++
++	return 0;
++}
++
++static int mt7621_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
++				    struct mtd_oob_region *oob_region)
++{
++	struct nand_chip *nand = mtd_to_nand(mtd);
++
++	if (section)
++		return -ERANGE;
++
++	oob_region->offset = NFI_FDM_SIZE * nand->ecc.steps;
++	oob_region->length = mtd->oobsize - oob_region->offset;
++
++	return 0;
++}
++
++static const struct mtd_ooblayout_ops mt7621_nfc_ooblayout_ops = {
++	.free = mt7621_nfc_ooblayout_free,
++	.ecc = mt7621_nfc_ooblayout_ecc,
++};
++
++static void mt7621_nfc_write_fdm(struct mt7621_nfc *nfc)
++{
++	struct nand_chip *nand = &nfc->nand;
++	u32 vall, valm;
++	u8 *oobptr;
++	int i, j;
++
++	for (i = 0; i < nand->ecc.steps; i++) {
++		vall = 0;
++		valm = 0;
++		oobptr = oob_fdm_ptr(nand, i);
++
++		for (j = 0; j < 4; j++)
++			vall |= (u32)oobptr[j] << (j * 8);
++
++		for (j = 0; j < 4; j++)
++			valm |= (u32)oobptr[j + 4] << ((j - 4) * 8);
++
++		nfi_write32(nfc, NFI_FDML(i), vall);
++		nfi_write32(nfc, NFI_FDMM(i), valm);
++	}
++}
++
++static void mt7621_nfc_read_sector_fdm(struct mt7621_nfc *nfc, u32 sect)
++{
++	struct nand_chip *nand = &nfc->nand;
++	u32 vall, valm;
++	u8 *oobptr;
++	int i;
++
++	vall = nfi_read32(nfc, NFI_FDML(sect));
++	valm = nfi_read32(nfc, NFI_FDMM(sect));
++	oobptr = oob_fdm_ptr(nand, sect);
++
++	for (i = 0; i < 4; i++)
++		oobptr[i] = (vall >> (i * 8)) & 0xff;
++
++	for (i = 0; i < 4; i++)
++		oobptr[i + 4] = (valm >> (i * 8)) & 0xff;
++}
++
++static int mt7621_nfc_read_page_hwecc(struct nand_chip *nand, uint8_t *buf,
++				      int oob_required, int page)
++{
++	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
++	struct mtd_info *mtd = nand_to_mtd(nand);
++	int bitflips = 0;
++	int rc, i;
++
++	nand_read_page_op(nand, page, 0, NULL, 0);
++
++	nfi_write16(nfc, NFI_CNFG, (CNFG_OP_CUSTOM << CNFG_OP_MODE_S) |
++		    CNFG_READ_MODE | CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
++
++	mt7621_ecc_decoder_op(nfc, true);
++
++	nfi_write16(nfc, NFI_CON,
++		    CON_NFI_BRD | (nand->ecc.steps << CON_NFI_SEC_S));
++
++	for (i = 0; i < nand->ecc.steps; i++) {
++		if (buf)
++			mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i),
++					     nand->ecc.size);
++		else
++			mt7621_nfc_read_data_discard(nfc, nand->ecc.size);
++
++		rc = mt7621_ecc_decoder_wait_done(nfc, i);
++
++		mt7621_nfc_read_sector_fdm(nfc, i);
++
++		if (rc < 0) {
++			bitflips = -EIO;
++			continue;
++		}
++
++		rc = mt7621_ecc_correct_check(nfc,
++			buf ? page_data_ptr(nand, buf, i) : NULL,
++			oob_fdm_ptr(nand, i), i);
++
++		if (rc < 0) {
++			dev_warn(nfc->dev,
++				 "Uncorrectable ECC error at page %d.%d\n",
++				 page, i);
++			bitflips = -EBADMSG;
++			mtd->ecc_stats.failed++;
++		} else if (bitflips >= 0) {
++			bitflips += rc;
++			mtd->ecc_stats.corrected += rc;
++		}
++	}
++
++	mt7621_ecc_decoder_op(nfc, false);
++
++	nfi_write16(nfc, NFI_CON, 0);
++
++	return bitflips;
++}
++
++static int mt7621_nfc_read_page_raw(struct nand_chip *nand, uint8_t *buf,
++				    int oob_required, int page)
++{
++	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
++	int i;
++
++	nand_read_page_op(nand, page, 0, NULL, 0);
++
++	nfi_write16(nfc, NFI_CNFG, (CNFG_OP_CUSTOM << CNFG_OP_MODE_S) |
++		    CNFG_READ_MODE);
++
++	nfi_write16(nfc, NFI_CON,
++		    CON_NFI_BRD | (nand->ecc.steps << CON_NFI_SEC_S));
++
++	for (i = 0; i < nand->ecc.steps; i++) {
++		/* Read data */
++		if (buf)
++			mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i),
++					     nand->ecc.size);
++		else
++			mt7621_nfc_read_data_discard(nfc, nand->ecc.size);
++
++		/* Read FDM */
++		mt7621_nfc_read_data(nfc, oob_fdm_ptr(nand, i), NFI_FDM_SIZE);
++
++		/* Read ECC parity data */
++		mt7621_nfc_read_data(nfc, oob_ecc_ptr(nfc, i),
++				     nfc->spare_per_sector - NFI_FDM_SIZE);
++	}
++
++	nfi_write16(nfc, NFI_CON, 0);
++
++	return 0;
++}
++
++static int mt7621_nfc_read_oob_hwecc(struct nand_chip *nand, int page)
++{
++	return mt7621_nfc_read_page_hwecc(nand, NULL, 1, page);
++}
++
++static int mt7621_nfc_read_oob_raw(struct nand_chip *nand, int page)
++{
++	return mt7621_nfc_read_page_raw(nand, NULL, 1, page);
++}
++
++static int mt7621_nfc_check_empty_page(struct nand_chip *nand, const u8 *buf)
++{
++	struct mtd_info *mtd = nand_to_mtd(nand);
++	uint32_t i, j;
++	u8 *oobptr;
++
++	if (buf) {
++		for (i = 0; i < mtd->writesize; i++)
++			if (buf[i] != 0xff)
++				return 0;
++	}
++
++	for (i = 0; i < nand->ecc.steps; i++) {
++		oobptr = oob_fdm_ptr(nand, i);
++		for (j = 0; j < NFI_FDM_SIZE; j++)
++			if (oobptr[j] != 0xff)
++				return 0;
++	}
++
++	return 1;
++}
++
++static int mt7621_nfc_write_page_hwecc(struct nand_chip *nand,
++				       const uint8_t *buf, int oob_required,
++				       int page)
++{
++	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
++	struct mtd_info *mtd = nand_to_mtd(nand);
++
++	if (mt7621_nfc_check_empty_page(nand, buf)) {
++		/*
++		 * MT7621 ECC engine always generates parity code for input
++		 * pages, even for empty pages. Doing so will write back ECC
++		 * parity code to the oob region, which means such pages will
++		 * no longer be empty pages.
++		 *
++		 * To avoid this, stop write operation if current page is an
++		 * empty page.
++		 */
++		return 0;
++	}
++
++	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
++
++	nfi_write16(nfc, NFI_CNFG, (CNFG_OP_CUSTOM << CNFG_OP_MODE_S) |
++		   CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
++
++	mt7621_ecc_encoder_op(nfc, true);
++
++	mt7621_nfc_write_fdm(nfc);
++
++	nfi_write16(nfc, NFI_CON,
++		    CON_NFI_BWR | (nand->ecc.steps << CON_NFI_SEC_S));
++
++	if (buf)
++		mt7621_nfc_write_data(nfc, buf, mtd->writesize);
++	else
++		mt7621_nfc_write_data_empty(nfc, mtd->writesize);
++
++	mt7621_nfc_wait_write_completion(nfc, nand);
++
++	mt7621_ecc_encoder_op(nfc, false);
++
++	nfi_write16(nfc, NFI_CON, 0);
++
++	return nand_prog_page_end_op(nand);
++}
++
++static int mt7621_nfc_write_page_raw(struct nand_chip *nand,
++				     const uint8_t *buf, int oob_required,
++				     int page)
++{
++	struct mt7621_nfc *nfc = nand_get_controller_data(nand);
++	int i;
++
++	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
++
++	nfi_write16(nfc, NFI_CNFG, (CNFG_OP_CUSTOM << CNFG_OP_MODE_S));
++
++	nfi_write16(nfc, NFI_CON,
++		    CON_NFI_BWR | (nand->ecc.steps << CON_NFI_SEC_S));
++
++	for (i = 0; i < nand->ecc.steps; i++) {
++		/* Write data */
++		if (buf)
++			mt7621_nfc_write_data(nfc, page_data_ptr(nand, buf, i),
++					      nand->ecc.size);
++		else
++			mt7621_nfc_write_data_empty(nfc, nand->ecc.size);
++
++		/* Write FDM */
++		mt7621_nfc_write_data(nfc, oob_fdm_ptr(nand, i),
++				      NFI_FDM_SIZE);
++
++		/* Write dummy ECC parity data */
++		mt7621_nfc_write_data_empty(nfc, nfc->spare_per_sector -
++					    NFI_FDM_SIZE);
++	}
++
++	mt7621_nfc_wait_write_completion(nfc, nand);
++
++	nfi_write16(nfc, NFI_CON, 0);
++
++	return nand_prog_page_end_op(nand);
++}
++
++static int mt7621_nfc_write_oob_hwecc(struct nand_chip *nand, int page)
++{
++	return mt7621_nfc_write_page_hwecc(nand, NULL, 1, page);
++}
++
++static int mt7621_nfc_write_oob_raw(struct nand_chip *nand, int page)
++{
++	return mt7621_nfc_write_page_raw(nand, NULL, 1, page);
++}
++
++static int mt7621_nfc_init_chip(struct mt7621_nfc *nfc)
++{
++	struct nand_chip *nand = &nfc->nand;
++	struct mtd_info *mtd;
++	int ret;
++
++	nand->controller = &nfc->controller;
++	nand_set_controller_data(nand, (void *)nfc);
++	nand_set_flash_node(nand, nfc->dev->of_node);
++
++	nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_NO_SUBPAGE_WRITE;
++	if (!nfc->nfi_clk)
++		nand->options |= NAND_KEEP_TIMINGS;
++
++	nand->ecc.mode = NAND_ECC_HW_SYNDROME;
++	nand->ecc.read_page = mt7621_nfc_read_page_hwecc;
++	nand->ecc.read_page_raw = mt7621_nfc_read_page_raw;
++	nand->ecc.write_page = mt7621_nfc_write_page_hwecc;
++	nand->ecc.write_page_raw = mt7621_nfc_write_page_raw;
++	nand->ecc.read_oob = mt7621_nfc_read_oob_hwecc;
++	nand->ecc.read_oob_raw = mt7621_nfc_read_oob_raw;
++	nand->ecc.write_oob = mt7621_nfc_write_oob_hwecc;
++	nand->ecc.write_oob_raw = mt7621_nfc_write_oob_raw;
++
++	mtd = nand_to_mtd(nand);
++	mtd->owner = THIS_MODULE;
++	mtd->dev.parent = nfc->dev;
++	mtd->name = MT7621_NFC_NAME;
++	mtd_set_ooblayout(mtd, &mt7621_nfc_ooblayout_ops);
++
++	mt7621_nfc_hw_init(nfc);
++
++	ret = nand_scan(nand, 1);
++	if (ret)
++		return ret;
++
++	ret = mtd_device_register(mtd, NULL, 0);
++	if (ret) {
++		dev_err(nfc->dev, "Failed to register MTD: %d\n", ret);
++		nand_release(nand);
++		return ret;
++	}
++
++	return 0;
++}
++
++static int mt7621_nfc_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct mt7621_nfc *nfc;
++	struct resource *res;
++	int ret;
++
++	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
++	if (!nfc)
++		return -ENOMEM;
++
++	nand_controller_init(&nfc->controller);
++	nfc->controller.ops = &mt7621_nfc_controller_ops;
++	nfc->dev = dev;
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nfi");
++	nfc->nfi_regs = devm_ioremap_resource(dev, res);
++	if (IS_ERR(nfc->nfi_regs)) {
++		ret = PTR_ERR(nfc->nfi_regs);
++		return ret;
++	}
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecc");
++	nfc->ecc_regs = devm_ioremap_resource(dev, res);
++	if (IS_ERR(nfc->ecc_regs)) {
++		ret = PTR_ERR(nfc->ecc_regs);
++		return ret;
++	}
++
++	nfc->nfi_clk = devm_clk_get(dev, "nfi_clk");
++	if (IS_ERR(nfc->nfi_clk)) {
++		dev_warn(dev, "nfi clk not provided\n");
++		nfc->nfi_clk = NULL;
++	} else {
++		ret = clk_prepare_enable(nfc->nfi_clk);
++		if (ret) {
++			dev_err(dev, "Failed to enable nfi core clock\n");
++			return ret;
++		}
++	}
++
++	platform_set_drvdata(pdev, nfc);
++
++	ret = mt7621_nfc_init_chip(nfc);
++	if (ret) {
++		dev_err(dev, "Failed to initialize nand chip\n");
++		goto clk_disable;
++	}
++
++	return 0;
++
++clk_disable:
++	clk_disable_unprepare(nfc->nfi_clk);
++
++	return ret;
++}
++
++static int mt7621_nfc_remove(struct platform_device *pdev)
++{
++	struct mt7621_nfc *nfc = platform_get_drvdata(pdev);
++
++	nand_release(&nfc->nand);
++	clk_disable_unprepare(nfc->nfi_clk);
++
++	return 0;
++}
++
++static const struct of_device_id mt7621_nfc_id_table[] = {
++	{ .compatible = "mediatek,mt7621-nfc" },
++	{ },
++};
++MODULE_DEVICE_TABLE(of, match);
++
++static struct platform_driver mt7621_nfc_driver = {
++	.probe = mt7621_nfc_probe,
++	.remove = mt7621_nfc_remove,
++	.driver = {
++		.name = MT7621_NFC_NAME,
++		.owner = THIS_MODULE,
++		.of_match_table = mt7621_nfc_id_table,
++	},
++};
++module_platform_driver(mt7621_nfc_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Weijie Gao <weijie.gao at mediatek.com>");
++MODULE_DESCRIPTION("MediaTek MT7621 NAND Flash Controller driver");
diff --git a/target/linux/ramips/patches-5.10/0301-dt-bindings-add-documentation-for-mt7621-nand-driver.patch b/target/linux/ramips/patches-5.10/0301-dt-bindings-add-documentation-for-mt7621-nand-driver.patch
new file mode 100644
index 0000000000..3d122c10c0
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/0301-dt-bindings-add-documentation-for-mt7621-nand-driver.patch
@@ -0,0 +1,85 @@
+From 3d5f4da8296b23eb3abf8b13122b0d06a215e79c Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao at mediatek.com>
+Date: Wed, 1 Apr 2020 02:07:59 +0800
+Subject: [PATCH 2/2] dt-bindings: add documentation for mt7621-nand driver
+
+This patch adds documentation for MediaTek MT7621 NAND flash controller
+driver.
+
+Signed-off-by: Weijie Gao <weijie.gao at mediatek.com>
+---
+ .../bindings/mtd/mediatek,mt7621-nfc.yaml          | 68 ++++++++++++++++++++++
+ 1 file changed, 68 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/mediatek,mt7621-nfc.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/mediatek,mt7621-nfc.yaml
+@@ -0,0 +1,68 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/mediatek,mt7621-nfc.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: MediaTek MT7621 SoC NAND Flash Controller (NFC) DT binding
++
++maintainers:
++  - Weijie Gao <weijie.gao at mediatek.com>
++
++description: |
++  This driver uses a single node to describe both NAND Flash controller
++  interface (NFI) and ECC engine for MT7621 SoC.
++  MT7621 supports only one chip select.
++
++properties:
++  "#address-cells": false
++  "#size-cells": false
++
++  compatible:
++    enum:
++      - mediatek,mt7621-nfc
++
++  reg:
++    items:
++      - description: Register base of NFI core
++      - description: Register base of ECC engine
++
++  reg-names:
++    items:
++      - const: nfi
++      - const: ecc
++
++  clocks:
++    items:
++      - description: Source clock for NFI core, fixed 125MHz
++
++  clock-names:
++    items:
++      - const: nfi_clk
++
++required:
++  - compatible
++  - reg
++  - reg-names
++  - clocks
++  - clock-names
++
++examples:
++  - |
++    nficlock: nficlock {
++    	#clock-cells = <0>;
++    	compatible = "fixed-clock";
++
++    	clock-frequency = <125000000>;
++    };
++
++    nand at 1e003000 {
++    	compatible = "mediatek,mt7621-nfc";
++
++    	reg = <0x1e003000 0x800
++    	       0x1e003800 0x800>;
++    	reg-names = "nfi", "ecc";
++
++    	clocks = <&nficlock>;
++    	clock-names = "nfi_clk";
++    };
diff --git a/target/linux/ramips/patches-5.10/100-mt7621-core-detect-hack.patch b/target/linux/ramips/patches-5.10/100-mt7621-core-detect-hack.patch
new file mode 100644
index 0000000000..1c8b61f8ef
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/100-mt7621-core-detect-hack.patch
@@ -0,0 +1,61 @@
+There is a variant of MT7621 which contains only one CPU core instead of 2.
+This is not reflected in the config register, so the kernel detects more
+physical cores, which leads to a hang on SMP bringup.
+Add a hack to detect missing cores.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+
+--- a/arch/mips/kernel/smp-cps.c
++++ b/arch/mips/kernel/smp-cps.c
+@@ -43,6 +43,11 @@ static unsigned core_vpe_count(unsigned
+ 	return mips_cps_numvps(cluster, core);
+ }
+ 
++bool __weak plat_cpu_core_present(int core)
++{
++	return true;
++}
++
+ static void __init cps_smp_setup(void)
+ {
+ 	unsigned int nclusters, ncores, nvpes, core_vpes;
+@@ -60,6 +65,8 @@ static void __init cps_smp_setup(void)
+ 
+ 		ncores = mips_cps_numcores(cl);
+ 		for (c = 0; c < ncores; c++) {
++			if (!plat_cpu_core_present(c))
++				continue;
+ 			core_vpes = core_vpe_count(cl, c);
+ 
+ 			if (c > 0)
+--- a/arch/mips/ralink/mt7621.c
++++ b/arch/mips/ralink/mt7621.c
+@@ -13,6 +13,7 @@
+ #include <asm/mips-cps.h>
+ #include <asm/mach-ralink/ralink_regs.h>
+ #include <asm/mach-ralink/mt7621.h>
++#include <asm/mips-boards/launch.h>
+ 
+ #include <pinmux.h>
+ 
+@@ -160,6 +161,20 @@ void __init ralink_of_remap(void)
+ 		panic("Failed to remap core resources");
+ }
+ 
++bool plat_cpu_core_present(int core)
++{
++	struct cpulaunch *launch = (struct cpulaunch *)CKSEG0ADDR(CPULAUNCH);
++
++	if (!core)
++		return true;
++	launch += core * 2; /* 2 VPEs per core */
++	if (!(launch->flags & LAUNCH_FREADY))
++		return false;
++	if (launch->flags & (LAUNCH_FGO | LAUNCH_FGONE))
++		return false;
++	return true;
++}
++
+ void prom_soc_init(struct ralink_soc_info *soc_info)
+ {
+ 	void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7621_SYSC_BASE);
diff --git a/target/linux/ramips/patches-5.10/101-mt7621-timer.patch b/target/linux/ramips/patches-5.10/101-mt7621-timer.patch
new file mode 100644
index 0000000000..8528b71c88
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/101-mt7621-timer.patch
@@ -0,0 +1,87 @@
+--- a/arch/mips/ralink/mt7621.c
++++ b/arch/mips/ralink/mt7621.c
+@@ -7,6 +7,7 @@
+ 
+ #include <linux/kernel.h>
+ #include <linux/init.h>
++#include <linux/jiffies.h>
+ 
+ #include <asm/mipsregs.h>
+ #include <asm/smp-ops.h>
+@@ -14,6 +15,7 @@
+ #include <asm/mach-ralink/ralink_regs.h>
+ #include <asm/mach-ralink/mt7621.h>
+ #include <asm/mips-boards/launch.h>
++#include <asm/delay.h>
+ 
+ #include <pinmux.h>
+ 
+@@ -175,6 +177,58 @@ bool plat_cpu_core_present(int core)
+ 	return true;
+ }
+ 
++#define LPS_PREC 8
++/*
++*  Re-calibration lpj(loop-per-jiffy).
++*  (derived from kernel/calibrate.c)
++*/
++static int udelay_recal(void)
++{
++	unsigned int i, lpj = 0;
++	unsigned long ticks, loopbit;
++	int lps_precision = LPS_PREC;
++
++	lpj = (1<<12);
++
++	while ((lpj <<= 1) != 0) {
++		/* wait for "start of" clock tick */
++		ticks = jiffies;
++		while (ticks == jiffies)
++			/* nothing */;
++
++		/* Go .. */
++		ticks = jiffies;
++		__delay(lpj);
++		ticks = jiffies - ticks;
++		if (ticks)
++			break;
++	}
++
++	/*
++	 * Do a binary approximation to get lpj set to
++	 * equal one clock (up to lps_precision bits)
++	 */
++	lpj >>= 1;
++	loopbit = lpj;
++	while (lps_precision-- && (loopbit >>= 1)) {
++		lpj |= loopbit;
++		ticks = jiffies;
++		while (ticks == jiffies)
++			/* nothing */;
++		ticks = jiffies;
++		__delay(lpj);
++		if (jiffies != ticks)   /* longer than 1 tick */
++			lpj &= ~loopbit;
++	}
++	printk(KERN_INFO "%d CPUs re-calibrate udelay(lpj = %d)\n", NR_CPUS, lpj);
++
++	for(i=0; i< NR_CPUS; i++)
++		cpu_data[i].udelay_val = lpj;
++
++	return 0;
++}
++device_initcall(udelay_recal);
++
+ void prom_soc_init(struct ralink_soc_info *soc_info)
+ {
+ 	void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7621_SYSC_BASE);
+--- a/arch/mips/ralink/Kconfig
++++ b/arch/mips/ralink/Kconfig
+@@ -58,6 +58,7 @@ choice
+ 		select CLKSRC_MIPS_GIC
+ 		select HAVE_PCI if PCI_MT7621
+ 		select WEAK_REORDERING_BEYOND_LLSC
++		select GENERIC_CLOCKEVENTS_BROADCAST
+ endchoice
+ 
+ choice
diff --git a/target/linux/ramips/patches-5.10/102-mt7621-fix-cpu-clk-add-clkdev.patch b/target/linux/ramips/patches-5.10/102-mt7621-fix-cpu-clk-add-clkdev.patch
new file mode 100644
index 0000000000..0c997a3f28
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/102-mt7621-fix-cpu-clk-add-clkdev.patch
@@ -0,0 +1,224 @@
+--- a/arch/mips/include/asm/mach-ralink/mt7621.h
++++ b/arch/mips/include/asm/mach-ralink/mt7621.h
+@@ -17,6 +17,10 @@
+ #define SYSC_REG_CHIP_REV		0x0c
+ #define SYSC_REG_SYSTEM_CONFIG0		0x10
+ #define SYSC_REG_SYSTEM_CONFIG1		0x14
++#define SYSC_REG_CLKCFG0		0x2c
++#define SYSC_REG_CUR_CLK_STS		0x44
++
++#define MEMC_REG_CPU_PLL		0x648
+ 
+ #define CHIP_REV_PKG_MASK		0x1
+ #define CHIP_REV_PKG_SHIFT		16
+@@ -24,6 +28,22 @@
+ #define CHIP_REV_VER_SHIFT		8
+ #define CHIP_REV_ECO_MASK		0xf
+ 
++#define XTAL_MODE_SEL_MASK		0x7
++#define XTAL_MODE_SEL_SHIFT		6
++
++#define CPU_CLK_SEL_MASK		0x3
++#define CPU_CLK_SEL_SHIFT		30
++
++#define CUR_CPU_FDIV_MASK		0x1f
++#define CUR_CPU_FDIV_SHIFT		8
++#define CUR_CPU_FFRAC_MASK		0x1f
++#define CUR_CPU_FFRAC_SHIFT		0
++
++#define CPU_PLL_PREDIV_MASK		0x3
++#define CPU_PLL_PREDIV_SHIFT		12
++#define CPU_PLL_FBDIV_MASK		0x7f
++#define CPU_PLL_FBDIV_SHIFT		4
++
+ #define MT7621_DRAM_BASE                0x0
+ #define MT7621_DDR2_SIZE_MIN		32
+ #define MT7621_DDR2_SIZE_MAX		256
+--- a/arch/mips/ralink/mt7621.c
++++ b/arch/mips/ralink/mt7621.c
+@@ -8,6 +8,10 @@
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/jiffies.h>
++#include <linux/clk.h>
++#include <linux/clkdev.h>
++#include <linux/clk-provider.h>
++#include <dt-bindings/clock/mt7621-clk.h>
+ 
+ #include <asm/mipsregs.h>
+ #include <asm/smp-ops.h>
+@@ -16,16 +20,12 @@
+ #include <asm/mach-ralink/mt7621.h>
+ #include <asm/mips-boards/launch.h>
+ #include <asm/delay.h>
++#include <asm/time.h>
+ 
+ #include <pinmux.h>
+ 
+ #include "common.h"
+ 
+-#define SYSC_REG_SYSCFG		0x10
+-#define SYSC_REG_CPLL_CLKCFG0	0x2c
+-#define SYSC_REG_CUR_CLK_STS	0x44
+-#define CPU_CLK_SEL		(BIT(30) | BIT(31))
+-
+ #define MT7621_GPIO_MODE_UART1		1
+ #define MT7621_GPIO_MODE_I2C		2
+ #define MT7621_GPIO_MODE_UART3_MASK	0x3
+@@ -111,49 +111,89 @@ static struct rt2880_pmx_group mt7621_pi
+ 	{ 0 }
+ };
+ 
++static struct clk *clks[MT7621_CLK_MAX];
++static struct clk_onecell_data clk_data = {
++	.clks = clks,
++	.clk_num = ARRAY_SIZE(clks),
++};
++
+ phys_addr_t mips_cpc_default_phys_base(void)
+ {
+ 	panic("Cannot detect cpc address");
+ }
+ 
+-void __init ralink_clk_init(void)
++static struct clk *__init mt7621_add_sys_clkdev(
++	const char *id, unsigned long rate)
+ {
+-	int cpu_fdiv = 0;
+-	int cpu_ffrac = 0;
+-	int fbdiv = 0;
+-	u32 clk_sts, syscfg;
+-	u8 clk_sel = 0, xtal_mode;
+-	u32 cpu_clk;
++	struct clk *clk;
++	int err;
++
++	clk = clk_register_fixed_rate(NULL, id, NULL, 0, rate);
++	if (IS_ERR(clk))
++		panic("failed to allocate %s clock structure", id);
++
++	err = clk_register_clkdev(clk, id, NULL);
++	if (err)
++		panic("unable to register %s clock device", id);
+ 
+-	if ((rt_sysc_r32(SYSC_REG_CPLL_CLKCFG0) & CPU_CLK_SEL) != 0)
+-		clk_sel = 1;
++	return clk;
++}
++
++void __init ralink_clk_init(void)
++{
++	u32 syscfg, xtal_sel, clkcfg, clk_sel, curclk, ffiv, ffrac;
++	u32 pll, prediv, fbdiv;
++	u32 xtal_clk, cpu_clk, bus_clk;
++	const static u32 prediv_tbl[] = {0, 1, 2, 2};
++
++	syscfg = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0);
++	xtal_sel = (syscfg >> XTAL_MODE_SEL_SHIFT) & XTAL_MODE_SEL_MASK;
++
++	clkcfg = rt_sysc_r32(SYSC_REG_CLKCFG0);
++	clk_sel = (clkcfg >> CPU_CLK_SEL_SHIFT) & CPU_CLK_SEL_MASK;
++
++	curclk = rt_sysc_r32(SYSC_REG_CUR_CLK_STS);
++	ffiv = (curclk >> CUR_CPU_FDIV_SHIFT) & CUR_CPU_FDIV_MASK;
++	ffrac = (curclk >> CUR_CPU_FFRAC_SHIFT) & CUR_CPU_FFRAC_MASK;
++
++	if (xtal_sel <= 2)
++		xtal_clk = 20 * 1000 * 1000;
++	else if (xtal_sel <= 5)
++		xtal_clk = 40 * 1000 * 1000;
++	else
++		xtal_clk = 25 * 1000 * 1000;
+ 
+ 	switch (clk_sel) {
+ 	case 0:
+-		clk_sts = rt_sysc_r32(SYSC_REG_CUR_CLK_STS);
+-		cpu_fdiv = ((clk_sts >> 8) & 0x1F);
+-		cpu_ffrac = (clk_sts & 0x1F);
+-		cpu_clk = (500 * cpu_ffrac / cpu_fdiv) * 1000 * 1000;
++		cpu_clk = 500 * 1000 * 1000;
+ 		break;
+-
+ 	case 1:
+-		fbdiv = ((rt_sysc_r32(0x648) >> 4) & 0x7F) + 1;
+-		syscfg = rt_sysc_r32(SYSC_REG_SYSCFG);
+-		xtal_mode = (syscfg >> 6) & 0x7;
+-		if (xtal_mode >= 6) {
+-			/* 25Mhz Xtal */
+-			cpu_clk = 25 * fbdiv * 1000 * 1000;
+-		} else if (xtal_mode >= 3) {
+-			/* 40Mhz Xtal */
+-			cpu_clk = 40 * fbdiv * 1000 * 1000;
+-		} else {
+-			/* 20Mhz Xtal */
+-			cpu_clk = 20 * fbdiv * 1000 * 1000;
+-		}
++		pll = rt_memc_r32(MEMC_REG_CPU_PLL);
++		fbdiv = (pll >> CPU_PLL_FBDIV_SHIFT) & CPU_PLL_FBDIV_MASK;
++		prediv = (pll >> CPU_PLL_PREDIV_SHIFT) & CPU_PLL_PREDIV_MASK;
++		cpu_clk = ((fbdiv + 1) * xtal_clk) >> prediv_tbl[prediv];
+ 		break;
++	default:
++		cpu_clk = xtal_clk;
+ 	}
++
++	cpu_clk = cpu_clk / ffiv * ffrac;
++	bus_clk = cpu_clk / 4;
++
++	clks[MT7621_CLK_CPU] = mt7621_add_sys_clkdev("cpu", cpu_clk);
++	clks[MT7621_CLK_BUS] = mt7621_add_sys_clkdev("bus", bus_clk);
++
++	pr_info("CPU Clock: %dMHz\n", cpu_clk / 1000000);
++	mips_hpt_frequency = cpu_clk / 2;
+ }
+ 
++static void __init mt7621_clocks_init_dt(struct device_node *np)
++{
++	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
++}
++
++CLK_OF_DECLARE(ar7100, "mediatek,mt7621-pll", mt7621_clocks_init_dt);
++
+ void __init ralink_of_remap(void)
+ {
+ 	rt_sysc_membase = plat_of_remap_node("mtk,mt7621-sysc");
+--- a/arch/mips/ralink/timer-gic.c
++++ b/arch/mips/ralink/timer-gic.c
+@@ -9,14 +9,14 @@
+ 
+ #include <linux/of.h>
+ #include <linux/clk-provider.h>
+-#include <linux/clocksource.h>
++#include <asm/time.h>
+ 
+ #include "common.h"
+ 
+ void __init plat_time_init(void)
+ {
+ 	ralink_of_remap();
+-
++	ralink_clk_init();
+ 	of_clk_init(NULL);
+ 	timer_probe();
+ }
+--- /dev/null
++++ b/include/dt-bindings/clock/mt7621-clk.h
+@@ -0,0 +1,18 @@
++/*
++ * Copyright (C) 2018 Weijie Gao <hackpascal at gmail.com>
++ *
++ * 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.
++ *
++ */
++
++#ifndef __DT_BINDINGS_MT7621_CLK_H
++#define __DT_BINDINGS_MT7621_CLK_H
++
++#define MT7621_CLK_CPU		0
++#define MT7621_CLK_BUS		1
++
++#define MT7621_CLK_MAX		2
++
++#endif /* __DT_BINDINGS_MT7621_CLK_H */
diff --git a/target/linux/ramips/patches-5.10/105-mt7621-memory-detect.patch b/target/linux/ramips/patches-5.10/105-mt7621-memory-detect.patch
new file mode 100644
index 0000000000..08b4b0de29
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/105-mt7621-memory-detect.patch
@@ -0,0 +1,125 @@
+From b5a52351a66f3c2a7a207548aa87d78ff2d336c0 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213 at gmail.com>
+Date: Wed, 10 Jul 2019 00:24:48 +0800
+Subject: [PATCH] MIPS: ralink: mt7621: add memory detection support
+
+mt7621 has the following memory map:
+0x0-0x1c000000: lower 448m memory
+0x1c000000-0x2000000: peripheral registers
+0x20000000-0x2400000: higher 64m memory
+
+detect_memory_region in arch/mips/kernel/setup.c only add the first
+memory region and isn't suitable for 512m memory detection because
+it may accidentally read the memory area for peripheral registers.
+
+This commit adds memory detection capability for mt7621:
+1. add the highmem area when 512m is detected.
+2. guard memcmp from accessing peripheral registers:
+     This only happens when some weird user decided to change
+     kernel load address to 256m or higher address. Since this
+     is a quite unusual case, we just skip 512m testing and return
+     256m as memory size.
+
+Signed-off-by: Chuanhong Guo <gch981213 at gmail.com>
+---
+ arch/mips/include/asm/mach-ralink/mt7621.h |  7 ++---
+ arch/mips/ralink/mt7621.c                  | 30 +++++++++++++++++++---
+ 2 files changed, 30 insertions(+), 7 deletions(-)
+
+--- a/arch/mips/include/asm/mach-ralink/mt7621.h
++++ b/arch/mips/include/asm/mach-ralink/mt7621.h
+@@ -44,9 +44,10 @@
+ #define CPU_PLL_FBDIV_MASK		0x7f
+ #define CPU_PLL_FBDIV_SHIFT		4
+ 
+-#define MT7621_DRAM_BASE                0x0
+-#define MT7621_DDR2_SIZE_MIN		32
+-#define MT7621_DDR2_SIZE_MAX		256
++#define MT7621_LOWMEM_BASE		0x0
++#define MT7621_LOWMEM_MAX_SIZE		0x1C000000
++#define MT7621_HIGHMEM_BASE		0x20000000
++#define MT7621_HIGHMEM_SIZE		0x4000000
+ 
+ #define MT7621_CHIP_NAME0		0x3637544D
+ #define MT7621_CHIP_NAME1		0x20203132
+--- a/arch/mips/ralink/mt7621.c
++++ b/arch/mips/ralink/mt7621.c
+@@ -13,6 +13,7 @@
+ #include <linux/clk-provider.h>
+ #include <dt-bindings/clock/mt7621-clk.h>
+ 
++#include <asm/bootinfo.h>
+ #include <asm/mipsregs.h>
+ #include <asm/smp-ops.h>
+ #include <asm/mips-cps.h>
+@@ -55,6 +56,8 @@
+ #define MT7621_GPIO_MODE_SDHCI_SHIFT	18
+ #define MT7621_GPIO_MODE_SDHCI_GPIO	1
+ 
++static void *detect_magic __initdata = detect_memory_region;
++
+ static struct rt2880_pmx_func uart1_grp[] =  { FUNC("uart1", 0, 1, 2) };
+ static struct rt2880_pmx_func i2c_grp[] =  { FUNC("i2c", 0, 3, 2) };
+ static struct rt2880_pmx_func uart3_grp[] = {
+@@ -139,6 +142,28 @@ static struct clk *__init mt7621_add_sys
+ 	return clk;
+ }
+ 
++void __init mt7621_memory_detect(void)
++{
++	void *dm = &detect_magic;
++	phys_addr_t size;
++
++	for (size = 32 * SZ_1M; size < 256 * SZ_1M; size <<= 1) {
++		if (!memcmp(dm, dm + size, sizeof(detect_magic)))
++			break;
++	}
++
++	if ((size == 256 * SZ_1M) &&
++	    (CPHYSADDR(dm + size) < MT7621_LOWMEM_MAX_SIZE) &&
++	    memcmp(dm, dm + size, sizeof(detect_magic))) {
++		add_memory_region(MT7621_LOWMEM_BASE, MT7621_LOWMEM_MAX_SIZE,
++				  BOOT_MEM_RAM);
++		add_memory_region(MT7621_HIGHMEM_BASE, MT7621_HIGHMEM_SIZE,
++				  BOOT_MEM_RAM);
++	} else {
++		add_memory_region(MT7621_LOWMEM_BASE, size, BOOT_MEM_RAM);
++	}
++}
++
+ void __init ralink_clk_init(void)
+ {
+ 	u32 syscfg, xtal_sel, clkcfg, clk_sel, curclk, ffiv, ffrac;
+@@ -317,10 +342,7 @@ void prom_soc_init(struct ralink_soc_inf
+ 		(rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK,
+ 		(rev & CHIP_REV_ECO_MASK));
+ 
+-	soc_info->mem_size_min = MT7621_DDR2_SIZE_MIN;
+-	soc_info->mem_size_max = MT7621_DDR2_SIZE_MAX;
+-	soc_info->mem_base = MT7621_DRAM_BASE;
+-
++	soc_info->mem_detect = mt7621_memory_detect;
+ 	rt2880_pinmux_data = mt7621_pinmux_data;
+ 
+ 
+--- a/arch/mips/ralink/common.h
++++ b/arch/mips/ralink/common.h
+@@ -17,6 +17,7 @@ struct ralink_soc_info {
+ 	unsigned long mem_size;
+ 	unsigned long mem_size_min;
+ 	unsigned long mem_size_max;
++	void (*mem_detect)(void);
+ };
+ extern struct ralink_soc_info soc_info;
+ 
+--- a/arch/mips/ralink/of.c
++++ b/arch/mips/ralink/of.c
+@@ -87,6 +87,8 @@ void __init plat_mem_setup(void)
+ 	of_scan_flat_dt(early_init_dt_find_memory, NULL);
+ 	if (memory_dtb)
+ 		of_scan_flat_dt(early_init_dt_scan_memory, NULL);
++	else if (soc_info.mem_detect)
++		soc_info.mem_detect();
+ 	else if (soc_info.mem_size)
+ 		add_memory_region(soc_info.mem_base, soc_info.mem_size * SZ_1M,
+ 				  BOOT_MEM_RAM);
diff --git a/target/linux/ramips/patches-5.10/110-mt7621-perfctr-fix.patch b/target/linux/ramips/patches-5.10/110-mt7621-perfctr-fix.patch
new file mode 100644
index 0000000000..dfeac7eb99
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/110-mt7621-perfctr-fix.patch
@@ -0,0 +1,15 @@
+--- a/arch/mips/ralink/irq-gic.c
++++ b/arch/mips/ralink/irq-gic.c
+@@ -13,6 +13,12 @@
+ 
+ int get_c0_perfcount_int(void)
+ {
++	/*
++	 * Performance counter events are routed through GIC.
++	 * Prevent them from firing on CPU IRQ7 as well
++	 */
++	clear_c0_status(IE_SW0 << 7);
++
+ 	return gic_get_c0_perfcount_int();
+ }
+ EXPORT_SYMBOL_GPL(get_c0_perfcount_int);
diff --git a/target/linux/ramips/patches-5.10/111-gpio-mmio-introduce-BGPIOF_NO_SET_ON_INPUT.patch b/target/linux/ramips/patches-5.10/111-gpio-mmio-introduce-BGPIOF_NO_SET_ON_INPUT.patch
new file mode 100644
index 0000000000..fdb89d0902
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/111-gpio-mmio-introduce-BGPIOF_NO_SET_ON_INPUT.patch
@@ -0,0 +1,85 @@
+From 5d7b644aad721ecca20bd8976b38fb243fdc84f9 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213 at gmail.com>
+Date: Sun, 15 Mar 2020 20:13:37 +0800
+Subject: [PATCH] gpio: mmio: introduce BGPIOF_NO_SET_ON_INPUT
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some gpio controllers ignores pin value writing when that pin is
+configured as input mode. As a result, bgpio_dir_out should set
+pin to output before configuring pin values or gpio pin values
+can't be set up properly.
+Introduce two variants of bgpio_dir_out: bgpio_dir_out_val_first
+and bgpio_dir_out_dir_first, and assign direction_output according
+to a new flag: BGPIOF_NO_SET_ON_INPUT.
+
+Signed-off-by: Chuanhong Guo <gch981213 at gmail.com>
+Tested-by: René van Dorst <opensource at vdorst.com>
+Reviewed-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+---
+ drivers/gpio/gpio-mmio.c    | 23 +++++++++++++++++++----
+ include/linux/gpio/driver.h |  1 +
+ 2 files changed, 20 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpio/gpio-mmio.c
++++ b/drivers/gpio/gpio-mmio.c
+@@ -381,12 +381,10 @@ static int bgpio_get_dir(struct gpio_chi
+ 	return 1;
+ }
+ 
+-static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
++static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+ {
+ 	unsigned long flags;
+ 
+-	gc->set(gc, gpio, val);
+-
+ 	spin_lock_irqsave(&gc->bgpio_lock, flags);
+ 
+ 	gc->bgpio_dir |= bgpio_line2mask(gc, gpio);
+@@ -397,7 +395,21 @@ static int bgpio_dir_out(struct gpio_chi
+ 		gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
+ 
+ 	spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++}
+ 
++static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
++				   int val)
++{
++	bgpio_dir_out(gc, gpio, val);
++	gc->set(gc, gpio, val);
++	return 0;
++}
++
++static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
++				   int val)
++{
++	gc->set(gc, gpio, val);
++	bgpio_dir_out(gc, gpio, val);
+ 	return 0;
+ }
+ 
+@@ -530,7 +542,10 @@ static int bgpio_setup_direction(struct
+ 	if (dirout || dirin) {
+ 		gc->reg_dir_out = dirout;
+ 		gc->reg_dir_in = dirin;
+-		gc->direction_output = bgpio_dir_out;
++		if (flags & BGPIOF_NO_SET_ON_INPUT)
++			gc->direction_output = bgpio_dir_out_dir_first;
++		else
++			gc->direction_output = bgpio_dir_out_val_first;
+ 		gc->direction_input = bgpio_dir_in;
+ 		gc->get_direction = bgpio_get_dir;
+ 	} else {
+--- a/include/linux/gpio/driver.h
++++ b/include/linux/gpio/driver.h
+@@ -567,6 +567,7 @@ int bgpio_init(struct gpio_chip *gc, str
+ #define BGPIOF_BIG_ENDIAN_BYTE_ORDER	BIT(3)
+ #define BGPIOF_READ_OUTPUT_REG_SET	BIT(4) /* reg_set stores output value */
+ #define BGPIOF_NO_OUTPUT		BIT(5) /* only input */
++#define BGPIOF_NO_SET_ON_INPUT		BIT(6)
+ 
+ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
+ 		     irq_hw_number_t hwirq);
diff --git a/target/linux/ramips/patches-5.10/112-gpio-mt7621-add-BGPIOF_NO_SET_ON_INPUT-flag.patch b/target/linux/ramips/patches-5.10/112-gpio-mt7621-add-BGPIOF_NO_SET_ON_INPUT-flag.patch
new file mode 100644
index 0000000000..862f9adb47
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/112-gpio-mt7621-add-BGPIOF_NO_SET_ON_INPUT-flag.patch
@@ -0,0 +1,33 @@
+From ad65f02fd73e9a700f1693a4513ae923ca07beb0 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213 at gmail.com>
+Date: Sun, 15 Mar 2020 20:13:38 +0800
+Subject: [PATCH] gpio: mt7621: add BGPIOF_NO_SET_ON_INPUT flag
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+DSET/DCLR registers only works on output pins. Add corresponding
+BGPIOF_NO_SET_ON_INPUT flag to bgpio_init call to fix direction_out
+behavior.
+
+Signed-off-by: Chuanhong Guo <gch981213 at gmail.com>
+Tested-by: René van Dorst <opensource at vdorst.com>
+Reviewed-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+---
+ drivers/gpio/gpio-mt7621.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpio/gpio-mt7621.c
++++ b/drivers/gpio/gpio-mt7621.c
+@@ -227,8 +227,8 @@ mediatek_gpio_bank_probe(struct device *
+ 	ctrl = mtk->base + GPIO_REG_DCLR + (rg->bank * GPIO_BANK_STRIDE);
+ 	diro = mtk->base + GPIO_REG_CTRL + (rg->bank * GPIO_BANK_STRIDE);
+ 
+-	ret = bgpio_init(&rg->chip, dev, 4,
+-			 dat, set, ctrl, diro, NULL, 0);
++	ret = bgpio_init(&rg->chip, dev, 4, dat, set, ctrl, diro, NULL,
++			 BGPIOF_NO_SET_ON_INPUT);
+ 	if (ret) {
+ 		dev_err(dev, "bgpio_init() failed\n");
+ 		return ret;
diff --git a/target/linux/ramips/patches-5.10/200-add-ralink-eth.patch b/target/linux/ramips/patches-5.10/200-add-ralink-eth.patch
new file mode 100644
index 0000000000..b8fd8e511d
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/200-add-ralink-eth.patch
@@ -0,0 +1,20 @@
+--- a/drivers/net/ethernet/Kconfig
++++ b/drivers/net/ethernet/Kconfig
+@@ -159,6 +159,7 @@ source "drivers/net/ethernet/pasemi/Kcon
+ source "drivers/net/ethernet/pensando/Kconfig"
+ source "drivers/net/ethernet/qlogic/Kconfig"
+ source "drivers/net/ethernet/qualcomm/Kconfig"
++source "drivers/net/ethernet/ralink/Kconfig"
+ source "drivers/net/ethernet/rdc/Kconfig"
+ source "drivers/net/ethernet/realtek/Kconfig"
+ source "drivers/net/ethernet/renesas/Kconfig"
+--- a/drivers/net/ethernet/Makefile
++++ b/drivers/net/ethernet/Makefile
+@@ -72,6 +72,7 @@ obj-$(CONFIG_NET_VENDOR_PACKET_ENGINES)
+ obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/
+ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
+ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/
++obj-$(CONFIG_NET_VENDOR_RALINK) += ralink/
+ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
+ obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/
+ obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
diff --git a/target/linux/ramips/patches-5.10/300-mt7620-export-chip-version-and-pkg.patch b/target/linux/ramips/patches-5.10/300-mt7620-export-chip-version-and-pkg.patch
new file mode 100644
index 0000000000..8b4335eb03
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/300-mt7620-export-chip-version-and-pkg.patch
@@ -0,0 +1,19 @@
+--- a/arch/mips/include/asm/mach-ralink/mt7620.h
++++ b/arch/mips/include/asm/mach-ralink/mt7620.h
+@@ -135,4 +135,16 @@ static inline int mt7620_get_eco(void)
+ 	return rt_sysc_r32(SYSC_REG_CHIP_REV) & CHIP_REV_ECO_MASK;
+ }
+ 
++static inline int mt7620_get_chipver(void)
++{
++	return (rt_sysc_r32(SYSC_REG_CHIP_REV) >> CHIP_REV_VER_SHIFT) &
++		CHIP_REV_VER_MASK;
++}
++
++static inline int mt7620_get_pkg(void)
++{
++	return (rt_sysc_r32(SYSC_REG_CHIP_REV) >> CHIP_REV_PKG_SHIFT) &
++		CHIP_REV_PKG_MASK;
++}
++
+ #endif
diff --git a/target/linux/ramips/patches-5.10/301-MIPS-ralink-mt7621-introduce-soc_device-initializati.patch b/target/linux/ramips/patches-5.10/301-MIPS-ralink-mt7621-introduce-soc_device-initializati.patch
new file mode 100644
index 0000000000..dc3dd0d1ec
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/301-MIPS-ralink-mt7621-introduce-soc_device-initializati.patch
@@ -0,0 +1,89 @@
+From f798b7588bd7397bbab958281ca6c88d08714941 Mon Sep 17 00:00:00 2001
+From: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Date: Thu, 12 Mar 2020 12:29:15 +0100
+Subject: [PATCH] MIPS: ralink: mt7621: introduce 'soc_device' initialization
+
+mt7621 SoC has its own 'ralink_soc_info' structure with some
+information about the soc itself. Pcie controller and pcie phy
+drivers for this soc which are still in staging git tree make uses
+of 'soc_device_attribute' looking for revision 'E2' in order to
+know if reset lines are or not inverted. This way of doing things
+seems to be necessary in order to make things clean and properly.
+Hence, introduce this 'soc_device' to be able to properly use those
+attributes in drivers. Also set 'data' pointer points to the struct
+'ralink_soc_info' to be able to export also current soc information
+using this mechanism.
+
+Cc: Paul Burton <paul.burton at mips.com>
+Cc: ralf at linux-mips.org
+Cc: jhogan at kernel.org
+Cc: john at phrozen.org
+Cc: NeilBrown <neil at brown.name>
+Cc: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+Cc: linux-mips at vger.kernel.org
+
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos at gmail.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend at alpha.franken.de>
+---
+ arch/mips/ralink/mt7621.c | 32 +++++++++++++++++++++++++++++++-
+ 1 file changed, 31 insertions(+), 1 deletion(-)
+
+--- a/arch/mips/ralink/mt7621.c
++++ b/arch/mips/ralink/mt7621.c
+@@ -7,6 +7,8 @@
+ 
+ #include <linux/kernel.h>
+ #include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/sys_soc.h>
+ #include <linux/jiffies.h>
+ #include <linux/clk.h>
+ #include <linux/clkdev.h>
+@@ -294,6 +296,33 @@ static int udelay_recal(void)
+ }
+ device_initcall(udelay_recal);
+ 
++static void soc_dev_init(struct ralink_soc_info *soc_info, u32 rev)
++{
++	struct soc_device *soc_dev;
++	struct soc_device_attribute *soc_dev_attr;
++
++	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
++	if (!soc_dev_attr)
++		return;
++
++	soc_dev_attr->soc_id = "mt7621";
++	soc_dev_attr->family = "Ralink";
++
++	if (((rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK) == 1 &&
++	    (rev & CHIP_REV_ECO_MASK) == 1)
++		soc_dev_attr->revision = "E2";
++	else
++		soc_dev_attr->revision = "E1";
++
++	soc_dev_attr->data = soc_info;
++
++	soc_dev = soc_device_register(soc_dev_attr);
++	if (IS_ERR(soc_dev)) {
++		kfree(soc_dev_attr);
++		return;
++	}
++}
++
+ void prom_soc_init(struct ralink_soc_info *soc_info)
+ {
+ 	void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7621_SYSC_BASE);
+@@ -345,11 +374,12 @@ void prom_soc_init(struct ralink_soc_inf
+ 	soc_info->mem_detect = mt7621_memory_detect;
+ 	rt2880_pinmux_data = mt7621_pinmux_data;
+ 
+-
+ 	if (!register_cps_smp_ops())
+ 		return;
+ 	if (!register_cmp_smp_ops())
+ 		return;
+ 	if (!register_vsmp_smp_ops())
+ 		return;
++
++	soc_dev_init(soc_info, rev);
+ }
diff --git a/target/linux/ramips/patches-5.10/302-spi-nor-add-gd25q512.patch b/target/linux/ramips/patches-5.10/302-spi-nor-add-gd25q512.patch
new file mode 100644
index 0000000000..7fcf0c54b6
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/302-spi-nor-add-gd25q512.patch
@@ -0,0 +1,14 @@
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -2303,6 +2303,11 @@ static const struct flash_info spi_nor_i
+ 			SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ 			.fixups = &gd25q256_fixups,
+ 	},
++	{
++		"gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024,
++			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4B_OPCODES)
++	},
+ 
+ 	/* Intel/Numonyx -- xxxs33b */
+ 	{ "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) },
diff --git a/target/linux/ramips/patches-5.10/401-net-ethernet-mediatek-support-net-labels.patch b/target/linux/ramips/patches-5.10/401-net-ethernet-mediatek-support-net-labels.patch
new file mode 100644
index 0000000000..6583fca60b
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/401-net-ethernet-mediatek-support-net-labels.patch
@@ -0,0 +1,34 @@
+From bd0f89de5476ca25e73fae829ba3e1dafae1d90d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= <opensource at vdorst.com>
+Date: Fri, 21 Jun 2019 10:04:05 +0200
+Subject: [PATCH] net: ethernet: mediatek: support net-labels
+
+With this patch, device name can be set within dts file in the same way as dsa
+port can.
+Add: label = "wan"; to GMAC node.
+
+Signed-off-by: René van Dorst <opensource at vdorst.com>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2922,6 +2922,7 @@ static const struct net_device_ops mtk_n
+ 
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ {
++	const char *name = of_get_property(np, "label", NULL);
+ 	const __be32 *_id = of_get_property(np, "reg", NULL);
+ 	struct phylink *phylink;
+ 	int phy_mode, id, err;
+@@ -3014,6 +3015,9 @@ static int mtk_add_mac(struct mtk_eth *e
+ 
+ 	eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
+ 
++	if (name)
++		strlcpy(eth->netdev[id]->name, name, IFNAMSIZ);
++
+ 	return 0;
+ 
+ free_netdev:
diff --git a/target/linux/ramips/patches-5.10/990-NET-no-auto-carrier-off-support.patch b/target/linux/ramips/patches-5.10/990-NET-no-auto-carrier-off-support.patch
new file mode 100644
index 0000000000..a31d1ac3a6
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/990-NET-no-auto-carrier-off-support.patch
@@ -0,0 +1,47 @@
+From 0b6eb1e68290243d439ee330ea8d0b239a5aec69 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic at openwrt.org>
+Date: Sun, 27 Jul 2014 09:38:50 +0100
+Subject: [PATCH 34/53] NET: multi phy support
+
+Signed-off-by: John Crispin <blogic at openwrt.org>
+---
+ drivers/net/phy/phy.c |    9 ++++++---
+ include/linux/phy.h   |    1 +
+ 2 files changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -546,7 +546,10 @@ static int phy_check_link_status(struct
+ 		phy_link_up(phydev);
+ 	} else if (!phydev->link && phydev->state != PHY_NOLINK) {
+ 		phydev->state = PHY_NOLINK;
+-		phy_link_down(phydev, true);
++		if (!phydev->no_auto_carrier_off)
++			phy_link_down(phydev, true);
++		else
++			phy_link_down(phydev, false);
+ 	}
+ 
+ 	return 0;
+@@ -926,7 +929,10 @@ void phy_state_machine(struct work_struc
+ 	case PHY_HALTED:
+ 		if (phydev->link) {
+ 			phydev->link = 0;
+-			phy_link_down(phydev, true);
++			if (!phydev->no_auto_carrier_off)
++				phy_link_down(phydev, true);
++			else
++				phy_link_down(phydev, false);
+ 		}
+ 		do_suspend = true;
+ 		break;
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -380,6 +380,7 @@ struct phy_device {
+ 	unsigned suspended_by_mdio_bus:1;
+ 	unsigned sysfs_links:1;
+ 	unsigned loopback_enabled:1;
++	unsigned no_auto_carrier_off:1;
+ 
+ 	unsigned autoneg:1;
+ 	/* The most recently read link state */
diff --git a/target/linux/ramips/patches-5.10/991-at803x.patch b/target/linux/ramips/patches-5.10/991-at803x.patch
new file mode 100644
index 0000000000..af0132f8f7
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/991-at803x.patch
@@ -0,0 +1,156 @@
+From 924453aa9d2324e5611f8e2b71df746d8f0c79f1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= <opensource at vdorst.com>
+Date: Fri, 13 Nov 2020 16:11:32 +0100
+Subject: [PATCH] net: phy: at803x: add support for SFP module in
+ RGMII-to-x-base mode
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: René van Dorst <opensource at vdorst.com>
+---
+ drivers/net/phy/at803x.c | 91 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 91 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -14,6 +14,8 @@
+ #include <linux/etherdevice.h>
+ #include <linux/of_gpio.h>
+ #include <linux/gpio/consumer.h>
++#include <linux/sfp.h>
++#include <linux/phylink.h>
+ 
+ #define AT803X_SPECIFIC_STATUS			0x11
+ #define AT803X_SS_SPEED_MASK			(3 << 14)
+@@ -53,9 +55,18 @@
+ 
+ #define AT803X_MODE_CFG_MASK			0x0F
+ #define AT803X_MODE_CFG_SGMII			0x01
++#define AT803X_MODE_CFG_BX1000_RGMII_50		0x02
++#define AT803X_MODE_CFG_BX1000_RGMII_75		0x03
++#define AT803X_MODE_FIBER			0x01
++#define AT803X_MODE_COPPER			0x00
+ 
+ #define AT803X_PSSR			0x11	/*PHY-Specific Status Register*/
+ #define AT803X_PSSR_MR_AN_COMPLETE	0x0200
++#define	 PSSR_LINK			BIT(10)
++#define	 PSSR_SYNC_STATUS		BIT(8)
++#define	 PSSR_DUPLEX			BIT(13)
++#define	 PSSR_SPEED_1000		BIT(15)
++#define	 PSSR_SPEED_100			BIT(14)
+ 
+ #define AT803X_DEBUG_REG_0			0x00
+ #define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15)
+@@ -243,10 +254,72 @@ static int at803x_resume(struct phy_devi
+ 	return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
+ }
+ 
++static int at803x_mode(struct phy_device *phydev)
++{
++	int mode;
++
++	mode = phy_read(phydev, AT803X_REG_CHIP_CONFIG) & AT803X_MODE_CFG_MASK;
++
++	if (mode == AT803X_MODE_CFG_BX1000_RGMII_50 ||
++	    mode == AT803X_MODE_CFG_BX1000_RGMII_75)
++		return AT803X_MODE_FIBER;
++	return AT803X_MODE_COPPER;
++}
++
++static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(at803x_support) = { 0, };
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
++	struct phy_device *phydev = upstream;
++	phy_interface_t iface;
++
++	phylink_set(at803x_support, 1000baseX_Full);
++	/* AT803x only support 1000baseX but SGMII works fine when module runs
++	 * at 1Gbit.
++	 */
++	phylink_set(at803x_support, 1000baseT_Full);
++
++	sfp_parse_support(phydev->sfp_bus, id, support);
++
++	// Limit to interfaces that both sides support
++	linkmode_and(support, support, at803x_support);
++
++	if (linkmode_empty(support))
++		goto unsupported_mode;
++
++	iface = sfp_select_interface(phydev->sfp_bus, support);
++
++	if (iface != PHY_INTERFACE_MODE_SGMII &&
++	    iface != PHY_INTERFACE_MODE_1000BASEX)
++		goto unsupported_mode;
++
++	dev_info(&phydev->mdio.dev, "SFP interface %s", phy_modes(iface));
++
++	return 0;
++
++unsupported_mode:
++	dev_info(&phydev->mdio.dev, "incompatible SFP module inserted;"
++		 "Only SGMII at 1Gbit/1000BASEX are supported!\n");
++	return -EINVAL;
++}
++
++static const struct sfp_upstream_ops at803x_sfp_ops = {
++	.attach = phy_sfp_attach,
++	.detach = phy_sfp_detach,
++	.module_insert = at803x_sfp_insert,
++};
++
+ static int at803x_probe(struct phy_device *phydev)
+ {
+ 	struct device *dev = &phydev->mdio.dev;
+ 	struct at803x_priv *priv;
++	int ret;
++
++	if (at803x_mode(phydev) == AT803X_MODE_FIBER) {
++		ret = phy_sfp_probe(phydev, &at803x_sfp_ops);
++		if (ret < 0)
++			return ret;
++	}
+ 
+ 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ 	if (!priv)
+@@ -394,6 +467,10 @@ static int at803x_read_status(struct phy
+ {
+ 	int ss, err, old_link = phydev->link;
+ 
++	/* Handle (Fiber) SGMII to RGMII mode */
++	if (at803x_mode(phydev) == AT803X_MODE_FIBER)
++		return genphy_c37_read_status(phydev);
++
+ 	/* Update the link, but return if there was an error */
+ 	err = genphy_update_link(phydev);
+ 	if (err)
+@@ -448,6 +525,19 @@ static int at803x_read_status(struct phy
+ 	return 0;
+ }
+ 
++static int at803x_config_aneg(struct phy_device *phydev)
++{
++	/* Handle (Fiber) SerDes to RGMII mode */
++	if (at803x_mode(phydev) == AT803X_MODE_FIBER) {
++		pr_warn("%s: fiber\n", __func__);
++		return genphy_c37_config_aneg(phydev);
++	}
++
++	pr_warn("%s: enter\n", __func__);
++
++	return genphy_config_aneg(phydev);
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ 	/* ATHEROS 8035 */
+@@ -491,6 +581,7 @@ static struct phy_driver at803x_driver[]
+ 	.suspend		= at803x_suspend,
+ 	.resume			= at803x_resume,
+ 	/* PHY_GBIT_FEATURES */
++	.config_aneg		= at803x_config_aneg,
+ 	.read_status		= at803x_read_status,
+ 	.aneg_done		= at803x_aneg_done,
+ 	.ack_interrupt		= &at803x_ack_interrupt,
diff --git a/target/linux/ramips/patches-5.10/999-fix-pci-init-mt7620.patch b/target/linux/ramips/patches-5.10/999-fix-pci-init-mt7620.patch
new file mode 100644
index 0000000000..7c00d4c9ae
--- /dev/null
+++ b/target/linux/ramips/patches-5.10/999-fix-pci-init-mt7620.patch
@@ -0,0 +1,21 @@
+--- a/arch/mips/pci/pci-mt7620.c
++++ b/arch/mips/pci/pci-mt7620.c
+@@ -32,6 +32,7 @@
+ #define PPLL_CFG1			0x9c
+ 
+ #define PPLL_DRV			0xa0
++#define PPLL_LD			BIT(23)
+ #define PDRV_SW_SET			BIT(31)
+ #define LC_CKDRVPD			BIT(19)
+ #define LC_CKDRVOHZ			BIT(18)
+@@ -239,8 +240,8 @@ static int mt7620_pci_hw_init(struct pla
+ 	rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1);
+ 	mdelay(100);
+ 
+-	if (!(rt_sysc_r32(PPLL_CFG1) & PDRV_SW_SET)) {
+-		dev_err(&pdev->dev, "MT7620 PPLL unlock\n");
++	if (!(rt_sysc_r32(PPLL_CFG1) & PPLL_LD)) {
++		dev_err(&pdev->dev, "MT7620 PPLL is unlocked, aborting init\n");
+ 		reset_control_assert(rstpcie0);
+ 		rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1);
+ 		return -1;



More information about the lede-commits mailing list