aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm47xx/patches-4.19
diff options
context:
space:
mode:
authorAdrian Schmutzler <freifunk@adrianschmutzler.de>2020-02-07 15:42:54 +0100
committerAdrian Schmutzler <freifunk@adrianschmutzler.de>2020-02-14 14:10:51 +0100
commit8fe5ad5d3373cebed4fbc55a7b779721971ce427 (patch)
tree5d9106ede800508dde24c21dda7f90b8b1b8af5b /target/linux/bcm47xx/patches-4.19
parent8590e70fab64172700c21bc5e658eba17a1eae70 (diff)
downloadupstream-8fe5ad5d3373cebed4fbc55a7b779721971ce427.tar.gz
upstream-8fe5ad5d3373cebed4fbc55a7b779721971ce427.tar.bz2
upstream-8fe5ad5d3373cebed4fbc55a7b779721971ce427.zip
brcm47xx: rename target to bcm47xx
This change makes the names of Broadcom targets consistent by using the common notation based on SoC/CPU ID (which is used internally anyway), bcmXXXX instead of brcmXXXX. This is even used for target TITLE in make menuconfig already, only the short target name used brcm so far. Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de>
Diffstat (limited to 'target/linux/bcm47xx/patches-4.19')
-rw-r--r--target/linux/bcm47xx/patches-4.19/031-v5.1-mips-bcm47xx-Enable-USB-power-on-Netgear-WNDR3400v2.patch34
-rw-r--r--target/linux/bcm47xx/patches-4.19/159-cpu_fixes.patch510
-rw-r--r--target/linux/bcm47xx/patches-4.19/160-kmap_coherent.patch78
-rw-r--r--target/linux/bcm47xx/patches-4.19/209-b44-register-adm-switch.patch121
-rw-r--r--target/linux/bcm47xx/patches-4.19/210-b44_phy_fix.patch54
-rw-r--r--target/linux/bcm47xx/patches-4.19/280-activate_ssb_support_in_usb.patch25
-rw-r--r--target/linux/bcm47xx/patches-4.19/300-fork_cacheflush.patch21
-rw-r--r--target/linux/bcm47xx/patches-4.19/310-no_highpage.patch74
-rw-r--r--target/linux/bcm47xx/patches-4.19/320-MIPS-BCM47XX-Devices-database-update-for-4.x.patch185
-rw-r--r--target/linux/bcm47xx/patches-4.19/400-mtd-bcm47xxpart-get-nvram.patch34
-rw-r--r--target/linux/bcm47xx/patches-4.19/610-pci_ide_fix.patch41
-rw-r--r--target/linux/bcm47xx/patches-4.19/791-tg3-no-pci-sleep.patch17
-rw-r--r--target/linux/bcm47xx/patches-4.19/800-bcma-add-table-of-serial-flashes-with-smaller-blocks.patch73
-rw-r--r--target/linux/bcm47xx/patches-4.19/820-wgt634u-nvram-fix.patch304
-rw-r--r--target/linux/bcm47xx/patches-4.19/830-huawei_e970_support.patch101
-rw-r--r--target/linux/bcm47xx/patches-4.19/831-old_gpio_wdt.patch360
-rw-r--r--target/linux/bcm47xx/patches-4.19/900-ssb-reject-PCI-writes-setting-CardBus-bridge-resourc.patch30
-rw-r--r--target/linux/bcm47xx/patches-4.19/940-bcm47xx-yenta.patch46
-rw-r--r--target/linux/bcm47xx/patches-4.19/976-ssb_increase_pci_delay.patch11
-rw-r--r--target/linux/bcm47xx/patches-4.19/999-wl_exports.patch22
20 files changed, 2141 insertions, 0 deletions
diff --git a/target/linux/bcm47xx/patches-4.19/031-v5.1-mips-bcm47xx-Enable-USB-power-on-Netgear-WNDR3400v2.patch b/target/linux/bcm47xx/patches-4.19/031-v5.1-mips-bcm47xx-Enable-USB-power-on-Netgear-WNDR3400v2.patch
new file mode 100644
index 0000000000..34a79f9d5b
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/031-v5.1-mips-bcm47xx-Enable-USB-power-on-Netgear-WNDR3400v2.patch
@@ -0,0 +1,34 @@
+From cdb8faa00e3fcdd0ad10add743516d616dc7d38e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
+Date: Mon, 11 Mar 2019 22:08:22 +0100
+Subject: [PATCH] mips: bcm47xx: Enable USB power on Netgear WNDR3400v2
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Eric has reported on OpenWrt's bug tracking system[1], that he's not
+able to use USB devices on his WNDR3400v2 device after the boot, until
+he turns on GPIO #21 manually through sysfs.
+
+1. https://bugs.openwrt.org/index.php?do=details&task_id=2170
+
+Cc: Rafał Miłecki <zajec5@gmail.com>
+Cc: Hauke Mehrtens <hauke@hauke-m.de>
+Reported-by: Eric Bohlman <ericbohlman@gmail.com>
+Tested-by: Eric Bohlman <ericbohlman@gmail.com>
+Signed-off-by: Petr Štetiar <ynezz@true.cz>
+Signed-off-by: Paul Burton <paul.burton@mips.com>
+---
+ arch/mips/bcm47xx/workarounds.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/mips/bcm47xx/workarounds.c
++++ b/arch/mips/bcm47xx/workarounds.c
+@@ -24,6 +24,7 @@ void __init bcm47xx_workarounds(void)
+ case BCM47XX_BOARD_NETGEAR_WNR3500L:
+ bcm47xx_workarounds_enable_usb_power(12);
+ break;
++ case BCM47XX_BOARD_NETGEAR_WNDR3400V2:
+ case BCM47XX_BOARD_NETGEAR_WNDR3400_V3:
+ bcm47xx_workarounds_enable_usb_power(21);
+ break;
diff --git a/target/linux/bcm47xx/patches-4.19/159-cpu_fixes.patch b/target/linux/bcm47xx/patches-4.19/159-cpu_fixes.patch
new file mode 100644
index 0000000000..75e493b2dd
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/159-cpu_fixes.patch
@@ -0,0 +1,510 @@
+--- a/arch/mips/include/asm/r4kcache.h
++++ b/arch/mips/include/asm/r4kcache.h
+@@ -26,6 +26,38 @@
+ extern void (*r4k_blast_dcache)(void);
+ extern void (*r4k_blast_icache)(void);
+
++#if defined(CONFIG_BCM47XX) && !defined(CONFIG_CPU_MIPS32_R2)
++#include <asm/paccess.h>
++#include <linux/ssb/ssb.h>
++#define BCM4710_DUMMY_RREG() bcm4710_dummy_rreg()
++
++static inline unsigned long bcm4710_dummy_rreg(void)
++{
++ return *(volatile unsigned long *)(KSEG1ADDR(SSB_ENUM_BASE));
++}
++
++#define BCM4710_FILL_TLB(addr) bcm4710_fill_tlb((void *)(addr))
++
++static inline unsigned long bcm4710_fill_tlb(void *addr)
++{
++ return *(unsigned long *)addr;
++}
++
++#define BCM4710_PROTECTED_FILL_TLB(addr) bcm4710_protected_fill_tlb((void *)(addr))
++
++static inline void bcm4710_protected_fill_tlb(void *addr)
++{
++ unsigned long x;
++ get_dbe(x, (unsigned long *)addr);;
++}
++
++#else
++#define BCM4710_DUMMY_RREG()
++
++#define BCM4710_FILL_TLB(addr)
++#define BCM4710_PROTECTED_FILL_TLB(addr)
++#endif
++
+ /*
+ * This macro return a properly sign-extended address suitable as base address
+ * for indexed cache operations. Two issues here:
+@@ -99,6 +131,7 @@ static inline void flush_icache_line_ind
+ static inline void flush_dcache_line_indexed(unsigned long addr)
+ {
+ __dflush_prologue
++ BCM4710_DUMMY_RREG();
+ cache_op(Index_Writeback_Inv_D, addr);
+ __dflush_epilogue
+ }
+@@ -126,6 +159,7 @@ static inline void flush_icache_line(uns
+ static inline void flush_dcache_line(unsigned long addr)
+ {
+ __dflush_prologue
++ BCM4710_DUMMY_RREG();
+ cache_op(Hit_Writeback_Inv_D, addr);
+ __dflush_epilogue
+ }
+@@ -133,6 +167,7 @@ static inline void flush_dcache_line(uns
+ static inline void invalidate_dcache_line(unsigned long addr)
+ {
+ __dflush_prologue
++ BCM4710_DUMMY_RREG();
+ cache_op(Hit_Invalidate_D, addr);
+ __dflush_epilogue
+ }
+@@ -206,6 +241,7 @@ static inline int protected_flush_icache
+ #ifdef CONFIG_EVA
+ return protected_cachee_op(Hit_Invalidate_I, addr);
+ #else
++ BCM4710_DUMMY_RREG();
+ return protected_cache_op(Hit_Invalidate_I, addr);
+ #endif
+ }
+@@ -219,6 +255,7 @@ static inline int protected_flush_icache
+ */
+ static inline int protected_writeback_dcache_line(unsigned long addr)
+ {
++ BCM4710_DUMMY_RREG();
+ #ifdef CONFIG_EVA
+ return protected_cachee_op(Hit_Writeback_Inv_D, addr);
+ #else
+@@ -576,8 +613,51 @@ static inline void invalidate_tcache_pag
+ : "r" (base), \
+ "i" (op));
+
++static inline void blast_dcache(void)
++{
++ unsigned long start = KSEG0;
++ unsigned long dcache_size = current_cpu_data.dcache.waysize * current_cpu_data.dcache.ways;
++ unsigned long end = (start + dcache_size);
++
++ do {
++ BCM4710_DUMMY_RREG();
++ cache_op(Index_Writeback_Inv_D, start);
++ start += current_cpu_data.dcache.linesz;
++ } while(start < end);
++}
++
++static inline void blast_dcache_page(unsigned long page)
++{
++ unsigned long start = page;
++ unsigned long end = start + PAGE_SIZE;
++
++ BCM4710_FILL_TLB(start);
++ do {
++ BCM4710_DUMMY_RREG();
++ cache_op(Hit_Writeback_Inv_D, start);
++ start += current_cpu_data.dcache.linesz;
++ } while(start < end);
++}
++
++static inline void blast_dcache_page_indexed(unsigned long page)
++{
++ unsigned long start = page;
++ unsigned long end = start + PAGE_SIZE;
++ unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit;
++ unsigned long ws_end = current_cpu_data.dcache.ways <<
++ current_cpu_data.dcache.waybit;
++ unsigned long ws, addr;
++ for (ws = 0; ws < ws_end; ws += ws_inc) {
++ start = page + ws;
++ for (addr = start; addr < end; addr += current_cpu_data.dcache.linesz) {
++ BCM4710_DUMMY_RREG();
++ cache_op(Index_Writeback_Inv_D, addr);
++ }
++ }
++}
++
+ /* build blast_xxx, blast_xxx_page, blast_xxx_page_indexed */
+-#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize, extra) \
++#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize, extra, war) \
+ static inline void extra##blast_##pfx##cache##lsize(void) \
+ { \
+ unsigned long start = INDEX_BASE; \
+@@ -589,6 +669,7 @@ static inline void extra##blast_##pfx##c
+ \
+ __##pfx##flush_prologue \
+ \
++ war \
+ for (ws = 0; ws < ws_end; ws += ws_inc) \
+ for (addr = start; addr < end; addr += lsize * 32) \
+ cache##lsize##_unroll32(addr|ws, indexop); \
+@@ -603,6 +684,7 @@ static inline void extra##blast_##pfx##c
+ \
+ __##pfx##flush_prologue \
+ \
++ war \
+ do { \
+ cache##lsize##_unroll32(start, hitop); \
+ start += lsize * 32; \
+@@ -621,6 +703,8 @@ static inline void extra##blast_##pfx##c
+ current_cpu_data.desc.waybit; \
+ unsigned long ws, addr; \
+ \
++ war \
++ \
+ __##pfx##flush_prologue \
+ \
+ for (ws = 0; ws < ws_end; ws += ws_inc) \
+@@ -630,26 +714,26 @@ static inline void extra##blast_##pfx##c
+ __##pfx##flush_epilogue \
+ }
+
+-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, )
+-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, )
+-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I_Loongson2, 32, loongson2_)
+-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, )
+-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, )
+-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, )
+-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 128, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 128, )
+-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, )
+-
+-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, )
+-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, )
+-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, )
+-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, )
+-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, )
+-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, )
++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, , )
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, , BCM4710_FILL_TLB(start);)
++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, , )
++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, , )
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, , BCM4710_FILL_TLB(start);)
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I_Loongson2, 32, loongson2_, BCM4710_FILL_TLB(start);)
++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, , )
++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, , )
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, , BCM4710_FILL_TLB(start);)
++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, , )
++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 128, , )
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 128, , )
++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, , )
++
++__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, , )
++__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, , )
++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, , )
++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, , )
++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, , )
++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, , )
+
+ #define __BUILD_BLAST_USER_CACHE(pfx, desc, indexop, hitop, lsize) \
+ static inline void blast_##pfx##cache##lsize##_user_page(unsigned long page) \
+@@ -678,53 +762,23 @@ __BUILD_BLAST_USER_CACHE(d, dcache, Inde
+ __BUILD_BLAST_USER_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64)
+
+ /* build blast_xxx_range, protected_blast_xxx_range */
+-#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot, extra) \
++#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot, extra, war, war2) \
+ static inline void prot##extra##blast_##pfx##cache##_range(unsigned long start, \
+ unsigned long end) \
+ { \
+ unsigned long lsize = cpu_##desc##_line_size(); \
+- unsigned long lsize_2 = lsize * 2; \
+- unsigned long lsize_3 = lsize * 3; \
+- unsigned long lsize_4 = lsize * 4; \
+- unsigned long lsize_5 = lsize * 5; \
+- unsigned long lsize_6 = lsize * 6; \
+- unsigned long lsize_7 = lsize * 7; \
+- unsigned long lsize_8 = lsize * 8; \
+ unsigned long addr = start & ~(lsize - 1); \
+- unsigned long aend = (end + lsize - 1) & ~(lsize - 1); \
+- int lines = (aend - addr) / lsize; \
++ unsigned long aend = (end - 1) & ~(lsize - 1); \
++ war \
+ \
+ __##pfx##flush_prologue \
+ \
+- while (lines >= 8) { \
+- prot##cache_op(hitop, addr); \
+- prot##cache_op(hitop, addr + lsize); \
+- prot##cache_op(hitop, addr + lsize_2); \
+- prot##cache_op(hitop, addr + lsize_3); \
+- prot##cache_op(hitop, addr + lsize_4); \
+- prot##cache_op(hitop, addr + lsize_5); \
+- prot##cache_op(hitop, addr + lsize_6); \
+- prot##cache_op(hitop, addr + lsize_7); \
+- addr += lsize_8; \
+- lines -= 8; \
+- } \
+- \
+- if (lines & 0x4) { \
+- prot##cache_op(hitop, addr); \
+- prot##cache_op(hitop, addr + lsize); \
+- prot##cache_op(hitop, addr + lsize_2); \
+- prot##cache_op(hitop, addr + lsize_3); \
+- addr += lsize_4; \
+- } \
+- \
+- if (lines & 0x2) { \
+- prot##cache_op(hitop, addr); \
+- prot##cache_op(hitop, addr + lsize); \
+- addr += lsize_2; \
+- } \
+- \
+- if (lines & 0x1) { \
++ while (1) { \
++ war2 \
+ prot##cache_op(hitop, addr); \
++ if (addr == aend) \
++ break; \
++ addr += lsize; \
+ } \
+ \
+ __##pfx##flush_epilogue \
+@@ -732,8 +786,8 @@ static inline void prot##extra##blast_##
+
+ #ifndef CONFIG_EVA
+
+-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_, )
+-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_, )
++__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_, , BCM4710_PROTECTED_FILL_TLB(addr); BCM4710_PROTECTED_FILL_TLB(aend);, BCM4710_DUMMY_RREG();)
++__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_, , , )
+
+ #else
+
+@@ -770,15 +824,15 @@ __BUILD_PROT_BLAST_CACHE_RANGE(d, dcache
+ __BUILD_PROT_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I)
+
+ #endif
+-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_, )
++__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_, , , )
+ __BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I_Loongson2, \
+- protected_, loongson2_)
+-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, , )
+-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, , )
+-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, , )
++ protected_, loongson2_, , )
++__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, , , BCM4710_FILL_TLB(addr); BCM4710_FILL_TLB(aend);, BCM4710_DUMMY_RREG();)
++__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, , , , )
++__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, , , , )
+ /* blast_inv_dcache_range */
+-__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, , )
+-__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, , )
++__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, , , , BCM4710_DUMMY_RREG();)
++__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, , , , )
+
+ /* Currently, this is very specific to Loongson-3 */
+ #define __BUILD_BLAST_CACHE_NODE(pfx, desc, indexop, hitop, lsize) \
+--- a/arch/mips/include/asm/stackframe.h
++++ b/arch/mips/include/asm/stackframe.h
+@@ -428,6 +428,10 @@
+ eretnc
+ #else
+ .set arch=r4000
++#ifdef CONFIG_BCM47XX
++ nop
++ nop
++#endif
+ eret
+ .set mips0
+ #endif
+--- a/arch/mips/kernel/genex.S
++++ b/arch/mips/kernel/genex.S
+@@ -21,6 +21,19 @@
+ #include <asm/war.h>
+ #include <asm/thread_info.h>
+
++#ifdef CONFIG_BCM47XX
++# ifdef eret
++# undef eret
++# endif
++# define eret \
++ .set push; \
++ .set noreorder; \
++ nop; \
++ nop; \
++ eret; \
++ .set pop;
++#endif
++
+ __INIT
+
+ /*
+@@ -32,6 +45,9 @@
+ NESTED(except_vec3_generic, 0, sp)
+ .set push
+ .set noat
++#ifdef CONFIG_BCM47XX
++ nop
++#endif
+ #if R5432_CP0_INTERRUPT_WAR
+ mfc0 k0, CP0_INDEX
+ #endif
+@@ -55,6 +71,9 @@ NESTED(except_vec3_r4000, 0, sp)
+ .set push
+ .set arch=r4000
+ .set noat
++#ifdef CONFIG_BCM47XX
++ nop
++#endif
+ mfc0 k1, CP0_CAUSE
+ li k0, 31<<2
+ andi k1, k1, 0x7c
+--- a/arch/mips/mm/c-r4k.c
++++ b/arch/mips/mm/c-r4k.c
+@@ -39,6 +39,9 @@
+ #include <asm/dma-coherence.h>
+ #include <asm/mips-cps.h>
+
++/* For enabling BCM4710 cache workarounds */
++static int bcm4710 = 0;
++
+ /*
+ * Bits describing what cache ops an SMP callback function may perform.
+ *
+@@ -190,6 +193,9 @@ static void r4k_blast_dcache_user_page_s
+ {
+ unsigned long dc_lsize = cpu_dcache_line_size();
+
++ if (bcm4710)
++ r4k_blast_dcache_page = blast_dcache_page;
++ else
+ if (dc_lsize == 0)
+ r4k_blast_dcache_user_page = (void *)cache_noop;
+ else if (dc_lsize == 16)
+@@ -208,6 +214,9 @@ static void r4k_blast_dcache_page_indexe
+ {
+ unsigned long dc_lsize = cpu_dcache_line_size();
+
++ if (bcm4710)
++ r4k_blast_dcache_page_indexed = blast_dcache_page_indexed;
++ else
+ if (dc_lsize == 0)
+ r4k_blast_dcache_page_indexed = (void *)cache_noop;
+ else if (dc_lsize == 16)
+@@ -227,6 +236,9 @@ static void r4k_blast_dcache_setup(void)
+ {
+ unsigned long dc_lsize = cpu_dcache_line_size();
+
++ if (bcm4710)
++ r4k_blast_dcache = blast_dcache;
++ else
+ if (dc_lsize == 0)
+ r4k_blast_dcache = (void *)cache_noop;
+ else if (dc_lsize == 16)
+@@ -986,6 +998,8 @@ static void local_r4k_flush_cache_sigtra
+ }
+
+ R4600_HIT_CACHEOP_WAR_IMPL;
++ BCM4710_PROTECTED_FILL_TLB(addr);
++ BCM4710_PROTECTED_FILL_TLB(addr + 4);
+ if (!cpu_has_ic_fills_f_dc) {
+ if (dc_lsize)
+ vaddr ? flush_dcache_line(addr & ~(dc_lsize - 1))
+@@ -1888,6 +1902,17 @@ static void coherency_setup(void)
+ * silly idea of putting something else there ...
+ */
+ switch (current_cpu_type()) {
++ case CPU_BMIPS3300:
++ {
++ u32 cm;
++ cm = read_c0_diag();
++ /* Enable icache */
++ cm |= (1 << 31);
++ /* Enable dcache */
++ cm |= (1 << 30);
++ write_c0_diag(cm);
++ }
++ break;
+ case CPU_R4000PC:
+ case CPU_R4000SC:
+ case CPU_R4000MC:
+@@ -1934,6 +1959,15 @@ void r4k_cache_init(void)
+ extern void build_copy_page(void);
+ struct cpuinfo_mips *c = &current_cpu_data;
+
++ /* Check if special workarounds are required */
++#if defined(CONFIG_BCM47XX) && !defined(CONFIG_CPU_MIPS32_R2)
++ if (current_cpu_data.cputype == CPU_BMIPS32 && (current_cpu_data.processor_id & 0xff) == 0) {
++ printk("Enabling BCM4710A0 cache workarounds.\n");
++ bcm4710 = 1;
++ } else
++#endif
++ bcm4710 = 0;
++
+ probe_pcache();
+ probe_vcache();
+ setup_scache();
+@@ -2012,7 +2046,15 @@ void r4k_cache_init(void)
+ */
+ local_r4k___flush_cache_all(NULL);
+
++#ifdef CONFIG_BCM47XX
++ {
++ static void (*_coherency_setup)(void);
++ _coherency_setup = (void (*)(void)) KSEG1ADDR(coherency_setup);
++ _coherency_setup();
++ }
++#else
+ coherency_setup();
++#endif
+ board_cache_error_setup = r4k_cache_error_setup;
+
+ /*
+--- a/arch/mips/mm/tlbex.c
++++ b/arch/mips/mm/tlbex.c
+@@ -979,6 +979,9 @@ void build_get_pgde32(u32 **p, unsigned
+ uasm_i_srl(p, ptr, ptr, SMP_CPUID_PTRSHIFT);
+ uasm_i_addu(p, ptr, tmp, ptr);
+ #else
++#ifdef CONFIG_BCM47XX
++ uasm_i_nop(p);
++#endif
+ UASM_i_LA_mostly(p, ptr, pgdc);
+ #endif
+ uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */
+@@ -1340,6 +1343,9 @@ static void build_r4000_tlb_refill_handl
+ #ifdef CONFIG_64BIT
+ build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */
+ #else
++# ifdef CONFIG_BCM47XX
++ uasm_i_nop(&p);
++# endif
+ build_get_pgde32(&p, K0, K1); /* get pgd in K1 */
+ #endif
+
+@@ -1351,6 +1357,9 @@ static void build_r4000_tlb_refill_handl
+ build_update_entries(&p, K0, K1);
+ build_tlb_write_entry(&p, &l, &r, tlb_random);
+ uasm_l_leave(&l, p);
++#ifdef CONFIG_BCM47XX
++ uasm_i_nop(&p);
++#endif
+ uasm_i_eret(&p); /* return from trap */
+ }
+ #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
+@@ -2046,6 +2055,9 @@ build_r4000_tlbchange_handler_head(u32 *
+ #ifdef CONFIG_64BIT
+ build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */
+ #else
++# ifdef CONFIG_BCM47XX
++ uasm_i_nop(p);
++# endif
+ build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */
+ #endif
+
+@@ -2092,6 +2104,9 @@ build_r4000_tlbchange_handler_tail(u32 *
+ build_tlb_write_entry(p, l, r, tlb_indexed);
+ uasm_l_leave(l, *p);
+ build_restore_work_registers(p);
++#ifdef CONFIG_BCM47XX
++ uasm_i_nop(p);
++#endif
+ uasm_i_eret(p); /* return from trap */
+
+ #ifdef CONFIG_64BIT
diff --git a/target/linux/bcm47xx/patches-4.19/160-kmap_coherent.patch b/target/linux/bcm47xx/patches-4.19/160-kmap_coherent.patch
new file mode 100644
index 0000000000..db5660bc32
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/160-kmap_coherent.patch
@@ -0,0 +1,78 @@
+From: Jeff Hansen <jhansen@cardaccess-inc.com>
+Subject: [PATCH] kmap_coherent
+
+On ASUS WL-500gP there are some "Data bus error"s when executing simple
+commands liks "ps" or "cat /proc/1/cmdline".
+
+This fixes OpenWrt ticket #1485: https://dev.openwrt.org/ticket/1485
+---
+--- a/arch/mips/include/asm/cpu-features.h
++++ b/arch/mips/include/asm/cpu-features.h
+@@ -226,6 +226,9 @@
+ #ifndef cpu_has_local_ebase
+ #define cpu_has_local_ebase 1
+ #endif
++#ifndef cpu_use_kmap_coherent
++#define cpu_use_kmap_coherent 1
++#endif
+
+ /*
+ * I-Cache snoops remote store. This only matters on SMP. Some multiprocessors
+--- a/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h
++++ b/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h
+@@ -80,4 +80,6 @@
+ #define cpu_scache_line_size() 0
+ #define cpu_has_vz 0
+
++#define cpu_use_kmap_coherent 0
++
+ #endif /* __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H */
+--- a/arch/mips/mm/c-r4k.c
++++ b/arch/mips/mm/c-r4k.c
+@@ -694,7 +694,7 @@ static inline void local_r4k_flush_cache
+ map_coherent = (cpu_has_dc_aliases &&
+ page_mapcount(page) &&
+ !Page_dcache_dirty(page));
+- if (map_coherent)
++ if (map_coherent && cpu_use_kmap_coherent)
+ vaddr = kmap_coherent(page, addr);
+ else
+ vaddr = kmap_atomic(page);
+@@ -719,7 +719,7 @@ static inline void local_r4k_flush_cache
+ }
+
+ if (vaddr) {
+- if (map_coherent)
++ if (map_coherent && cpu_use_kmap_coherent)
+ kunmap_coherent();
+ else
+ kunmap_atomic(vaddr);
+--- a/arch/mips/mm/init.c
++++ b/arch/mips/mm/init.c
+@@ -168,7 +168,7 @@ void copy_user_highpage(struct page *to,
+ void *vfrom, *vto;
+
+ vto = kmap_atomic(to);
+- if (cpu_has_dc_aliases &&
++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent &&
+ page_mapcount(from) && !Page_dcache_dirty(from)) {
+ vfrom = kmap_coherent(from, vaddr);
+ copy_page(vto, vfrom);
+@@ -190,7 +190,7 @@ void copy_to_user_page(struct vm_area_st
+ struct page *page, unsigned long vaddr, void *dst, const void *src,
+ unsigned long len)
+ {
+- if (cpu_has_dc_aliases &&
++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent &&
+ page_mapcount(page) && !Page_dcache_dirty(page)) {
+ void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
+ memcpy(vto, src, len);
+@@ -208,7 +208,7 @@ void copy_from_user_page(struct vm_area_
+ struct page *page, unsigned long vaddr, void *dst, const void *src,
+ unsigned long len)
+ {
+- if (cpu_has_dc_aliases &&
++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent &&
+ page_mapcount(page) && !Page_dcache_dirty(page)) {
+ void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
+ memcpy(dst, vfrom, len);
diff --git a/target/linux/bcm47xx/patches-4.19/209-b44-register-adm-switch.patch b/target/linux/bcm47xx/patches-4.19/209-b44-register-adm-switch.patch
new file mode 100644
index 0000000000..bc2d2ab90a
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/209-b44-register-adm-switch.patch
@@ -0,0 +1,121 @@
+From b36f694256f41bc71571f467646d015dda128d14 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sat, 9 Nov 2013 17:03:59 +0100
+Subject: [PATCH 210/210] b44: register adm switch
+
+---
+ drivers/net/ethernet/broadcom/b44.c | 57 +++++++++++++++++++++++++++++++++++
+ drivers/net/ethernet/broadcom/b44.h | 3 ++
+ 2 files changed, 60 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/b44.c
++++ b/drivers/net/ethernet/broadcom/b44.c
+@@ -31,6 +31,8 @@
+ #include <linux/ssb/ssb.h>
+ #include <linux/slab.h>
+ #include <linux/phy.h>
++#include <linux/platform_device.h>
++#include <linux/platform_data/adm6996-gpio.h>
+
+ #include <linux/uaccess.h>
+ #include <asm/io.h>
+@@ -2249,6 +2251,69 @@ static void b44_adjust_link(struct net_d
+ }
+ }
+
++#ifdef CONFIG_BCM47XX
++static int b44_register_adm_switch(struct b44 *bp)
++{
++ int gpio;
++ struct platform_device *pdev;
++ struct adm6996_gpio_platform_data adm_data = {0};
++ struct platform_device_info info = {0};
++
++ adm_data.model = ADM6996L;
++ gpio = bcm47xx_nvram_gpio_pin("adm_eecs");
++ if (gpio >= 0)
++ adm_data.eecs = gpio;
++ else
++ adm_data.eecs = 2;
++
++ gpio = bcm47xx_nvram_gpio_pin("adm_eesk");
++ if (gpio >= 0)
++ adm_data.eesk = gpio;
++ else
++ adm_data.eesk = 3;
++
++ gpio = bcm47xx_nvram_gpio_pin("adm_eedi");
++ if (gpio >= 0)
++ adm_data.eedi = gpio;
++ else
++ adm_data.eedi = 4;
++
++ /*
++ * We ignore the "adm_rc" GPIO here. The driver does not use it,
++ * and it conflicts with the Reset button GPIO on the Linksys WRT54GSv1.
++ */
++
++ info.parent = bp->sdev->dev;
++ info.name = "adm6996_gpio";
++ info.id = -1;
++ info.data = &adm_data;
++ info.size_data = sizeof(adm_data);
++
++ if (!bp->adm_switch) {
++ pdev = platform_device_register_full(&info);
++ if (IS_ERR(pdev))
++ return PTR_ERR(pdev);
++
++ bp->adm_switch = pdev;
++ }
++ return 0;
++}
++static void b44_unregister_adm_switch(struct b44 *bp)
++{
++ if (bp->adm_switch)
++ platform_device_unregister(bp->adm_switch);
++}
++#else
++static int b44_register_adm_switch(struct b44 *bp)
++{
++ return 0;
++}
++static void b44_unregister_adm_switch(struct b44 *bp)
++{
++
++}
++#endif /* CONFIG_BCM47XX */
++
+ static int b44_register_phy_one(struct b44 *bp)
+ {
+ struct mii_bus *mii_bus;
+@@ -2284,6 +2349,9 @@ static int b44_register_phy_one(struct b
+ if (!mdiobus_is_registered_device(bp->mii_bus, bp->phy_addr) &&
+ (sprom->boardflags_lo & (B44_BOARDFLAG_ROBO | B44_BOARDFLAG_ADM))) {
+
++ if (sprom->boardflags_lo & B44_BOARDFLAG_ADM)
++ b44_register_adm_switch(bp);
++
+ dev_info(sdev->dev,
+ "could not find PHY at %i, use fixed one\n",
+ bp->phy_addr);
+@@ -2478,6 +2546,7 @@ static void b44_remove_one(struct ssb_de
+ unregister_netdev(dev);
+ if (bp->flags & B44_FLAG_EXTERNAL_PHY)
+ b44_unregister_phy_one(bp);
++ b44_unregister_adm_switch(bp);
+ ssb_device_disable(sdev, 0);
+ ssb_bus_may_powerdown(sdev->bus);
+ netif_napi_del(&bp->napi);
+--- a/drivers/net/ethernet/broadcom/b44.h
++++ b/drivers/net/ethernet/broadcom/b44.h
+@@ -408,6 +408,9 @@ struct b44 {
+ struct mii_bus *mii_bus;
+ int old_link;
+ struct mii_if_info mii_if;
++
++ /* platform device for associated switch */
++ struct platform_device *adm_switch;
+ };
+
+ #endif /* _B44_H */
diff --git a/target/linux/bcm47xx/patches-4.19/210-b44_phy_fix.patch b/target/linux/bcm47xx/patches-4.19/210-b44_phy_fix.patch
new file mode 100644
index 0000000000..8c7a73ac0f
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/210-b44_phy_fix.patch
@@ -0,0 +1,54 @@
+--- a/drivers/net/ethernet/broadcom/b44.c
++++ b/drivers/net/ethernet/broadcom/b44.c
+@@ -431,10 +431,34 @@ static void b44_wap54g10_workaround(stru
+ error:
+ pr_warn("PHY: cannot reset MII transceiver isolate bit\n");
+ }
++
++static void b44_bcm47xx_workarounds(struct b44 *bp)
++{
++ char buf[20];
++ struct ssb_device *sdev = bp->sdev;
++
++ /* Toshiba WRC-1000, Siemens SE505 v1, Askey RT-210W, RT-220W */
++ if (sdev->bus->sprom.board_num == 100) {
++ bp->phy_addr = B44_PHY_ADDR_NO_LOCAL_PHY;
++ } else {
++ /* WL-HDD */
++ if (bcm47xx_nvram_getenv("hardware_version", buf, sizeof(buf)) >= 0 &&
++ !strncmp(buf, "WL300-", strlen("WL300-"))) {
++ if (sdev->bus->sprom.et0phyaddr == 0 &&
++ sdev->bus->sprom.et1phyaddr == 1)
++ bp->phy_addr = B44_PHY_ADDR_NO_LOCAL_PHY;
++ }
++ }
++ return;
++}
+ #else
+ static inline void b44_wap54g10_workaround(struct b44 *bp)
+ {
+ }
++
++static inline void b44_bcm47xx_workarounds(struct b44 *bp)
++{
++}
+ #endif
+
+ static int b44_setup_phy(struct b44 *bp)
+@@ -443,6 +467,7 @@ static int b44_setup_phy(struct b44 *bp)
+ int err;
+
+ b44_wap54g10_workaround(bp);
++ b44_bcm47xx_workarounds(bp);
+
+ if (bp->flags & B44_FLAG_EXTERNAL_PHY)
+ return 0;
+@@ -2179,6 +2204,8 @@ static int b44_get_invariants(struct b44
+ * valid PHY address. */
+ bp->phy_addr &= 0x1F;
+
++ b44_bcm47xx_workarounds(bp);
++
+ memcpy(bp->dev->dev_addr, addr, ETH_ALEN);
+
+ if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){
diff --git a/target/linux/bcm47xx/patches-4.19/280-activate_ssb_support_in_usb.patch b/target/linux/bcm47xx/patches-4.19/280-activate_ssb_support_in_usb.patch
new file mode 100644
index 0000000000..a1c988bb59
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/280-activate_ssb_support_in_usb.patch
@@ -0,0 +1,25 @@
+This prevents the options from being delete with make kernel_oldconfig.
+---
+ drivers/ssb/Kconfig | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/bcma/Kconfig
++++ b/drivers/bcma/Kconfig
+@@ -32,6 +32,7 @@ config BCMA_HOST_PCI
+ config BCMA_HOST_SOC
+ bool "Support for BCMA in a SoC"
+ depends on HAS_IOMEM
++ select USB_HCD_BCMA if USB_EHCI_HCD || USB_OHCI_HCD
+ help
+ Host interface for a Broadcom AIX bus directly mapped into
+ the memory. This only works with the Broadcom SoCs from the
+--- a/drivers/ssb/Kconfig
++++ b/drivers/ssb/Kconfig
+@@ -135,6 +135,7 @@ config SSB_SFLASH
+ config SSB_EMBEDDED
+ bool
+ depends on SSB_DRIVER_MIPS && SSB_PCICORE_HOSTMODE
++ select USB_HCD_SSB if USB_EHCI_HCD || USB_OHCI_HCD
+ default y
+
+ config SSB_DRIVER_EXTIF
diff --git a/target/linux/bcm47xx/patches-4.19/300-fork_cacheflush.patch b/target/linux/bcm47xx/patches-4.19/300-fork_cacheflush.patch
new file mode 100644
index 0000000000..b5efaaf03c
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/300-fork_cacheflush.patch
@@ -0,0 +1,21 @@
+From: Wolfram Joost <dbox2@frokaschwei.de>
+Subject: [PATCH] fork_cacheflush
+
+On ASUS WL-500gP there are many unexpected "Segmentation fault"s that
+seem to be caused by a kernel. They can be avoided by:
+1) Disabling highpage
+2) Using flush_cache_mm in flush_cache_dup_mm
+
+For details see OpenWrt ticket #2035 https://dev.openwrt.org/ticket/2035
+---
+--- a/arch/mips/include/asm/cacheflush.h
++++ b/arch/mips/include/asm/cacheflush.h
+@@ -47,7 +47,7 @@
+ extern void (*flush_cache_all)(void);
+ extern void (*__flush_cache_all)(void);
+ extern void (*flush_cache_mm)(struct mm_struct *mm);
+-#define flush_cache_dup_mm(mm) do { (void) (mm); } while (0)
++#define flush_cache_dup_mm(mm) flush_cache_mm(mm)
+ extern void (*flush_cache_range)(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
+ extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn);
diff --git a/target/linux/bcm47xx/patches-4.19/310-no_highpage.patch b/target/linux/bcm47xx/patches-4.19/310-no_highpage.patch
new file mode 100644
index 0000000000..09bfc64552
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/310-no_highpage.patch
@@ -0,0 +1,74 @@
+From: Jeff Hansen <jhansen@cardaccess-inc.com>
+Subject: [PATCH] no highpage
+
+On ASUS WL-500gP there are many unexpected "Segmentation fault"s that
+seem to be caused by a kernel. They can be avoided by:
+1) Disabling highpage
+2) Using flush_cache_mm in flush_cache_dup_mm
+
+For details see OpenWrt ticket #2035 https://dev.openwrt.org/ticket/2035
+---
+--- a/arch/mips/include/asm/page.h
++++ b/arch/mips/include/asm/page.h
+@@ -71,6 +71,7 @@ static inline unsigned int page_size_ftl
+ #endif /* CONFIG_MIPS_HUGE_TLB_SUPPORT */
+
+ #include <linux/pfn.h>
++#include <asm/cpu-features.h>
+
+ extern void build_clear_page(void);
+ extern void build_copy_page(void);
+@@ -110,11 +111,16 @@ static inline void clear_user_page(void
+ flush_data_cache_page((unsigned long)addr);
+ }
+
+-struct vm_area_struct;
+-extern void copy_user_highpage(struct page *to, struct page *from,
+- unsigned long vaddr, struct vm_area_struct *vma);
++static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
++ struct page *to)
++{
++ extern void (*flush_data_cache_page)(unsigned long addr);
+
+-#define __HAVE_ARCH_COPY_USER_HIGHPAGE
++ copy_page(vto, vfrom);
++ if (!cpu_has_ic_fills_f_dc ||
++ pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
++ flush_data_cache_page((unsigned long)vto);
++}
+
+ /*
+ * These are used to make use of C type-checking..
+--- a/arch/mips/mm/init.c
++++ b/arch/mips/mm/init.c
+@@ -162,30 +162,6 @@ void kunmap_coherent(void)
+ preempt_enable();
+ }
+
+-void copy_user_highpage(struct page *to, struct page *from,
+- unsigned long vaddr, struct vm_area_struct *vma)
+-{
+- void *vfrom, *vto;
+-
+- vto = kmap_atomic(to);
+- if (cpu_has_dc_aliases && cpu_use_kmap_coherent &&
+- page_mapcount(from) && !Page_dcache_dirty(from)) {
+- vfrom = kmap_coherent(from, vaddr);
+- copy_page(vto, vfrom);
+- kunmap_coherent();
+- } else {
+- vfrom = kmap_atomic(from);
+- copy_page(vto, vfrom);
+- kunmap_atomic(vfrom);
+- }
+- if ((!cpu_has_ic_fills_f_dc) ||
+- pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
+- flush_data_cache_page((unsigned long)vto);
+- kunmap_atomic(vto);
+- /* Make sure this page is cleared on other CPU's too before using it */
+- smp_wmb();
+-}
+-
+ void copy_to_user_page(struct vm_area_struct *vma,
+ struct page *page, unsigned long vaddr, void *dst, const void *src,
+ unsigned long len)
diff --git a/target/linux/bcm47xx/patches-4.19/320-MIPS-BCM47XX-Devices-database-update-for-4.x.patch b/target/linux/bcm47xx/patches-4.19/320-MIPS-BCM47XX-Devices-database-update-for-4.x.patch
new file mode 100644
index 0000000000..74060e2181
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/320-MIPS-BCM47XX-Devices-database-update-for-4.x.patch
@@ -0,0 +1,185 @@
+--- a/arch/mips/bcm47xx/board.c
++++ b/arch/mips/bcm47xx/board.c
+@@ -141,6 +141,7 @@ struct bcm47xx_board_type_list2 bcm47xx_
+ {{BCM47XX_BOARD_LINKSYS_WRT300NV11, "Linksys WRT300N V1.1"}, "WRT300N", "1.1"},
+ {{BCM47XX_BOARD_LINKSYS_WRT310NV1, "Linksys WRT310N V1"}, "WRT310N", "1.0"},
+ {{BCM47XX_BOARD_LINKSYS_WRT310NV2, "Linksys WRT310N V2"}, "WRT310N", "2.0"},
++ {{BCM47XX_BOARD_LINKSYS_WRT320N_V1, "Linksys WRT320N V1"}, "WRT320N", "1.0"},
+ {{BCM47XX_BOARD_LINKSYS_WRT54G3GV2, "Linksys WRT54G3GV2-VF"}, "WRT54G3GV2-VF", "1.0"},
+ {{BCM47XX_BOARD_LINKSYS_WRT610NV1, "Linksys WRT610N V1"}, "WRT610N", "1.0"},
+ {{BCM47XX_BOARD_LINKSYS_WRT610NV2, "Linksys WRT610N V2"}, "WRT610N", "2.0"},
+@@ -160,9 +161,12 @@ struct bcm47xx_board_type_list1 bcm47xx_
+ {{BCM47XX_BOARD_LUXUL_XVW_P30_V1, "Luxul XVW-P30 V1"}, "luxul_xvwp30_v1"},
+ {{BCM47XX_BOARD_LUXUL_XWR_600_V1, "Luxul XWR-600 V1"}, "luxul_xwr600_v1"},
+ {{BCM47XX_BOARD_LUXUL_XWR_1750_V1, "Luxul XWR-1750 V1"}, "luxul_xwr1750_v1"},
++ {{BCM47XX_BOARD_NETGEAR_R6300_V1, "Netgear R6300 V1"}, "U12H218T00_NETGEAR"},
+ {{BCM47XX_BOARD_NETGEAR_WGR614V8, "Netgear WGR614 V8"}, "U12H072T00_NETGEAR"},
+ {{BCM47XX_BOARD_NETGEAR_WGR614V9, "Netgear WGR614 V9"}, "U12H094T00_NETGEAR"},
+ {{BCM47XX_BOARD_NETGEAR_WGR614_V10, "Netgear WGR614 V10"}, "U12H139T01_NETGEAR"},
++ {{BCM47XX_BOARD_NETGEAR_WN2500RP_V1, "Netgear WN2500RP V1"}, "U12H197T00_NETGEAR"},
++ {{BCM47XX_BOARD_NETGEAR_WN2500RP_V2, "Netgear WN2500RP V2"}, "U12H294T00_NETGEAR"},
+ {{BCM47XX_BOARD_NETGEAR_WNDR3300, "Netgear WNDR3300"}, "U12H093T00_NETGEAR"},
+ {{BCM47XX_BOARD_NETGEAR_WNDR3400V1, "Netgear WNDR3400 V1"}, "U12H155T00_NETGEAR"},
+ {{BCM47XX_BOARD_NETGEAR_WNDR3400V2, "Netgear WNDR3400 V2"}, "U12H187T00_NETGEAR"},
+--- a/arch/mips/bcm47xx/buttons.c
++++ b/arch/mips/bcm47xx/buttons.c
+@@ -27,6 +27,12 @@
+ /* Asus */
+
+ static const struct gpio_keys_button
++bcm47xx_buttons_asus_rtn10u[] __initconst = {
++ BCM47XX_GPIO_KEY(20, KEY_WPS_BUTTON),
++ BCM47XX_GPIO_KEY(21, KEY_RESTART),
++};
++
++static const struct gpio_keys_button
+ bcm47xx_buttons_asus_rtn12[] __initconst = {
+ BCM47XX_GPIO_KEY(0, KEY_WPS_BUTTON),
+ BCM47XX_GPIO_KEY(1, KEY_RESTART),
+@@ -277,6 +283,18 @@ bcm47xx_buttons_linksys_wrt310nv1[] __in
+ };
+
+ static const struct gpio_keys_button
++bcm47xx_buttons_linksys_wrt310n_v2[] __initconst = {
++ BCM47XX_GPIO_KEY(5, KEY_WPS_BUTTON),
++ BCM47XX_GPIO_KEY(6, KEY_RESTART),
++};
++
++static const struct gpio_keys_button
++bcm47xx_buttons_linksys_wrt320n_v1[] __initconst = {
++ BCM47XX_GPIO_KEY(5, KEY_WPS_BUTTON),
++ BCM47XX_GPIO_KEY(8, KEY_RESTART),
++};
++
++static const struct gpio_keys_button
+ bcm47xx_buttons_linksys_wrt54g3gv2[] __initconst = {
+ BCM47XX_GPIO_KEY(5, KEY_WIMAX),
+ BCM47XX_GPIO_KEY(6, KEY_RESTART),
+@@ -385,6 +403,17 @@ bcm47xx_buttons_motorola_wr850gv2v3[] __
+ /* Netgear */
+
+ static const struct gpio_keys_button
++bcm47xx_buttons_netgear_r6300_v1[] __initconst = {
++ BCM47XX_GPIO_KEY(6, KEY_RESTART),
++};
++
++static const struct gpio_keys_button
++bcm47xx_buttons_netgear_wn2500rp_v1[] __initconst = {
++ BCM47XX_GPIO_KEY(12, KEY_RESTART),
++ BCM47XX_GPIO_KEY(31, KEY_WPS_BUTTON),
++};
++
++static const struct gpio_keys_button
+ bcm47xx_buttons_netgear_wndr3400v1[] __initconst = {
+ BCM47XX_GPIO_KEY(4, KEY_RESTART),
+ BCM47XX_GPIO_KEY(6, KEY_WPS_BUTTON),
+@@ -471,6 +500,9 @@ int __init bcm47xx_buttons_register(void
+ int err;
+
+ switch (board) {
++ case BCM47XX_BOARD_ASUS_RTN10U:
++ err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_rtn10u);
++ break;
+ case BCM47XX_BOARD_ASUS_RTN12:
+ err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_rtn12);
+ break;
+@@ -601,6 +633,12 @@ int __init bcm47xx_buttons_register(void
+ case BCM47XX_BOARD_LINKSYS_WRT310NV1:
+ err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt310nv1);
+ break;
++ case BCM47XX_BOARD_LINKSYS_WRT310NV2:
++ err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt310n_v2);
++ break;
++ case BCM47XX_BOARD_LINKSYS_WRT320N_V1:
++ err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt320n_v1);
++ break;
+ case BCM47XX_BOARD_LINKSYS_WRT54G3GV2:
+ err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt54g3gv2);
+ break;
+@@ -664,6 +702,12 @@ int __init bcm47xx_buttons_register(void
+ err = bcm47xx_copy_bdata(bcm47xx_buttons_motorola_wr850gv2v3);
+ break;
+
++ case BCM47XX_BOARD_NETGEAR_R6300_V1:
++ err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_r6300_v1);
++ break;
++ case BCM47XX_BOARD_NETGEAR_WN2500RP_V1:
++ err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_wn2500rp_v1);
++ break;
+ case BCM47XX_BOARD_NETGEAR_WNDR3400V1:
+ err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_wndr3400v1);
+ break;
+--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx_board.h
++++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx_board.h
+@@ -72,6 +72,7 @@ enum bcm47xx_board {
+ BCM47XX_BOARD_LINKSYS_WRT300NV11,
+ BCM47XX_BOARD_LINKSYS_WRT310NV1,
+ BCM47XX_BOARD_LINKSYS_WRT310NV2,
++ BCM47XX_BOARD_LINKSYS_WRT320N_V1,
+ BCM47XX_BOARD_LINKSYS_WRT54G3GV2,
+ BCM47XX_BOARD_LINKSYS_WRT54G_TYPE_0101,
+ BCM47XX_BOARD_LINKSYS_WRT54G_TYPE_0467,
+@@ -98,9 +99,12 @@ enum bcm47xx_board {
+ BCM47XX_BOARD_MOTOROLA_WR850GP,
+ BCM47XX_BOARD_MOTOROLA_WR850GV2V3,
+
++ BCM47XX_BOARD_NETGEAR_R6300_V1,
+ BCM47XX_BOARD_NETGEAR_WGR614V8,
+ BCM47XX_BOARD_NETGEAR_WGR614V9,
+ BCM47XX_BOARD_NETGEAR_WGR614_V10,
++ BCM47XX_BOARD_NETGEAR_WN2500RP_V1,
++ BCM47XX_BOARD_NETGEAR_WN2500RP_V2,
+ BCM47XX_BOARD_NETGEAR_WNDR3300,
+ BCM47XX_BOARD_NETGEAR_WNDR3400V1,
+ BCM47XX_BOARD_NETGEAR_WNDR3400V2,
+--- a/arch/mips/bcm47xx/leds.c
++++ b/arch/mips/bcm47xx/leds.c
+@@ -30,6 +30,14 @@
+ /* Asus */
+
+ static const struct gpio_led
++bcm47xx_leds_asus_rtn10u[] __initconst = {
++ BCM47XX_GPIO_LED(5, "green", "wlan", 0, LEDS_GPIO_DEFSTATE_OFF),
++ BCM47XX_GPIO_LED(6, "green", "power", 1, LEDS_GPIO_DEFSTATE_ON),
++ BCM47XX_GPIO_LED(7, "green", "wps", 0, LEDS_GPIO_DEFSTATE_OFF),
++ BCM47XX_GPIO_LED(8, "green", "usb", 0, LEDS_GPIO_DEFSTATE_OFF),
++};
++
++static const struct gpio_led
+ bcm47xx_leds_asus_rtn12[] __initconst = {
+ BCM47XX_GPIO_LED(2, "unk", "power", 1, LEDS_GPIO_DEFSTATE_ON),
+ BCM47XX_GPIO_LED(7, "unk", "wlan", 0, LEDS_GPIO_DEFSTATE_OFF),
+@@ -314,6 +322,13 @@ bcm47xx_leds_linksys_wrt310nv1[] __initc
+ };
+
+ static const struct gpio_led
++bcm47xx_leds_linksys_wrt320n_v1[] __initconst = {
++ BCM47XX_GPIO_LED(1, "blue", "wlan", 1, LEDS_GPIO_DEFSTATE_OFF),
++ BCM47XX_GPIO_LED(2, "blue", "power", 0, LEDS_GPIO_DEFSTATE_ON),
++ BCM47XX_GPIO_LED(4, "amber", "wps", 1, LEDS_GPIO_DEFSTATE_OFF),
++};
++
++static const struct gpio_led
+ bcm47xx_leds_linksys_wrt54g_generic[] __initconst = {
+ BCM47XX_GPIO_LED(0, "unk", "dmz", 1, LEDS_GPIO_DEFSTATE_OFF),
+ BCM47XX_GPIO_LED(1, "unk", "power", 0, LEDS_GPIO_DEFSTATE_ON),
+@@ -556,6 +571,9 @@ void __init bcm47xx_leds_register(void)
+ enum bcm47xx_board board = bcm47xx_board_get();
+
+ switch (board) {
++ case BCM47XX_BOARD_ASUS_RTN10U:
++ bcm47xx_set_pdata(bcm47xx_leds_asus_rtn10u);
++ break;
+ case BCM47XX_BOARD_ASUS_RTN12:
+ bcm47xx_set_pdata(bcm47xx_leds_asus_rtn12);
+ break;
+@@ -689,6 +707,9 @@ void __init bcm47xx_leds_register(void)
+ case BCM47XX_BOARD_LINKSYS_WRT310NV1:
+ bcm47xx_set_pdata(bcm47xx_leds_linksys_wrt310nv1);
+ break;
++ case BCM47XX_BOARD_LINKSYS_WRT320N_V1:
++ bcm47xx_set_pdata(bcm47xx_leds_linksys_wrt320n_v1);
++ break;
+ case BCM47XX_BOARD_LINKSYS_WRT54G3GV2:
+ bcm47xx_set_pdata(bcm47xx_leds_linksys_wrt54g3gv2);
+ break;
diff --git a/target/linux/bcm47xx/patches-4.19/400-mtd-bcm47xxpart-get-nvram.patch b/target/linux/bcm47xx/patches-4.19/400-mtd-bcm47xxpart-get-nvram.patch
new file mode 100644
index 0000000000..df9d434c9f
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/400-mtd-bcm47xxpart-get-nvram.patch
@@ -0,0 +1,34 @@
+--- a/drivers/mtd/bcm47xxpart.c
++++ b/drivers/mtd/bcm47xxpart.c
+@@ -102,6 +102,7 @@ static int bcm47xxpart_parse(struct mtd_
+ int trx_num = 0; /* Number of found TRX partitions */
+ int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
+ int err;
++ bool found_nvram = false;
+
+ /*
+ * Some really old flashes (like AT45DB*) had smaller erasesize-s, but
+@@ -283,12 +284,23 @@ static int bcm47xxpart_parse(struct mtd_
+ if (buf[0] == NVRAM_HEADER) {
+ bcm47xxpart_add_part(&parts[curr_part++], "nvram",
+ master->size - blocksize, 0);
++ found_nvram = true;
+ break;
+ }
+ }
+
+ kfree(buf);
+
++ if (!found_nvram) {
++ pr_err("can not find a nvram partition reserve last block\n");
++ bcm47xxpart_add_part(&parts[curr_part++], "nvram_guess",
++ master->size - blocksize * 2, MTD_WRITEABLE);
++ for (i = 0; i < curr_part; i++) {
++ if (parts[i].size + parts[i].offset == master->size)
++ parts[i].offset -= blocksize * 2;
++ }
++ }
++
+ /*
+ * Assume that partitions end at the beginning of the one they are
+ * followed by.
diff --git a/target/linux/bcm47xx/patches-4.19/610-pci_ide_fix.patch b/target/linux/bcm47xx/patches-4.19/610-pci_ide_fix.patch
new file mode 100644
index 0000000000..a353eec08c
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/610-pci_ide_fix.patch
@@ -0,0 +1,41 @@
+From: b.sander
+Subject: [PATCH] pci: IDE fix
+
+These are standard probing messages when using pdc202xx_old:
+pdc202xx_old 0000:00:01.0: IDE controller (0x105a:0x0d30 rev 0x02)
+PCI: Enabling device 0000:00:01.0 (0004 -> 0007)
+PCI: Fixing up device 0000:00:01.0
+0000:00:01.0: (U)DMA Burst Bit DISABLED Primary PCI Mode Secondary PCI Mode.
+0000:00:01.0: FORCING BURST BIT 0x00->0x01 ACTIVE
+pdc202xx_old 0000:00:01.0: 100% native mode on irq 6
+
+With the default MAX_HWIFS value after above we get:
+ ide2: BM-DMA at 0x0400-0x0407
+ ide3: BM-DMA at 0x0408-0x040f
+Probing IDE interface ide2...
+hde: CF500, CFA DISK drive
+
+As you can see it's ide2 + ide3 and hde.
+
+With this patch applied we get:
+ ide0: BM-DMA at 0x0400-0x0407
+ ide1: BM-DMA at 0x0408-0x040f
+Probing IDE interface ide0...
+hda: CF500, CFA DISK drive
+
+This fixes OpenWrt ticket #7061: https://dev.openwrt.org/ticket/7061
+---
+--- a/include/linux/ide.h
++++ b/include/linux/ide.h
+@@ -235,7 +235,11 @@ static inline void ide_std_init_ports(st
+ hw->io_ports.ctl_addr = ctl_addr;
+ }
+
++#if defined CONFIG_BCM47XX
++# define MAX_HWIFS 2
++#else
+ #define MAX_HWIFS 10
++#endif
+
+ /*
+ * Now for the data we need to maintain per-drive: ide_drive_t
diff --git a/target/linux/bcm47xx/patches-4.19/791-tg3-no-pci-sleep.patch b/target/linux/bcm47xx/patches-4.19/791-tg3-no-pci-sleep.patch
new file mode 100644
index 0000000000..31c87b38c5
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/791-tg3-no-pci-sleep.patch
@@ -0,0 +1,17 @@
+When the Ethernet controller is powered down and someone wants to
+access the mdio bus like the witch driver (b53) the system crashed if
+PCI_D3hot was set before. This patch deactivates this power sawing mode
+when a switch driver is in use.
+
+--- a/drivers/net/ethernet/broadcom/tg3.c
++++ b/drivers/net/ethernet/broadcom/tg3.c
+@@ -4279,7 +4279,8 @@ static int tg3_power_down_prepare(struct
+ static void tg3_power_down(struct tg3 *tp)
+ {
+ pci_wake_from_d3(tp->pdev, tg3_flag(tp, WOL_ENABLE));
+- pci_set_power_state(tp->pdev, PCI_D3hot);
++ if (!tg3_flag(tp, ROBOSWITCH))
++ pci_set_power_state(tp->pdev, PCI_D3hot);
+ }
+
+ static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 *duplex)
diff --git a/target/linux/bcm47xx/patches-4.19/800-bcma-add-table-of-serial-flashes-with-smaller-blocks.patch b/target/linux/bcm47xx/patches-4.19/800-bcma-add-table-of-serial-flashes-with-smaller-blocks.patch
new file mode 100644
index 0000000000..318dc55810
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/800-bcma-add-table-of-serial-flashes-with-smaller-blocks.patch
@@ -0,0 +1,73 @@
+From 597715c61ae75a05ab3310a34ff3857a006f0f63 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Thu, 20 Nov 2014 21:32:42 +0100
+Subject: [PATCH] bcma: add table of serial flashes with smaller blocks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+ drivers/bcma/driver_chipcommon_sflash.c | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+--- a/drivers/bcma/driver_chipcommon_sflash.c
++++ b/drivers/bcma/driver_chipcommon_sflash.c
+@@ -9,6 +9,7 @@
+
+ #include <linux/platform_device.h>
+ #include <linux/bcma/bcma.h>
++#include <bcm47xx_board.h>
+
+ static struct resource bcma_sflash_resource = {
+ .name = "bcma_sflash",
+@@ -42,6 +43,13 @@ static const struct bcma_sflash_tbl_e bc
+ { NULL },
+ };
+
++/* Some devices use smaller blocks (and have more of them) */
++static const struct bcma_sflash_tbl_e bcma_sflash_st_shrink_tbl[] = {
++ { "M25P16", 0x14, 0x1000, 512, },
++ { "M25P32", 0x15, 0x1000, 1024, },
++ { NULL },
++};
++
+ static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
+ { "SST25WF512", 1, 0x1000, 16, },
+ { "SST25VF512", 0x48, 0x1000, 16, },
+@@ -85,6 +93,24 @@ static void bcma_sflash_cmd(struct bcma_
+ bcma_err(cc->core->bus, "SFLASH control command failed (timeout)!\n");
+ }
+
++const struct bcma_sflash_tbl_e *bcma_sflash_shrink_flash(u32 id)
++{
++ enum bcm47xx_board board = bcm47xx_board_get();
++ const struct bcma_sflash_tbl_e *e;
++
++ switch (board) {
++ case BCM47XX_BOARD_NETGEAR_WGR614_V10:
++ case BCM47XX_BOARD_NETGEAR_WNR1000_V3:
++ for (e = bcma_sflash_st_shrink_tbl; e->name; e++) {
++ if (e->id == id)
++ return e;
++ }
++ return NULL;
++ default:
++ return NULL;
++ }
++}
++
+ /* Initialize serial flash access */
+ int bcma_sflash_init(struct bcma_drv_cc *cc)
+ {
+@@ -115,6 +141,10 @@ int bcma_sflash_init(struct bcma_drv_cc
+ case 0x13:
+ return -ENOTSUPP;
+ default:
++ e = bcma_sflash_shrink_flash(id);
++ if (e)
++ break;
++
+ for (e = bcma_sflash_st_tbl; e->name; e++) {
+ if (e->id == id)
+ break;
diff --git a/target/linux/bcm47xx/patches-4.19/820-wgt634u-nvram-fix.patch b/target/linux/bcm47xx/patches-4.19/820-wgt634u-nvram-fix.patch
new file mode 100644
index 0000000000..aaab39ae41
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/820-wgt634u-nvram-fix.patch
@@ -0,0 +1,304 @@
+The Netgear wgt634u uses a different format for storing the
+configuration. This patch is needed to read out the correct
+configuration. The cfe_env.c file uses a different method way to read
+out the configuration than the in kernel cfe config reader.
+
+--- a/drivers/firmware/broadcom/Makefile
++++ b/drivers/firmware/broadcom/Makefile
+@@ -1,2 +1,2 @@
+-obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o
++obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o cfe_env.o
+ obj-$(CONFIG_BCM47XX_SPROM) += bcm47xx_sprom.o
+--- /dev/null
++++ b/drivers/firmware/broadcom/cfe_env.c
+@@ -0,0 +1,228 @@
++/*
++ * CFE environment variable access
++ *
++ * Copyright 2001-2003, Broadcom Corporation
++ * Copyright 2006, Felix Fietkau <nbd@nbd.name>
++ *
++ * 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.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <asm/io.h>
++#include <linux/uaccess.h>
++
++#define NVRAM_SIZE (0x1ff0)
++static char _nvdata[NVRAM_SIZE];
++static char _valuestr[256];
++
++/*
++ * TLV types. These codes are used in the "type-length-value"
++ * encoding of the items stored in the NVRAM device (flash or EEPROM)
++ *
++ * The layout of the flash/nvram is as follows:
++ *
++ * <type> <length> <data ...> <type> <length> <data ...> <type_end>
++ *
++ * The type code of "ENV_TLV_TYPE_END" marks the end of the list.
++ * The "length" field marks the length of the data section, not
++ * including the type and length fields.
++ *
++ * Environment variables are stored as follows:
++ *
++ * <type_env> <length> <flags> <name> = <value>
++ *
++ * If bit 0 (low bit) is set, the length is an 8-bit value.
++ * If bit 0 (low bit) is clear, the length is a 16-bit value
++ *
++ * Bit 7 set indicates "user" TLVs. In this case, bit 0 still
++ * indicates the size of the length field.
++ *
++ * Flags are from the constants below:
++ *
++ */
++#define ENV_LENGTH_16BITS 0x00 /* for low bit */
++#define ENV_LENGTH_8BITS 0x01
++
++#define ENV_TYPE_USER 0x80
++
++#define ENV_CODE_SYS(n,l) (((n)<<1)|(l))
++#define ENV_CODE_USER(n,l) ((((n)<<1)|(l)) | ENV_TYPE_USER)
++
++/*
++ * The actual TLV types we support
++ */
++
++#define ENV_TLV_TYPE_END 0x00
++#define ENV_TLV_TYPE_ENV ENV_CODE_SYS(0,ENV_LENGTH_8BITS)
++
++/*
++ * Environment variable flags
++ */
++
++#define ENV_FLG_NORMAL 0x00 /* normal read/write */
++#define ENV_FLG_BUILTIN 0x01 /* builtin - not stored in flash */
++#define ENV_FLG_READONLY 0x02 /* read-only - cannot be changed */
++
++#define ENV_FLG_MASK 0xFF /* mask of attributes we keep */
++#define ENV_FLG_ADMIN 0x100 /* lets us internally override permissions */
++
++
++/* *********************************************************************
++ * _nvram_read(buffer,offset,length)
++ *
++ * Read data from the NVRAM device
++ *
++ * Input parameters:
++ * buffer - destination buffer
++ * offset - offset of data to read
++ * length - number of bytes to read
++ *
++ * Return value:
++ * number of bytes read, or <0 if error occured
++ ********************************************************************* */
++static int
++_nvram_read(unsigned char *nv_buf, unsigned char *buffer, int offset, int length)
++{
++ int i;
++ if (offset > NVRAM_SIZE)
++ return -1;
++
++ for ( i = 0; i < length; i++) {
++ buffer[i] = ((volatile unsigned char*)nv_buf)[offset + i];
++ }
++ return length;
++}
++
++
++static char*
++_strnchr(const char *dest,int c,size_t cnt)
++{
++ while (*dest && (cnt > 0)) {
++ if (*dest == c) return (char *) dest;
++ dest++;
++ cnt--;
++ }
++ return NULL;
++}
++
++
++
++/*
++ * Core support API: Externally visible.
++ */
++
++/*
++ * Get the value of an NVRAM variable
++ * @param name name of variable to get
++ * @return value of variable or NULL if undefined
++ */
++
++char *cfe_env_get(unsigned char *nv_buf, const char *name)
++{
++ int size;
++ unsigned char *buffer;
++ unsigned char *ptr;
++ unsigned char *envval;
++ unsigned int reclen;
++ unsigned int rectype;
++ int offset;
++ int flg;
++
++ if (!strcmp(name, "nvram_type"))
++ return "cfe";
++
++ size = NVRAM_SIZE;
++ buffer = &_nvdata[0];
++
++ ptr = buffer;
++ offset = 0;
++
++ /* Read the record type and length */
++ if (_nvram_read(nv_buf, ptr,offset,1) != 1) {
++ goto error;
++ }
++
++ while ((*ptr != ENV_TLV_TYPE_END) && (size > 1)) {
++
++ /* Adjust pointer for TLV type */
++ rectype = *(ptr);
++ offset++;
++ size--;
++
++ /*
++ * Read the length. It can be either 1 or 2 bytes
++ * depending on the code
++ */
++ if (rectype & ENV_LENGTH_8BITS) {
++ /* Read the record type and length - 8 bits */
++ if (_nvram_read(nv_buf, ptr,offset,1) != 1) {
++ goto error;
++ }
++ reclen = *(ptr);
++ size--;
++ offset++;
++ }
++ else {
++ /* Read the record type and length - 16 bits, MSB first */
++ if (_nvram_read(nv_buf, ptr,offset,2) != 2) {
++ goto error;
++ }
++ reclen = (((unsigned int) *(ptr)) << 8) + (unsigned int) *(ptr+1);
++ size -= 2;
++ offset += 2;
++ }
++
++ if (reclen > size)
++ break; /* should not happen, bad NVRAM */
++
++ switch (rectype) {
++ case ENV_TLV_TYPE_ENV:
++ /* Read the TLV data */
++ if (_nvram_read(nv_buf, ptr,offset,reclen) != reclen)
++ goto error;
++ flg = *ptr++;
++ envval = (unsigned char *) _strnchr(ptr,'=',(reclen-1));
++ if (envval) {
++ *envval++ = '\0';
++ memcpy(_valuestr,envval,(reclen-1)-(envval-ptr));
++ _valuestr[(reclen-1)-(envval-ptr)] = '\0';
++#if 0
++ printk(KERN_INFO "NVRAM:%s=%s\n", ptr, _valuestr);
++#endif
++ if(!strcmp(ptr, name)){
++ return _valuestr;
++ }
++ if((strlen(ptr) > 1) && !strcmp(&ptr[1], name))
++ return _valuestr;
++ }
++ break;
++
++ default:
++ /* Unknown TLV type, skip it. */
++ break;
++ }
++
++ /*
++ * Advance to next TLV
++ */
++
++ size -= (int)reclen;
++ offset += reclen;
++
++ /* Read the next record type */
++ ptr = buffer;
++ if (_nvram_read(nv_buf, ptr,offset,1) != 1)
++ goto error;
++ }
++
++error:
++ return NULL;
++
++}
++
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -37,6 +37,8 @@ struct nvram_header {
+ static char nvram_buf[NVRAM_SPACE];
+ static size_t nvram_len;
+ static const u32 nvram_sizes[] = {0x6000, 0x8000, 0xF000, 0x10000};
++static int cfe_env;
++extern char *cfe_env_get(char *nv_buf, const char *name);
+
+ static u32 find_nvram_size(void __iomem *end)
+ {
+@@ -56,7 +58,9 @@ static u32 find_nvram_size(void __iomem
+ static int nvram_find_and_copy(void __iomem *iobase, u32 lim)
+ {
+ struct nvram_header __iomem *header;
++ int i;
+ u32 off;
++ u32 *src, *dst;
+ u32 size;
+
+ if (nvram_len) {
+@@ -64,6 +68,26 @@ static int nvram_find_and_copy(void __io
+ return -EEXIST;
+ }
+
++ cfe_env = 0;
++
++ /* XXX: hack for supporting the CFE environment stuff on WGT634U */
++ if (lim >= 8 * 1024 * 1024) {
++ src = (u32 *)(iobase + 8 * 1024 * 1024 - 0x2000);
++ dst = (u32 *)nvram_buf;
++
++ if ((*src & 0xff00ff) == 0x000001) {
++ printk("early_nvram_init: WGT634U NVRAM found.\n");
++
++ for (i = 0; i < 0x1ff0; i++) {
++ if (*src == 0xFFFFFFFF)
++ break;
++ *dst++ = *src++;
++ }
++ cfe_env = 1;
++ return 0;
++ }
++ }
++
+ /* TODO: when nvram is on nand flash check for bad blocks first. */
+ off = FLASH_MIN;
+ while (off <= lim) {
+@@ -174,6 +198,13 @@ int bcm47xx_nvram_getenv(const char *nam
+ if (!name)
+ return -EINVAL;
+
++ if (cfe_env) {
++ value = cfe_env_get(nvram_buf, name);
++ if (!value)
++ return -ENOENT;
++ return snprintf(val, val_len, "%s", value);
++ }
++
+ if (!nvram_len) {
+ err = nvram_init();
+ if (err)
diff --git a/target/linux/bcm47xx/patches-4.19/830-huawei_e970_support.patch b/target/linux/bcm47xx/patches-4.19/830-huawei_e970_support.patch
new file mode 100644
index 0000000000..4d43b9960b
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/830-huawei_e970_support.patch
@@ -0,0 +1,101 @@
+--- a/arch/mips/bcm47xx/setup.c
++++ b/arch/mips/bcm47xx/setup.c
+@@ -37,6 +37,7 @@
+ #include <linux/ssb/ssb.h>
+ #include <linux/ssb/ssb_embedded.h>
+ #include <linux/bcma/bcma_soc.h>
++#include <linux/old_gpio_wdt.h>
+ #include <asm/bootinfo.h>
+ #include <asm/idle.h>
+ #include <asm/prom.h>
+@@ -254,6 +255,33 @@ static struct fixed_phy_status bcm47xx_f
+ .duplex = DUPLEX_FULL,
+ };
+
++static struct gpio_wdt_platform_data gpio_wdt_data;
++
++static struct platform_device gpio_wdt_device = {
++ .name = "gpio-wdt",
++ .id = 0,
++ .dev = {
++ .platform_data = &gpio_wdt_data,
++ },
++};
++
++static int __init bcm47xx_register_gpio_watchdog(void)
++{
++ enum bcm47xx_board board = bcm47xx_board_get();
++
++ switch (board) {
++ case BCM47XX_BOARD_HUAWEI_E970:
++ pr_info("bcm47xx: detected Huawei E970 or similar, starting early gpio_wdt timer\n");
++ gpio_wdt_data.gpio = 7;
++ gpio_wdt_data.interval = HZ;
++ gpio_wdt_data.first_interval = HZ / 5;
++ return platform_device_register(&gpio_wdt_device);
++ default:
++ /* Nothing to do */
++ return 0;
++ }
++}
++
+ static int __init bcm47xx_register_bus_complete(void)
+ {
+ switch (bcm47xx_bus_type) {
+@@ -275,6 +303,7 @@ static int __init bcm47xx_register_bus_c
+ bcm47xx_workarounds();
+
+ fixed_phy_add(PHY_POLL, 0, &bcm47xx_fixed_phy_status, -1);
++ bcm47xx_register_gpio_watchdog();
+ return 0;
+ }
+ device_initcall(bcm47xx_register_bus_complete);
+--- a/arch/mips/configs/bcm47xx_defconfig
++++ b/arch/mips/configs/bcm47xx_defconfig
+@@ -66,6 +66,7 @@ CONFIG_HW_RANDOM=y
+ CONFIG_GPIO_SYSFS=y
+ CONFIG_WATCHDOG=y
+ CONFIG_BCM47XX_WDT=y
++CONFIG_GPIO_WDT=y
+ CONFIG_SSB_DRIVER_GIGE=y
+ CONFIG_BCMA_DRIVER_GMAC_CMN=y
+ CONFIG_USB=y
+--- a/drivers/ssb/embedded.c
++++ b/drivers/ssb/embedded.c
+@@ -34,11 +34,36 @@ int ssb_watchdog_timer_set(struct ssb_bu
+ }
+ EXPORT_SYMBOL(ssb_watchdog_timer_set);
+
++#ifdef CONFIG_BCM47XX
++#include <bcm47xx_board.h>
++
++static bool ssb_watchdog_supported(void)
++{
++ enum bcm47xx_board board = bcm47xx_board_get();
++
++ /* The Huawei E970 has a hardware watchdog using a GPIO */
++ switch (board) {
++ case BCM47XX_BOARD_HUAWEI_E970:
++ return false;
++ default:
++ return true;
++ }
++}
++#else
++static bool ssb_watchdog_supported(void)
++{
++ return true;
++}
++#endif
++
+ int ssb_watchdog_register(struct ssb_bus *bus)
+ {
+ struct bcm47xx_wdt wdt = {};
+ struct platform_device *pdev;
+
++ if (!ssb_watchdog_supported())
++ return 0;
++
+ if (ssb_chipco_available(&bus->chipco)) {
+ wdt.driver_data = &bus->chipco;
+ wdt.timer_set = ssb_chipco_watchdog_timer_set_wdt;
diff --git a/target/linux/bcm47xx/patches-4.19/831-old_gpio_wdt.patch b/target/linux/bcm47xx/patches-4.19/831-old_gpio_wdt.patch
new file mode 100644
index 0000000000..e88f08b53c
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/831-old_gpio_wdt.patch
@@ -0,0 +1,360 @@
+This generic GPIO watchdog is used on Huawei E970 (bcm47xx)
+
+Signed-off-by: Mathias Adam <m.adam--openwrt@adamis.de>
+
+--- a/drivers/watchdog/Kconfig
++++ b/drivers/watchdog/Kconfig
+@@ -1498,6 +1498,15 @@ config WDT_MTX1
+ Hardware driver for the MTX-1 boards. This is a watchdog timer that
+ will reboot the machine after a 100 seconds timer expired.
+
++config GPIO_WDT
++ tristate "GPIO Hardware Watchdog"
++ help
++ Hardware driver for GPIO-controlled watchdogs. GPIO pin and
++ toggle interval settings are platform-specific. The driver
++ will stop toggling the GPIO (i.e. machine reboots) after a
++ 100 second timer expired and no process has written to
++ /dev/watchdog during that time.
++
+ config PNX833X_WDT
+ tristate "PNX833x Hardware Watchdog"
+ depends on SOC_PNX8335
+--- a/drivers/watchdog/Makefile
++++ b/drivers/watchdog/Makefile
+@@ -154,6 +154,7 @@ obj-$(CONFIG_RC32434_WDT) += rc32434_wdt
+ obj-$(CONFIG_INDYDOG) += indydog.o
+ obj-$(CONFIG_JZ4740_WDT) += jz4740_wdt.o
+ obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
++obj-$(CONFIG_GPIO_WDT) += old_gpio_wdt.o
+ obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
+ obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
+ obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+--- /dev/null
++++ b/drivers/watchdog/old_gpio_wdt.c
+@@ -0,0 +1,301 @@
++/*
++ * Driver for GPIO-controlled Hardware Watchdogs.
++ *
++ * Copyright (C) 2013 Mathias Adam <m.adam--linux@adamis.de>
++ *
++ * Replaces mtx1_wdt (driver for the MTX-1 Watchdog):
++ *
++ * (C) Copyright 2005 4G Systems <info@4g-systems.biz>,
++ * All Rights Reserved.
++ * http://www.4g-systems.biz
++ *
++ * (C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@openwrt.org>
++ *
++ * 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.
++ *
++ * Neither Michael Stickel nor 4G Systems admit liability nor provide
++ * warranty for any of this software. This material is provided
++ * "AS-IS" and at no charge.
++ *
++ * (c) Copyright 2005 4G Systems <info@4g-systems.biz>
++ *
++ * Release 0.01.
++ * Author: Michael Stickel michael.stickel@4g-systems.biz
++ *
++ * Release 0.02.
++ * Author: Florian Fainelli florian@openwrt.org
++ * use the Linux watchdog/timer APIs
++ *
++ * Release 0.03.
++ * Author: Mathias Adam <m.adam--linux@adamis.de>
++ * make it a generic gpio watchdog driver
++ *
++ * The Watchdog is configured to reset the MTX-1
++ * if it is not triggered for 100 seconds.
++ * It should not be triggered more often than 1.6 seconds.
++ *
++ * A timer triggers the watchdog every 5 seconds, until
++ * it is opened for the first time. After the first open
++ * it MUST be triggered every 2..95 seconds.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/miscdevice.h>
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/ioport.h>
++#include <linux/timer.h>
++#include <linux/completion.h>
++#include <linux/jiffies.h>
++#include <linux/watchdog.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/uaccess.h>
++#include <linux/gpio.h>
++#include <linux/old_gpio_wdt.h>
++
++static int ticks = 100 * HZ;
++
++static struct {
++ struct completion stop;
++ spinlock_t lock;
++ int running;
++ struct timer_list timer;
++ int queue;
++ int default_ticks;
++ unsigned long inuse;
++ unsigned gpio;
++ unsigned int gstate;
++ int interval;
++ int first_interval;
++} gpio_wdt_device;
++
++static void gpio_wdt_trigger(struct timer_list *unused)
++{
++ spin_lock(&gpio_wdt_device.lock);
++ if (gpio_wdt_device.running && ticks > 0)
++ ticks -= gpio_wdt_device.interval;
++
++ /* toggle wdt gpio */
++ gpio_wdt_device.gstate = !gpio_wdt_device.gstate;
++ gpio_set_value(gpio_wdt_device.gpio, gpio_wdt_device.gstate);
++
++ if (gpio_wdt_device.queue && ticks > 0)
++ mod_timer(&gpio_wdt_device.timer, jiffies + gpio_wdt_device.interval);
++ else
++ complete(&gpio_wdt_device.stop);
++ spin_unlock(&gpio_wdt_device.lock);
++}
++
++static void gpio_wdt_reset(void)
++{
++ ticks = gpio_wdt_device.default_ticks;
++}
++
++
++static void gpio_wdt_start(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&gpio_wdt_device.lock, flags);
++ if (!gpio_wdt_device.queue) {
++ gpio_wdt_device.queue = 1;
++ gpio_wdt_device.gstate = 1;
++ gpio_set_value(gpio_wdt_device.gpio, 1);
++ mod_timer(&gpio_wdt_device.timer, jiffies + gpio_wdt_device.first_interval);
++ }
++ gpio_wdt_device.running++;
++ spin_unlock_irqrestore(&gpio_wdt_device.lock, flags);
++}
++
++static int gpio_wdt_stop(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&gpio_wdt_device.lock, flags);
++ if (gpio_wdt_device.queue) {
++ gpio_wdt_device.queue = 0;
++ gpio_wdt_device.gstate = 0;
++ gpio_set_value(gpio_wdt_device.gpio, 0);
++ }
++ ticks = gpio_wdt_device.default_ticks;
++ spin_unlock_irqrestore(&gpio_wdt_device.lock, flags);
++ return 0;
++}
++
++/* Filesystem functions */
++
++static int gpio_wdt_open(struct inode *inode, struct file *file)
++{
++ if (test_and_set_bit(0, &gpio_wdt_device.inuse))
++ return -EBUSY;
++ return nonseekable_open(inode, file);
++}
++
++
++static int gpio_wdt_release(struct inode *inode, struct file *file)
++{
++ clear_bit(0, &gpio_wdt_device.inuse);
++ return 0;
++}
++
++static long gpio_wdt_ioctl(struct file *file, unsigned int cmd,
++ unsigned long arg)
++{
++ void __user *argp = (void __user *)arg;
++ int __user *p = (int __user *)argp;
++ unsigned int value;
++ static const struct watchdog_info ident = {
++ .options = WDIOF_CARDRESET,
++ .identity = "GPIO WDT",
++ };
++
++ switch (cmd) {
++ case WDIOC_GETSUPPORT:
++ if (copy_to_user(argp, &ident, sizeof(ident)))
++ return -EFAULT;
++ break;
++ case WDIOC_GETSTATUS:
++ case WDIOC_GETBOOTSTATUS:
++ put_user(0, p);
++ break;
++ case WDIOC_SETOPTIONS:
++ if (get_user(value, p))
++ return -EFAULT;
++ if (value & WDIOS_ENABLECARD)
++ gpio_wdt_start();
++ else if (value & WDIOS_DISABLECARD)
++ gpio_wdt_stop();
++ else
++ return -EINVAL;
++ return 0;
++ case WDIOC_KEEPALIVE:
++ gpio_wdt_reset();
++ break;
++ default:
++ return -ENOTTY;
++ }
++ return 0;
++}
++
++
++static ssize_t gpio_wdt_write(struct file *file, const char *buf,
++ size_t count, loff_t *ppos)
++{
++ if (!count)
++ return -EIO;
++ gpio_wdt_reset();
++ return count;
++}
++
++static const struct file_operations gpio_wdt_fops = {
++ .owner = THIS_MODULE,
++ .llseek = no_llseek,
++ .unlocked_ioctl = gpio_wdt_ioctl,
++ .open = gpio_wdt_open,
++ .write = gpio_wdt_write,
++ .release = gpio_wdt_release,
++};
++
++
++static struct miscdevice gpio_wdt_misc = {
++ .minor = WATCHDOG_MINOR,
++ .name = "watchdog",
++ .fops = &gpio_wdt_fops,
++};
++
++
++static int gpio_wdt_probe(struct platform_device *pdev)
++{
++ int ret;
++ struct gpio_wdt_platform_data *gpio_wdt_data = pdev->dev.platform_data;
++
++ gpio_wdt_device.gpio = gpio_wdt_data->gpio;
++ gpio_wdt_device.interval = gpio_wdt_data->interval;
++ gpio_wdt_device.first_interval = gpio_wdt_data->first_interval;
++ if (gpio_wdt_device.first_interval <= 0) {
++ gpio_wdt_device.first_interval = gpio_wdt_device.interval;
++ }
++
++ ret = gpio_request(gpio_wdt_device.gpio, "gpio-wdt");
++ if (ret < 0) {
++ dev_err(&pdev->dev, "failed to request gpio");
++ return ret;
++ }
++
++ spin_lock_init(&gpio_wdt_device.lock);
++ init_completion(&gpio_wdt_device.stop);
++ gpio_wdt_device.queue = 0;
++ clear_bit(0, &gpio_wdt_device.inuse);
++ timer_setup(&gpio_wdt_device.timer, gpio_wdt_trigger, 0L);
++ gpio_wdt_device.default_ticks = ticks;
++
++ gpio_wdt_start();
++ dev_info(&pdev->dev, "GPIO Hardware Watchdog driver (gpio=%i interval=%i/%i)\n",
++ gpio_wdt_data->gpio, gpio_wdt_data->first_interval, gpio_wdt_data->interval);
++ return 0;
++}
++
++static int gpio_wdt_remove(struct platform_device *pdev)
++{
++ /* FIXME: do we need to lock this test ? */
++ if (gpio_wdt_device.queue) {
++ gpio_wdt_device.queue = 0;
++ wait_for_completion(&gpio_wdt_device.stop);
++ }
++
++ gpio_free(gpio_wdt_device.gpio);
++ misc_deregister(&gpio_wdt_misc);
++ return 0;
++}
++
++static struct platform_driver gpio_wdt_driver = {
++ .probe = gpio_wdt_probe,
++ .remove = gpio_wdt_remove,
++ .driver.name = "gpio-wdt",
++ .driver.owner = THIS_MODULE,
++};
++
++static int __init gpio_wdt_init(void)
++{
++ return platform_driver_register(&gpio_wdt_driver);
++}
++arch_initcall(gpio_wdt_init);
++
++/*
++ * We do wdt initialization in two steps: arch_initcall probes the wdt
++ * very early to start pinging the watchdog (misc devices are not yet
++ * available), and later module_init() just registers the misc device.
++ */
++static int gpio_wdt_init_late(void)
++{
++ int ret;
++
++ ret = misc_register(&gpio_wdt_misc);
++ if (ret < 0) {
++ pr_err("GPIO_WDT: failed to register misc device\n");
++ return ret;
++ }
++ return 0;
++}
++#ifndef MODULE
++module_init(gpio_wdt_init_late);
++#endif
++
++static void __exit gpio_wdt_exit(void)
++{
++ platform_driver_unregister(&gpio_wdt_driver);
++}
++module_exit(gpio_wdt_exit);
++
++MODULE_AUTHOR("Michael Stickel, Florian Fainelli, Mathias Adam");
++MODULE_DESCRIPTION("Driver for GPIO hardware watchdogs");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
++MODULE_ALIAS("platform:gpio-wdt");
+--- /dev/null
++++ b/include/linux/old_gpio_wdt.h
+@@ -0,0 +1,21 @@
++/*
++ * Definitions for the GPIO watchdog driver
++ *
++ * Copyright (C) 2013 Mathias Adam <m.adam--linux@adamis.de>
++ *
++ * 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 _GPIO_WDT_H_
++#define _GPIO_WDT_H_
++
++struct gpio_wdt_platform_data {
++ int gpio; /* GPIO line number */
++ int interval; /* watchdog reset interval in system ticks */
++ int first_interval; /* first wd reset interval in system ticks */
++};
++
++#endif /* _GPIO_WDT_H_ */
diff --git a/target/linux/bcm47xx/patches-4.19/900-ssb-reject-PCI-writes-setting-CardBus-bridge-resourc.patch b/target/linux/bcm47xx/patches-4.19/900-ssb-reject-PCI-writes-setting-CardBus-bridge-resourc.patch
new file mode 100644
index 0000000000..6b7ee06e50
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/900-ssb-reject-PCI-writes-setting-CardBus-bridge-resourc.patch
@@ -0,0 +1,30 @@
+From 5c81397a0147ea59c778d1de14ef54e2268221f6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Wed, 8 Apr 2015 06:58:11 +0200
+Subject: [PATCH] ssb: reject PCI writes setting CardBus bridge resources
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If SoC has a CardBus we can set resources of device at slot 1 only. It's
+impossigle to set bridge resources as it simply overwrites device 1
+configuration and usually results in Data bus error-s.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+ drivers/ssb/driver_pcicore.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/ssb/driver_pcicore.c
++++ b/drivers/ssb/driver_pcicore.c
+@@ -164,6 +164,10 @@ static int ssb_extpci_write_config(struc
+ WARN_ON(!pc->hostmode);
+ if (unlikely(len != 1 && len != 2 && len != 4))
+ goto out;
++ /* CardBus SoCs allow configuring dev 1 resources only */
++ if (extpci_core->cardbusmode && dev != 1 &&
++ off >= PCI_BASE_ADDRESS_0 && off <= PCI_BASE_ADDRESS_5)
++ goto out;
+ addr = get_cfgspace_addr(pc, bus, dev, func, off);
+ if (unlikely(!addr))
+ goto out;
diff --git a/target/linux/bcm47xx/patches-4.19/940-bcm47xx-yenta.patch b/target/linux/bcm47xx/patches-4.19/940-bcm47xx-yenta.patch
new file mode 100644
index 0000000000..f5b7bda4e6
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/940-bcm47xx-yenta.patch
@@ -0,0 +1,46 @@
+--- a/drivers/pcmcia/yenta_socket.c
++++ b/drivers/pcmcia/yenta_socket.c
+@@ -920,6 +920,8 @@ static unsigned int yenta_probe_irq(stru
+ * Probe for usable interrupts using the force
+ * register to generate bogus card status events.
+ */
++#ifndef CONFIG_BCM47XX
++ /* WRT54G3G does not like this */
+ cb_writel(socket, CB_SOCKET_EVENT, -1);
+ cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
+ reg = exca_readb(socket, I365_CSCINT);
+@@ -935,6 +937,7 @@ static unsigned int yenta_probe_irq(stru
+ }
+ cb_writel(socket, CB_SOCKET_MASK, 0);
+ exca_writeb(socket, I365_CSCINT, reg);
++#endif
+
+ mask = probe_irq_mask(val) & 0xffff;
+
+@@ -1019,6 +1022,10 @@ static void yenta_get_socket_capabilitie
+ else
+ socket->socket.irq_mask = 0;
+
++ /* irq mask probing is broken for the WRT54G3G */
++ if (socket->socket.irq_mask == 0)
++ socket->socket.irq_mask = 0x6f8;
++
+ dev_info(&socket->dev->dev, "ISA IRQ mask 0x%04x, PCI irq %d\n",
+ socket->socket.irq_mask, socket->cb_irq);
+ }
+@@ -1250,6 +1257,15 @@ static int yenta_probe(struct pci_dev *d
+ dev_info(&dev->dev, "Socket status: %08x\n",
+ cb_readl(socket, CB_SOCKET_STATE));
+
++ /* Generate an interrupt on card insert/remove */
++ config_writew(socket, CB_SOCKET_MASK, CB_CSTSMASK | CB_CDMASK);
++
++ /* Set up Multifunction Routing Status Register */
++ config_writew(socket, 0x8C, 0x1000 /* MFUNC3 to GPIO3 */ | 0x2 /* MFUNC0 to INTA */);
++
++ /* Switch interrupts to parallelized */
++ config_writeb(socket, 0x92, 0x64);
++
+ yenta_fixup_parent_bridge(dev->subordinate);
+
+ /* Register it with the pcmcia layer.. */
diff --git a/target/linux/bcm47xx/patches-4.19/976-ssb_increase_pci_delay.patch b/target/linux/bcm47xx/patches-4.19/976-ssb_increase_pci_delay.patch
new file mode 100644
index 0000000000..99aa188374
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/976-ssb_increase_pci_delay.patch
@@ -0,0 +1,11 @@
+--- a/drivers/ssb/driver_pcicore.c
++++ b/drivers/ssb/driver_pcicore.c
+@@ -390,7 +390,7 @@ static void ssb_pcicore_init_hostmode(st
+ set_io_port_base(ssb_pcicore_controller.io_map_base);
+ /* Give some time to the PCI controller to configure itself with the new
+ * values. Not waiting at this point causes crashes of the machine. */
+- mdelay(10);
++ mdelay(300);
+ register_pci_controller(&ssb_pcicore_controller);
+ }
+
diff --git a/target/linux/bcm47xx/patches-4.19/999-wl_exports.patch b/target/linux/bcm47xx/patches-4.19/999-wl_exports.patch
new file mode 100644
index 0000000000..2936421aff
--- /dev/null
+++ b/target/linux/bcm47xx/patches-4.19/999-wl_exports.patch
@@ -0,0 +1,22 @@
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -34,7 +34,8 @@ struct nvram_header {
+ u32 config_ncdl; /* ncdl values for memc */
+ };
+
+-static char nvram_buf[NVRAM_SPACE];
++char nvram_buf[NVRAM_SPACE];
++EXPORT_SYMBOL(nvram_buf);
+ static size_t nvram_len;
+ static const u32 nvram_sizes[] = {0x6000, 0x8000, 0xF000, 0x10000};
+ static int cfe_env;
+--- a/arch/mips/mm/cache.c
++++ b/arch/mips/mm/cache.c
+@@ -64,6 +64,7 @@ void (*_dma_cache_wback)(unsigned long s
+ void (*_dma_cache_inv)(unsigned long start, unsigned long size);
+
+ EXPORT_SYMBOL(_dma_cache_wback_inv);
++EXPORT_SYMBOL(_dma_cache_inv);
+
+ #endif /* CONFIG_DMA_NONCOHERENT */
+