Просмотр исходного кода

bcm53xx: initial support for kernel 3.18

This adds initial support for kernel 3.18.

Signed-off-by: Hauke Mehrtens <[email protected]>

SVN-Revision: 43097
Hauke Mehrtens 11 лет назад
Родитель
Сommit
7fc9bb1aca
30 измененных файлов с 6385 добавлено и 0 удалено
  1. 304 0
      target/linux/bcm53xx/config-3.18
  2. 33 0
      target/linux/bcm53xx/patches-3.18/044-ARM-BCM5301X-fix-early-serial-console.patch
  3. 29 0
      target/linux/bcm53xx/patches-3.18/045-ARM-BCM5301X-select-GPIOLIB-as-optional.patch
  4. 43 0
      target/linux/bcm53xx/patches-3.18/046-ARM-BCM5301X-Add-Broadcom-s-bus-axi-to-the-DTS-file.patch
  5. 54 0
      target/linux/bcm53xx/patches-3.18/047-ARM-BCM5301X-Add-LEDs-for-Netgear-R6250-V1.patch
  6. 279 0
      target/linux/bcm53xx/patches-3.18/110-bcm47xx-move-the-nvram-header-file-into-common-space.patch
  7. 588 0
      target/linux/bcm53xx/patches-3.18/111-bcm47xx-nvram-add-new-nvram-driver-with-dt-support.patch
  8. 762 0
      target/linux/bcm53xx/patches-3.18/112-bcm53xx-sprom-add-sprom-driver.patch
  9. 38 0
      target/linux/bcm53xx/patches-3.18/121-bcma-get-irqs-from-dt.patch
  10. 65 0
      target/linux/bcm53xx/patches-3.18/122-bcma-fill-core-details-for-every-device.patch
  11. 88 0
      target/linux/bcm53xx/patches-3.18/123-bcma-get-sprom-from-devicetree.patch
  12. 114 0
      target/linux/bcm53xx/patches-3.18/130-ARM-BCM5301X-register-bcma-bus.patch
  13. 69 0
      target/linux/bcm53xx/patches-3.18/131-ARM-BCM5301X-add-restart-support.patch
  14. 29 0
      target/linux/bcm53xx/patches-3.18/150-pci-do-not-probe-too-early.patch
  15. 670 0
      target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch
  16. 195 0
      target/linux/bcm53xx/patches-3.18/300-ARM-BCM5301X-Disable-MMU-and-Dcache-for-decompression.patch
  17. 59 0
      target/linux/bcm53xx/patches-3.18/301-ARM-BCM5301X-Add-buttons-support-for-Netgear-R6250.patch
  18. 125 0
      target/linux/bcm53xx/patches-3.18/302-ARM-BCM5301X-Add-DT-for-Netgear-R6300-V2.patch
  19. 102 0
      target/linux/bcm53xx/patches-3.18/303-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-1750DHP.patch
  20. 160 0
      target/linux/bcm53xx/patches-3.18/304-ARM-BCM5301X-Add-DT-for-Asus-RT-N18U.patch
  21. 97 0
      target/linux/bcm53xx/patches-3.18/305-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-600DHP2.patch
  22. 46 0
      target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch
  23. 374 0
      target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch
  24. 263 0
      target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch
  25. 42 0
      target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch
  26. 32 0
      target/linux/bcm53xx/patches-3.18/410-mtd-bcm47xxpart-alloc-memory-for-more-partitions.patch
  27. 1616 0
      target/linux/bcm53xx/patches-3.18/420-mtd-bcm5301x_nand.patch
  28. 34 0
      target/linux/bcm53xx/patches-3.18/500-UBI-Detect-EOF-mark-and-erase-all-remaining-blocks.patch
  29. 33 0
      target/linux/bcm53xx/patches-3.18/900-bgmac-some-fixes-to-get-bgmac-work.patch
  30. 42 0
      target/linux/bcm53xx/patches-3.18/901-bcma-register-SoC-later-as-a-module.patch

+ 304 - 0
target/linux/bcm53xx/config-3.18

@@ -0,0 +1,304 @@
+CONFIG_ALIGNMENT_TRAP=y
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM_5301X=y
+# CONFIG_ARCH_BCM_63XX is not set
+# CONFIG_ARCH_BCM_MOBILE is not set
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+# CONFIG_ARCH_BRCMSTB is not set
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_ARCH_HAS_SG_CHAIN=y
+CONFIG_ARCH_HAS_TICK_BROADCAST=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+# CONFIG_ARCH_HISI is not set
+# CONFIG_ARCH_MEDIATEK is not set
+# CONFIG_ARCH_MESON is not set
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+# CONFIG_ARCH_MSM is not set
+CONFIG_ARCH_MULTIPLATFORM=y
+# CONFIG_ARCH_MULTI_CPU_AUTO is not set
+CONFIG_ARCH_MULTI_V6_V7=y
+CONFIG_ARCH_MULTI_V7=y
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+CONFIG_ARCH_NR_GPIO=0
+# CONFIG_ARCH_QCOM is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_ARM=y
+CONFIG_ARM_APPENDED_DTB=y
+# CONFIG_ARM_ATAG_DTB_COMPAT is not set
+# CONFIG_ARM_CPU_SUSPEND is not set
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_ARM_ERRATA_775420=y
+CONFIG_ARM_GIC=y
+CONFIG_ARM_GLOBAL_TIMER=y
+CONFIG_ARM_HAS_SG_CHAIN=y
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+# CONFIG_ARM_LPAE is not set
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+CONFIG_ARM_VIRT_EXT=y
+CONFIG_ATAGS=y
+# CONFIG_ATMEL_PIT is not set
+# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set
+CONFIG_AUTO_ZRELADDR=y
+CONFIG_B53=y
+# CONFIG_B53_MMAP_DRIVER is not set
+# CONFIG_B53_PHY_DRIVER is not set
+CONFIG_B53_SRAB_DRIVER=y
+CONFIG_BCM47XX_NVRAM=y
+CONFIG_BCM47XX_SPROM=y
+CONFIG_BCMA=y
+CONFIG_BCMA_BLOCKIO=y
+CONFIG_BCMA_DEBUG=y
+CONFIG_BCMA_DRIVER_GMAC_CMN=y
+CONFIG_BCMA_DRIVER_GPIO=y
+CONFIG_BCMA_HOST_PCI=y
+CONFIG_BCMA_HOST_PCI_POSSIBLE=y
+CONFIG_BCMA_HOST_SOC=y
+CONFIG_BGMAC=y
+# CONFIG_BUILD_BIN2C is not set
+CONFIG_CACHE_L2X0=y
+CONFIG_CACHE_PL310=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK=y
+CONFIG_CLKSRC_OF=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_COMMON_CLK=y
+# CONFIG_COMMON_CLK_PXA is not set
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_CPU_HAS_ASID=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_RMAP=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_V7=y
+CONFIG_CRC16=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_XZ=y
+# CONFIG_CXL_BASE is not set
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_DEBUG_BCM_5301X=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_LL_INCLUDE="debug/8250.S"
+# CONFIG_DEBUG_LL_UART_8250 is not set
+# CONFIG_DEBUG_LL_UART_PL01X is not set
+CONFIG_DEBUG_UART_8250=y
+# CONFIG_DEBUG_UART_8250_FLOW_CONTROL is not set
+CONFIG_DEBUG_UART_8250_SHIFT=0
+# CONFIG_DEBUG_UART_BCM63XX is not set
+CONFIG_DEBUG_UART_PHYS=0x18000300
+# CONFIG_DEBUG_UART_PL01X is not set
+CONFIG_DEBUG_UART_VIRT=0xf1000300
+CONFIG_DEBUG_UNCOMPRESS=y
+CONFIG_DEBUG_USER=y
+CONFIG_DTC=y
+CONFIG_EARLY_PRINTK=y
+# CONFIG_EM_TIMER_STI is not set
+CONFIG_FRAME_POINTER=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_NET_UTILS=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_ARCH_AUDITSYSCALL=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_PFN_VALID=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_ARM_SCU=y
+CONFIG_HAVE_ARM_TWD=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_BPF_JIT=y
+CONFIG_HAVE_CC_STACKPROTECTOR=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZ4=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_SMP=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_UID16=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HZ_FIXED=0
+CONFIG_HZ_PERIODIC=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IOMMU_HELPER=y
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_WORK=y
+CONFIG_KERNFS=y
+CONFIG_LIBFDT=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_MDIO_BOARDINFO=y
+CONFIG_MIGHT_HAVE_CACHE_L2X0=y
+CONFIG_MIGHT_HAVE_PCI=y
+CONFIG_MODULES_USE_ELF_REL=y
+# CONFIG_MODULE_COMPRESS is not set
+CONFIG_MTD_BCM47XX_PARTS=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_BCM=y
+CONFIG_MTD_NAND_ECC=y
+# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set
+# CONFIG_MTD_PHYSMAP_OF is not set
+# CONFIG_MTD_SM_COMMON is not set
+CONFIG_MTD_SPI_BCM53XXSPIFLASH=y
+CONFIG_MTD_SPI_NOR=y
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BEB_LIMIT=20
+CONFIG_MTD_UBI_BLOCK=y
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MULTI_IRQ_HANDLER=y
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NET_FLOW_LIMIT=y
+# CONFIG_NET_PTP_CLASSIFY is not set
+# CONFIG_NET_UDP_TUNNEL is not set
+CONFIG_NO_BOOTMEM=y
+CONFIG_NR_CPUS=4
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_ADDRESS_PCI=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_MTD=y
+CONFIG_OF_NET=y
+CONFIG_OF_PCI=y
+CONFIG_OF_PCI_IRQ=y
+CONFIG_OF_RESERVED_MEM=y
+CONFIG_OLD_SIGACTION=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_OUTER_CACHE=y
+CONFIG_OUTER_CACHE_SYNC=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PCI=y
+CONFIG_PCI_BCMA=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PHYLIB=y
+# CONFIG_PL310_ERRATA_588369 is not set
+# CONFIG_PL310_ERRATA_727915 is not set
+# CONFIG_PL310_ERRATA_753970 is not set
+# CONFIG_PL310_ERRATA_769419 is not set
+# CONFIG_PREEMPT_RCU is not set
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RPS=y
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCSI_DMA is not set
+CONFIG_SERIAL_EARLYCON=y
+CONFIG_SERIAL_OF_PLATFORM=y
+# CONFIG_SH_TIMER_CMT is not set
+# CONFIG_SH_TIMER_MTU2 is not set
+# CONFIG_SH_TIMER_TMU is not set
+CONFIG_SMP=y
+CONFIG_SMP_ON_UP=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+CONFIG_SPI_BCM53XX=y
+CONFIG_SPI_MASTER=y
+CONFIG_STOP_MACHINE=y
+CONFIG_SWCONFIG=y
+CONFIG_SWIOTLB=y
+CONFIG_SWP_EMULATE=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TREE_RCU=y
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_XZ=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UID16=y
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+# CONFIG_UPROBES is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USE_OF=y
+CONFIG_VECTORS_BASE=0xffff0000
+# CONFIG_VFP is not set
+# CONFIG_XEN is not set
+CONFIG_XPS=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_BCJ=y
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZONE_DMA_FLAG=0

+ 33 - 0
target/linux/bcm53xx/patches-3.18/044-ARM-BCM5301X-fix-early-serial-console.patch

@@ -0,0 +1,33 @@
+From 310a267714f7565dba8934dd51cdead6adc3b630 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Sun, 14 Sep 2014 21:02:35 +0200
+Subject: [PATCH 4/4] ARM: BCM5301X: fix early serial console
+
+This device actually has a 8250 serial with a shift of 0.
+Tested this on a BCM4708.
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ arch/arm/Kconfig.debug | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/Kconfig.debug
++++ b/arch/arm/Kconfig.debug
+@@ -113,7 +113,7 @@ choice
+ 	config DEBUG_BCM_5301X
+ 		bool "Kernel low-level debugging on BCM5301X UART1"
+ 		depends on ARCH_BCM_5301X
+-		select DEBUG_UART_PL01X
++		select DEBUG_UART_8250
+ 
+ 	config DEBUG_BCM_KONA_UART
+ 		bool "Kernel low-level debugging messages via BCM KONA UART"
+@@ -1249,7 +1249,7 @@ config DEBUG_UART_VIRT
+ config DEBUG_UART_8250_SHIFT
+ 	int "Register offset shift for the 8250 debug UART"
+ 	depends on DEBUG_LL_UART_8250 || DEBUG_UART_8250
+-	default 0 if FOOTBRIDGE || ARCH_IOP32X
++	default 0 if FOOTBRIDGE || ARCH_IOP32X || DEBUG_BCM_5301X
+ 	default 2
+ 
+ config DEBUG_UART_8250_WORD

+ 29 - 0
target/linux/bcm53xx/patches-3.18/045-ARM-BCM5301X-select-GPIOLIB-as-optional.patch

@@ -0,0 +1,29 @@
+From e7b1065712e769eb4de7b9d4aa222a4531c2b8fd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Sat, 20 Sep 2014 18:21:19 +0200
+Subject: [PATCH V2] ARM: BCM5301X: select GPIOLIB as optional
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+All routers (or 99% of them) based on BCM5301X use GPIOs to control LEDs
+and buttons.
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+V2: Don't select GPIOLIB. We may still think about making it default at
+    some point, but we dont' really require it to boot successfully.
+---
+ arch/arm/mach-bcm/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/mach-bcm/Kconfig
++++ b/arch/arm/mach-bcm/Kconfig
+@@ -119,6 +119,7 @@ config ARCH_BCM_63XX
+ config ARCH_BRCMSTB
+ 	bool "Broadcom BCM7XXX based boards" if ARCH_MULTI_V7
+ 	depends on MMU
++	select ARCH_WANT_OPTIONAL_GPIOLIB
+ 	select ARM_GIC
+ 	select MIGHT_HAVE_PCI
+ 	select HAVE_SMP

+ 43 - 0
target/linux/bcm53xx/patches-3.18/046-ARM-BCM5301X-Add-Broadcom-s-bus-axi-to-the-DTS-file.patch

@@ -0,0 +1,43 @@
+From a2533caee935fff97e3e8dbfad5cc159e6bf6034 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Wed, 1 Oct 2014 09:21:07 +0200
+Subject: [PATCH 1/2] ARM: BCM5301X: Add Broadcom's bus-axi to the DTS file
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/boot/dts/bcm5301x.dtsi | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm5301x.dtsi
++++ b/arch/arm/boot/dts/bcm5301x.dtsi
+@@ -8,6 +8,7 @@
+  * Licensed under the GNU/GPL. See COPYING for details.
+  */
+ 
++#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include "skeleton.dtsi"
+@@ -92,4 +93,19 @@
+ 			clock-frequency = <400000000>;
+ 		};
+ 	};
++
++	axi@18000000 {
++		compatible = "brcm,bus-axi";
++		reg = <0x18000000 0x1000>;
++		ranges = <0x00000000 0x18000000 0x00100000>;
++		#address-cells = <1>;
++		#size-cells = <1>;
++
++		chipcommon: chipcommon@0 {
++			reg = <0x00000000 0x1000>;
++
++			gpio-controller;
++			#gpio-cells = <2>;
++		};
++	};
+ };

+ 54 - 0
target/linux/bcm53xx/patches-3.18/047-ARM-BCM5301X-Add-LEDs-for-Netgear-R6250-V1.patch

@@ -0,0 +1,54 @@
+From b7e4d148906685882a081e7e50692313c5a8724e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Wed, 1 Oct 2014 09:23:09 +0200
+Subject: [PATCH 2/2] ARM: BCM5301X: Add LEDs for Netgear R6250 V1
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/boot/dts/bcm4708-netgear-r6250.dts | 34 +++++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm4708-netgear-r6250.dts
++++ b/arch/arm/boot/dts/bcm4708-netgear-r6250.dts
+@@ -32,4 +32,38 @@
+ 			status = "okay";
+ 		};
+ 	};
++
++	leds {
++		compatible = "gpio-leds";
++
++		logo {
++			label = "bcm53xx:white:logo";
++			gpios = <&chipcommon 1 GPIO_ACTIVE_HIGH>;
++			linux,default-trigger = "default-on";
++		};
++
++		power0 {
++			label = "bcm53xx:green:power";
++			gpios = <&chipcommon 2 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-off";
++		};
++
++		power1 {
++			label = "bcm53xx:amber:power";
++			gpios = <&chipcommon 3 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-on";
++		};
++
++		usb {
++			label = "bcm53xx:blue:usb";
++			gpios = <&chipcommon 8 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-off";
++		};
++
++		wireless {
++			label = "bcm53xx:blue:wireless";
++			gpios = <&chipcommon 11 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-off";
++		};
++	};
+ };

+ 279 - 0
target/linux/bcm53xx/patches-3.18/110-bcm47xx-move-the-nvram-header-file-into-common-space.patch

@@ -0,0 +1,279 @@
+From 7063a1583166abe1a9cefed38c2f53a0e14a0005 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Sun, 4 May 2014 16:35:42 +0200
+Subject: [PATCH 01/17] MIPS: BCM47XX: move the nvram header file into common
+ space
+
+Moving mach-bcm47xx/bcm47xx_nvram.h to include/linux/bcm47xx_nvram.h
+makes it possible to reuse this header on the ARM based bcm47xx/bcm53xx
+SoCs (e.g. BCM5301X devices). Broadcom uses ARM CPUs in their new SoC
+form the bcm47xx and bcm53xx line, but many other things like nvram
+stayed the same.
+
+This is a preparation for adding a new nvram driver, which can be used
+by the ARM SoC and the MIPS SoC code. The device drivers accessing
+nvram do not have to care about ARM or MIPS SoC version.
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ arch/mips/bcm47xx/board.c                          |  2 +-
+ arch/mips/bcm47xx/nvram.c                          |  2 +-
+ arch/mips/bcm47xx/setup.c                          |  2 +-
+ arch/mips/bcm47xx/sprom.c                          |  2 +-
+ arch/mips/bcm47xx/time.c                           |  2 +-
+ arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h | 53 -----------------
+ drivers/net/ethernet/broadcom/b44.c                |  8 +--
+ drivers/net/ethernet/broadcom/bgmac.c              |  2 +-
+ drivers/ssb/driver_chipcommon_pmu.c                |  6 +-
+ include/linux/bcm47xx_nvram.h                      | 66 ++++++++++++++++++++++
+ 10 files changed, 74 insertions(+), 71 deletions(-)
+ delete mode 100644 arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h
+ create mode 100644 include/linux/bcm47xx_nvram.h
+
+--- a/arch/mips/bcm47xx/board.c
++++ b/arch/mips/bcm47xx/board.c
+@@ -2,7 +2,7 @@
+ #include <linux/export.h>
+ #include <linux/string.h>
+ #include <bcm47xx_board.h>
+-#include <bcm47xx_nvram.h>
++#include <linux/bcm47xx_nvram.h>
+ 
+ struct bcm47xx_board_type {
+ 	const enum bcm47xx_board board;
+--- a/arch/mips/bcm47xx/nvram.c
++++ b/arch/mips/bcm47xx/nvram.c
+@@ -17,7 +17,7 @@
+ #include <linux/kernel.h>
+ #include <linux/string.h>
+ #include <asm/addrspace.h>
+-#include <bcm47xx_nvram.h>
++#include <linux/bcm47xx_nvram.h>
+ #include <asm/mach-bcm47xx/bcm47xx.h>
+ 
+ static char nvram_buf[NVRAM_SPACE];
+--- a/arch/mips/bcm47xx/setup.c
++++ b/arch/mips/bcm47xx/setup.c
+@@ -42,7 +42,7 @@
+ #include <asm/reboot.h>
+ #include <asm/time.h>
+ #include <bcm47xx.h>
+-#include <bcm47xx_nvram.h>
++#include <linux/bcm47xx_nvram.h>
+ #include <bcm47xx_board.h>
+ 
+ union bcm47xx_bus bcm47xx_bus;
+--- a/arch/mips/bcm47xx/sprom.c
++++ b/arch/mips/bcm47xx/sprom.c
+@@ -27,7 +27,7 @@
+  */
+ 
+ #include <bcm47xx.h>
+-#include <bcm47xx_nvram.h>
++#include <linux/bcm47xx_nvram.h>
+ #include <linux/if_ether.h>
+ #include <linux/etherdevice.h>
+ 
+--- a/arch/mips/bcm47xx/time.c
++++ b/arch/mips/bcm47xx/time.c
+@@ -27,7 +27,7 @@
+ #include <linux/ssb/ssb.h>
+ #include <asm/time.h>
+ #include <bcm47xx.h>
+-#include <bcm47xx_nvram.h>
++#include <linux/bcm47xx_nvram.h>
+ #include <bcm47xx_board.h>
+ 
+ void __init plat_time_init(void)
+--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h
++++ /dev/null
+@@ -1,53 +0,0 @@
+-/*
+- *  Copyright (C) 2005, Broadcom Corporation
+- *  Copyright (C) 2006, Felix Fietkau <[email protected]>
+- *
+- *  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.
+- */
+-
+-#ifndef __BCM47XX_NVRAM_H
+-#define __BCM47XX_NVRAM_H
+-
+-#include <linux/types.h>
+-#include <linux/kernel.h>
+-
+-struct nvram_header {
+-	u32 magic;
+-	u32 len;
+-	u32 crc_ver_init;	/* 0:7 crc, 8:15 ver, 16:31 sdram_init */
+-	u32 config_refresh;	/* 0:15 sdram_config, 16:31 sdram_refresh */
+-	u32 config_ncdl;	/* ncdl values for memc */
+-};
+-
+-#define NVRAM_HEADER		0x48534C46	/* 'FLSH' */
+-#define NVRAM_VERSION		1
+-#define NVRAM_HEADER_SIZE	20
+-#define NVRAM_SPACE		0x8000
+-
+-#define FLASH_MIN		0x00020000	/* Minimum flash size */
+-
+-#define NVRAM_MAX_VALUE_LEN 255
+-#define NVRAM_MAX_PARAM_LEN 64
+-
+-extern int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len);
+-
+-static inline void bcm47xx_nvram_parse_macaddr(char *buf, u8 macaddr[6])
+-{
+-	if (strchr(buf, ':'))
+-		sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0],
+-			&macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
+-			&macaddr[5]);
+-	else if (strchr(buf, '-'))
+-		sscanf(buf, "%hhx-%hhx-%hhx-%hhx-%hhx-%hhx", &macaddr[0],
+-			&macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
+-			&macaddr[5]);
+-	else
+-		printk(KERN_WARNING "Can not parse mac address: %s\n", buf);
+-}
+-
+-int bcm47xx_nvram_gpio_pin(const char *name);
+-
+-#endif /* __BCM47XX_NVRAM_H */
+--- a/drivers/net/ethernet/broadcom/b44.c
++++ b/drivers/net/ethernet/broadcom/b44.c
+@@ -31,6 +31,7 @@
+ #include <linux/ssb/ssb.h>
+ #include <linux/slab.h>
+ #include <linux/phy.h>
++#include <linux/bcm47xx_nvram.h>
+ 
+ #include <asm/uaccess.h>
+ #include <asm/io.h>
+@@ -399,8 +400,6 @@ static void b44_set_flow_ctrl(struct b44
+ 	__b44_set_flow_ctrl(bp, pause_enab);
+ }
+ 
+-#ifdef CONFIG_BCM47XX
+-#include <bcm47xx_nvram.h>
+ static void b44_wap54g10_workaround(struct b44 *bp)
+ {
+ 	char buf[20];
+@@ -429,11 +428,6 @@ static void b44_wap54g10_workaround(stru
+ error:
+ 	pr_warn("PHY: cannot reset MII transceiver isolate bit\n");
+ }
+-#else
+-static inline void b44_wap54g10_workaround(struct b44 *bp)
+-{
+-}
+-#endif
+ 
+ static int b44_setup_phy(struct b44 *bp)
+ {
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -17,7 +17,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/platform_data/b53.h>
+-#include <bcm47xx_nvram.h>
++#include <linux/bcm47xx_nvram.h>
+ 
+ static const struct bcma_device_id bgmac_bcma_tbl[] = {
+ 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_4706_MAC_GBIT, BCMA_ANY_REV, BCMA_ANY_CLASS),
+--- a/drivers/ssb/driver_chipcommon_pmu.c
++++ b/drivers/ssb/driver_chipcommon_pmu.c
+@@ -13,9 +13,7 @@
+ #include <linux/ssb/ssb_driver_chipcommon.h>
+ #include <linux/delay.h>
+ #include <linux/export.h>
+-#ifdef CONFIG_BCM47XX
+-#include <bcm47xx_nvram.h>
+-#endif
++#include <linux/bcm47xx_nvram.h>
+ 
+ #include "ssb_private.h"
+ 
+@@ -320,11 +318,9 @@ static void ssb_pmu_pll_init(struct ssb_
+ 	u32 crystalfreq = 0; /* in kHz. 0 = keep default freq. */
+ 
+ 	if (bus->bustype == SSB_BUSTYPE_SSB) {
+-#ifdef CONFIG_BCM47XX
+ 		char buf[20];
+ 		if (bcm47xx_nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0)
+ 			crystalfreq = simple_strtoul(buf, NULL, 0);
+-#endif
+ 	}
+ 
+ 	switch (bus->chip_id) {
+--- /dev/null
++++ b/include/linux/bcm47xx_nvram.h
+@@ -0,0 +1,66 @@
++/*
++ *  Copyright (C) 2005, Broadcom Corporation
++ *  Copyright (C) 2006, Felix Fietkau <[email protected]>
++ *  Copyright (C) 2014 Hauke Mehrtens <[email protected]>
++ *
++ *  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.
++ */
++
++#ifndef __BCM47XX_NVRAM_H
++#define __BCM47XX_NVRAM_H
++
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++struct nvram_header {
++	u32 magic;
++	u32 len;
++	u32 crc_ver_init;	/* 0:7 crc, 8:15 ver, 16:31 sdram_init */
++	u32 config_refresh;	/* 0:15 sdram_config, 16:31 sdram_refresh */
++	u32 config_ncdl;	/* ncdl values for memc */
++};
++
++#define NVRAM_HEADER		0x48534C46	/* 'FLSH' */
++#define NVRAM_VERSION		1
++#define NVRAM_HEADER_SIZE	20
++#define NVRAM_SPACE		0x8000
++
++#define FLASH_MIN		0x00020000	/* Minimum flash size */
++
++#define NVRAM_MAX_VALUE_LEN 255
++#define NVRAM_MAX_PARAM_LEN 64
++
++#ifdef CONFIG_BCM47XX
++int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len);
++
++int bcm47xx_nvram_gpio_pin(const char *name);
++#else
++static inline int bcm47xx_nvram_getenv(const char *name, char *val,
++				       size_t val_len)
++{
++	return -ENXIO;
++}
++
++static inline int bcm47xx_nvram_gpio_pin(const char *name)
++{
++	return -ENXIO;
++}
++#endif
++
++static inline void bcm47xx_nvram_parse_macaddr(char *buf, u8 macaddr[6])
++{
++	if (strchr(buf, ':'))
++		sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0],
++			&macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
++			&macaddr[5]);
++	else if (strchr(buf, '-'))
++		sscanf(buf, "%hhx-%hhx-%hhx-%hhx-%hhx-%hhx", &macaddr[0],
++			&macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
++			&macaddr[5]);
++	else
++		pr_warn("Can not parse mac address: %s\n", buf);
++}
++#endif /* __BCM47XX_NVRAM_H */

+ 588 - 0
target/linux/bcm53xx/patches-3.18/111-bcm47xx-nvram-add-new-nvram-driver-with-dt-support.patch

@@ -0,0 +1,588 @@
+From 71a6bff8656a1713615ffdd9139a83d65ba46c6d Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Sat, 3 May 2014 22:54:59 +0200
+Subject: [PATCH 02/17] bcm47xx-nvram: add new broadcom nvram driver with dt
+ support
+
+This adds a new driver which searches at a given memory range for a
+nvram like it is used on the bcm47xx and bcm53xx SoCs with ARM and MIPS
+CPUs. This driver provides acces to this nvram to other device in the
+device tree. You have to specify the memory ranges where the content of
+the flash chip is memory mapped and this driver will search there for
+some nvram and parse it. Other drivers can use this driver to access the
+device nvram. The nvram is used to store board configurations like the
+mac addresses, the switch configuration and the calibration data for
+the wifi devices.
+
+This was copied from arch/mips/bcm47xx/nvram.c and modified to interact
+with device tree. My plan is to make the MIPS bcm47xx also use this new
+driver some time later.
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ .../devicetree/bindings/misc/bcm47xx-nvram.txt     |  19 ++
+ arch/mips/bcm47xx/board.c                          |  40 ++--
+ arch/mips/bcm47xx/nvram.c                          |   7 +-
+ arch/mips/bcm47xx/setup.c                          |   4 +-
+ arch/mips/bcm47xx/sprom.c                          |   4 +-
+ arch/mips/bcm47xx/time.c                           |   2 +-
+ drivers/misc/Kconfig                               |   5 +
+ drivers/misc/Makefile                              |   1 +
+ drivers/misc/bcm47xx-nvram.c                       | 215 +++++++++++++++++++++
+ drivers/net/ethernet/broadcom/b44.c                |   2 +-
+ drivers/net/ethernet/broadcom/bgmac.c              |   5 +-
+ drivers/ssb/driver_chipcommon_pmu.c                |   3 +-
+ include/linux/bcm47xx_nvram.h                      |  17 +-
+ 13 files changed, 286 insertions(+), 38 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-nvram.txt
+ create mode 100644 drivers/misc/bcm47xx-nvram.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/misc/bcm47xx-nvram.txt
+@@ -0,0 +1,19 @@
++Broadcom bcm47xx/bcm53xx nvram access driver
++
++This driver provides access to the nvram for other drivers.
++
++Required properties:
++
++- compatible : brcm,bcm47xx-nvram
++
++- reg : iomem address range
++
++On NorthStar ARM SoCs the NAND flash is available at 0x1c000000 and the
++NOR flash is at 0x1e000000
++
++Example:
++
++nvram0: nvram@0 {
++	compatible = "brcm,bcm47xx-nvram";
++	reg = <0x1c000000 0x01000000>;
++};
+--- a/arch/mips/bcm47xx/board.c
++++ b/arch/mips/bcm47xx/board.c
+@@ -218,36 +218,36 @@ static __init const struct bcm47xx_board
+ 	const struct bcm47xx_board_type_list2 *e2;
+ 	const struct bcm47xx_board_type_list3 *e3;
+ 
+-	if (bcm47xx_nvram_getenv("model_name", buf1, sizeof(buf1)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "model_name", buf1, sizeof(buf1)) >= 0) {
+ 		for (e1 = bcm47xx_board_list_model_name; e1->value1; e1++) {
+ 			if (!strcmp(buf1, e1->value1))
+ 				return &e1->board;
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("model_no", buf1, sizeof(buf1)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "model_no", buf1, sizeof(buf1)) >= 0) {
+ 		for (e1 = bcm47xx_board_list_model_no; e1->value1; e1++) {
+ 			if (strstarts(buf1, e1->value1))
+ 				return &e1->board;
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("machine_name", buf1, sizeof(buf1)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "machine_name", buf1, sizeof(buf1)) >= 0) {
+ 		for (e1 = bcm47xx_board_list_machine_name; e1->value1; e1++) {
+ 			if (strstarts(buf1, e1->value1))
+ 				return &e1->board;
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("hardware_version", buf1, sizeof(buf1)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "hardware_version", buf1, sizeof(buf1)) >= 0) {
+ 		for (e1 = bcm47xx_board_list_hardware_version; e1->value1; e1++) {
+ 			if (strstarts(buf1, e1->value1))
+ 				return &e1->board;
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("hardware_version", buf1, sizeof(buf1)) >= 0 &&
+-	    bcm47xx_nvram_getenv("boardtype", buf2, sizeof(buf2)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "hardware_version", buf1, sizeof(buf1)) >= 0 &&
++	    bcm47xx_nvram_getenv(NULL, "boardtype", buf2, sizeof(buf2)) >= 0) {
+ 		for (e2 = bcm47xx_board_list_boot_hw; e2->value1; e2++) {
+ 			if (!strstarts(buf1, e2->value1) &&
+ 			    !strcmp(buf2, e2->value2))
+@@ -255,22 +255,22 @@ static __init const struct bcm47xx_board
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("productid", buf1, sizeof(buf1)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "productid", buf1, sizeof(buf1)) >= 0) {
+ 		for (e1 = bcm47xx_board_list_productid; e1->value1; e1++) {
+ 			if (!strcmp(buf1, e1->value1))
+ 				return &e1->board;
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("ModelId", buf1, sizeof(buf1)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "ModelId", buf1, sizeof(buf1)) >= 0) {
+ 		for (e1 = bcm47xx_board_list_ModelId; e1->value1; e1++) {
+ 			if (!strcmp(buf1, e1->value1))
+ 				return &e1->board;
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("melco_id", buf1, sizeof(buf1)) >= 0 ||
+-	    bcm47xx_nvram_getenv("buf1falo_id", buf1, sizeof(buf1)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "melco_id", buf1, sizeof(buf1)) >= 0 ||
++	    bcm47xx_nvram_getenv(NULL, "buf1falo_id", buf1, sizeof(buf1)) >= 0) {
+ 		/* buffalo hardware, check id for specific hardware matches */
+ 		for (e1 = bcm47xx_board_list_melco_id; e1->value1; e1++) {
+ 			if (!strcmp(buf1, e1->value1))
+@@ -278,8 +278,8 @@ static __init const struct bcm47xx_board
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("boot_hw_model", buf1, sizeof(buf1)) >= 0 &&
+-	    bcm47xx_nvram_getenv("boot_hw_ver", buf2, sizeof(buf2)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "boot_hw_model", buf1, sizeof(buf1)) >= 0 &&
++	    bcm47xx_nvram_getenv(NULL, "boot_hw_ver", buf2, sizeof(buf2)) >= 0) {
+ 		for (e2 = bcm47xx_board_list_boot_hw; e2->value1; e2++) {
+ 			if (!strcmp(buf1, e2->value1) &&
+ 			    !strcmp(buf2, e2->value2))
+@@ -287,16 +287,16 @@ static __init const struct bcm47xx_board
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("board_id", buf1, sizeof(buf1)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "board_id", buf1, sizeof(buf1)) >= 0) {
+ 		for (e1 = bcm47xx_board_list_board_id; e1->value1; e1++) {
+ 			if (!strcmp(buf1, e1->value1))
+ 				return &e1->board;
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("boardtype", buf1, sizeof(buf1)) >= 0 &&
+-	    bcm47xx_nvram_getenv("boardnum", buf2, sizeof(buf2)) >= 0 &&
+-	    bcm47xx_nvram_getenv("boardrev", buf3, sizeof(buf3)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "boardtype", buf1, sizeof(buf1)) >= 0 &&
++	    bcm47xx_nvram_getenv(NULL, "boardnum", buf2, sizeof(buf2)) >= 0 &&
++	    bcm47xx_nvram_getenv(NULL, "boardrev", buf3, sizeof(buf3)) >= 0) {
+ 		for (e3 = bcm47xx_board_list_board; e3->value1; e3++) {
+ 			if (!strcmp(buf1, e3->value1) &&
+ 			    !strcmp(buf2, e3->value2) &&
+@@ -305,9 +305,9 @@ static __init const struct bcm47xx_board
+ 		}
+ 	}
+ 
+-	if (bcm47xx_nvram_getenv("boardtype", buf1, sizeof(buf1)) >= 0 &&
+-	    bcm47xx_nvram_getenv("boardrev", buf2, sizeof(buf2)) >= 0 &&
+-	    bcm47xx_nvram_getenv("boardnum", buf3, sizeof(buf3)) ==  -ENOENT) {
++	if (bcm47xx_nvram_getenv(NULL, "boardtype", buf1, sizeof(buf1)) >= 0 &&
++	    bcm47xx_nvram_getenv(NULL, "boardrev", buf2, sizeof(buf2)) >= 0 &&
++	    bcm47xx_nvram_getenv(NULL, "boardnum", buf3, sizeof(buf3)) ==  -ENOENT) {
+ 		for (e2 = bcm47xx_board_list_board_type_rev; e2->value1; e2++) {
+ 			if (!strcmp(buf1, e2->value1) &&
+ 			    !strcmp(buf2, e2->value2))
+@@ -327,7 +327,7 @@ void __init bcm47xx_board_detect(void)
+ 		return;
+ 
+ 	/* check if the nvram is available */
+-	err = bcm47xx_nvram_getenv("boardtype", buf, sizeof(buf));
++	err = bcm47xx_nvram_getenv(NULL, "boardtype", buf, sizeof(buf));
+ 
+ 	/* init of nvram failed, probably too early now */
+ 	if (err == -ENXIO) {
+--- a/arch/mips/bcm47xx/nvram.c
++++ b/arch/mips/bcm47xx/nvram.c
+@@ -158,7 +158,8 @@ static int nvram_init(void)
+ 	return -ENXIO;
+ }
+ 
+-int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len)
++int bcm47xx_nvram_getenv(const struct device *dev, const char *name, char *val,
++			 size_t val_len)
+ {
+ 	char *var, *value, *end, *eq;
+ 	int err;
+@@ -190,7 +191,7 @@ int bcm47xx_nvram_getenv(char *name, cha
+ }
+ EXPORT_SYMBOL(bcm47xx_nvram_getenv);
+ 
+-int bcm47xx_nvram_gpio_pin(const char *name)
++int bcm47xx_nvram_gpio_pin(const struct device *dev, const char *name)
+ {
+ 	int i, err;
+ 	char nvram_var[10];
+@@ -200,7 +201,7 @@ int bcm47xx_nvram_gpio_pin(const char *n
+ 		err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i);
+ 		if (err <= 0)
+ 			continue;
+-		err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf));
++		err = bcm47xx_nvram_getenv(dev, nvram_var, buf, sizeof(buf));
+ 		if (err <= 0)
+ 			continue;
+ 		if (!strcmp(name, buf))
+--- a/arch/mips/bcm47xx/setup.c
++++ b/arch/mips/bcm47xx/setup.c
+@@ -132,7 +132,7 @@ static int bcm47xx_get_invariants(struct
+ 	memset(&iv->sprom, 0, sizeof(struct ssb_sprom));
+ 	bcm47xx_fill_sprom(&iv->sprom, NULL, false);
+ 
+-	if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0)
++	if (bcm47xx_nvram_getenv(NULL, "cardbus", buf, sizeof(buf)) >= 0)
+ 		iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10);
+ 
+ 	return 0;
+@@ -155,7 +155,7 @@ static void __init bcm47xx_register_ssb(
+ 		panic("Failed to initialize SSB bus (err %d)", err);
+ 
+ 	mcore = &bcm47xx_bus.ssb.mipscore;
+-	if (bcm47xx_nvram_getenv("kernel_args", buf, sizeof(buf)) >= 0) {
++	if (bcm47xx_nvram_getenv(NULL, "kernel_args", buf, sizeof(buf)) >= 0) {
+ 		if (strstr(buf, "console=ttyS1")) {
+ 			struct ssb_serial_port port;
+ 
+--- a/arch/mips/bcm47xx/sprom.c
++++ b/arch/mips/bcm47xx/sprom.c
+@@ -52,10 +52,10 @@ static int get_nvram_var(const char *pre
+ 
+ 	create_key(prefix, postfix, name, key, sizeof(key));
+ 
+-	err = bcm47xx_nvram_getenv(key, buf, len);
++	err = bcm47xx_nvram_getenv(NULL, key, buf, len);
+ 	if (fallback && err == -ENOENT && prefix) {
+ 		create_key(NULL, postfix, name, key, sizeof(key));
+-		err = bcm47xx_nvram_getenv(key, buf, len);
++		err = bcm47xx_nvram_getenv(NULL, key, buf, len);
+ 	}
+ 	return err;
+ }
+--- a/arch/mips/bcm47xx/time.c
++++ b/arch/mips/bcm47xx/time.c
+@@ -61,7 +61,7 @@ void __init plat_time_init(void)
+ 	}
+ 
+ 	if (chip_id == 0x5354) {
+-		len = bcm47xx_nvram_getenv("clkfreq", buf, sizeof(buf));
++		len = bcm47xx_nvram_getenv(NULL, "clkfreq", buf, sizeof(buf));
+ 		if (len >= 0 && !strncmp(buf, "200", 4))
+ 			hz = 100000000;
+ 	}
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -515,6 +515,11 @@ config VEXPRESS_SYSCFG
+ 	  bus. System Configuration interface is one of the possible means
+ 	  of generating transactions on this bus.
+ 
++config BCM47XX_NVRAM
++	tristate "BCM47XX nvram driver"
++	help
++		This adds support for the brcm47xx nvram driver.
++
+ source "drivers/misc/c2port/Kconfig"
+ source "drivers/misc/eeprom/Kconfig"
+ source "drivers/misc/cb710/Kconfig"
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -56,3 +56,4 @@ obj-$(CONFIG_GENWQE)		+= genwqe/
+ obj-$(CONFIG_ECHO)		+= echo/
+ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
+ obj-$(CONFIG_CXL_BASE)		+= cxl/
++obj-$(CONFIG_BCM47XX_NVRAM)	+= bcm47xx-nvram.o
+--- /dev/null
++++ b/drivers/misc/bcm47xx-nvram.c
+@@ -0,0 +1,215 @@
++/*
++ * BCM947xx nvram variable access
++ *
++ * Copyright (C) 2005 Broadcom Corporation
++ * Copyright (C) 2006 Felix Fietkau <[email protected]>
++ * Copyright (C) 2010-2014 Hauke Mehrtens <[email protected]>
++ *
++ * 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/types.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/of_address.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/bcm47xx_nvram.h>
++
++struct bcm47xx_nvram {
++	size_t nvram_len;
++	char *nvram_buf;
++};
++
++static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000};
++
++static u32 find_nvram_size(void __iomem *end)
++{
++	struct nvram_header __iomem *header;
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
++		header = (struct nvram_header __iomem *)(end - nvram_sizes[i]);
++		if (__raw_readl(&header->magic) == NVRAM_HEADER)
++			return nvram_sizes[i];
++	}
++
++	return 0;
++}
++
++/* Probe for NVRAM header */
++static int nvram_find_and_copy(struct device *dev, void __iomem *base,
++			       size_t len, char **nvram_buf,
++			       size_t *nvram_len)
++{
++	struct nvram_header __iomem *header;
++	int i;
++	u32 off;
++	u32 *dst;
++	__le32 __iomem *src;
++	u32 size;
++
++	/* TODO: when nvram is on nand flash check for bad blocks first. */
++	off = FLASH_MIN;
++	while (off <= len) {
++		/* Windowed flash access */
++		size = find_nvram_size(base + off);
++		if (size) {
++			header = (struct nvram_header __iomem *)
++					(base + off - size);
++			goto found;
++		}
++		off += 0x10000;
++	}
++
++	/* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
++	header = (struct nvram_header __iomem *)(base + 4096);
++	if (__raw_readl(&header->magic) == NVRAM_HEADER) {
++		size = NVRAM_SPACE;
++		goto found;
++	}
++
++	header = (struct nvram_header __iomem *)(base + 1024);
++	if (__raw_readl(&header->magic) == NVRAM_HEADER) {
++		size = NVRAM_SPACE;
++		goto found;
++	}
++
++	*nvram_buf = NULL;
++	*nvram_len = 0;
++	pr_err("no nvram found\n");
++	return -ENXIO;
++
++found:
++	if (readl(&header->len) > size)
++		pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n");
++	*nvram_len = min_t(u32, readl(&header->len), size);
++
++	*nvram_buf = devm_kzalloc(dev, *nvram_len, GFP_KERNEL);
++	if (!*nvram_buf)
++		return -ENOMEM;
++
++	src = (__le32 __iomem *) header;
++	dst = (u32 *) *nvram_buf;
++	for (i = 0; i < sizeof(struct nvram_header); i += 4)
++		*dst++ = __raw_readl(src++);
++	for (; i < *nvram_len; i += 4)
++		*dst++ = readl(src++);
++
++	return 0;
++}
++
++int bcm47xx_nvram_getenv(const struct device *dev, const char *name, char *val,
++			 size_t val_len)
++{
++	char *var, *value, *end, *eq;
++	struct bcm47xx_nvram *nvram;
++
++	if (!dev)
++		return -ENODEV;
++
++	nvram = dev_get_drvdata(dev);
++
++	if (!name || !nvram || !nvram->nvram_len)
++		return -EINVAL;
++
++	/* Look for name=value and return value */
++	var = nvram->nvram_buf + sizeof(struct nvram_header);
++	end = nvram->nvram_buf + nvram->nvram_len - 2;
++	end[0] = end[1] = '\0';
++	for (; *var; var = value + strlen(value) + 1) {
++		eq = strchr(var, '=');
++		if (!eq)
++			break;
++		value = eq + 1;
++		if ((eq - var) == strlen(name) &&
++			strncmp(var, name, (eq - var)) == 0) {
++			return snprintf(val, val_len, "%s", value);
++		}
++	}
++	return -ENOENT;
++}
++EXPORT_SYMBOL(bcm47xx_nvram_getenv);
++
++int bcm47xx_nvram_gpio_pin(const struct device *dev, const char *name)
++{
++	int i, err;
++	char nvram_var[10];
++	char buf[30];
++
++	if (!dev)
++		return -ENODEV;
++
++	for (i = 0; i < 32; i++) {
++		err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i);
++		if (err <= 0)
++			continue;
++		err = bcm47xx_nvram_getenv(dev, nvram_var, buf, sizeof(buf));
++		if (err <= 0)
++			continue;
++		if (!strcmp(name, buf))
++			return i;
++	}
++	return -ENOENT;
++}
++EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin);
++
++static int bcm47xx_nvram_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *np = dev->of_node;
++	struct bcm47xx_nvram *nvram;
++	int err;
++	struct resource flash_mem;
++	void __iomem *mmio;
++
++	/* Alloc */
++	nvram = devm_kzalloc(dev, sizeof(*nvram), GFP_KERNEL);
++	if (!nvram)
++		return -ENOMEM;
++
++	err = of_address_to_resource(np, 0, &flash_mem);
++	if (err)
++		return err;
++
++	mmio = ioremap_nocache(flash_mem.start, resource_size(&flash_mem));
++	if (!mmio)
++		return -ENOMEM;
++
++	err = nvram_find_and_copy(dev, mmio, resource_size(&flash_mem),
++				  &nvram->nvram_buf, &nvram->nvram_len);
++	if (err)
++		goto err_unmap_mmio;
++
++	platform_set_drvdata(pdev, nvram);
++
++err_unmap_mmio:
++	iounmap(mmio);
++	return err;
++}
++
++static const struct of_device_id bcm47xx_nvram_of_match_table[] = {
++	{ .compatible = "brcm,bcm47xx-nvram", },
++	{},
++};
++MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table);
++
++static struct platform_driver bcm47xx_nvram_driver = {
++	.driver = {
++		.owner = THIS_MODULE,
++		.name = "bcm47xx-nvram",
++		.of_match_table = bcm47xx_nvram_of_match_table,
++		/* driver unloading/unbinding currently not supported */
++		.suppress_bind_attrs = true,
++	},
++	.probe = bcm47xx_nvram_probe,
++};
++module_platform_driver(bcm47xx_nvram_driver);
++
++MODULE_AUTHOR("Hauke Mehrtens <[email protected]>");
++MODULE_LICENSE("GPLv2");
+--- a/drivers/net/ethernet/broadcom/b44.c
++++ b/drivers/net/ethernet/broadcom/b44.c
+@@ -411,7 +411,7 @@ static void b44_wap54g10_workaround(stru
+ 	 * see https://dev.openwrt.org/ticket/146
+ 	 * check and reset bit "isolate"
+ 	 */
+-	if (bcm47xx_nvram_getenv("boardnum", buf, sizeof(buf)) < 0)
++	if (bcm47xx_nvram_getenv(NULL, "boardnum", buf, sizeof(buf)) < 0)
+ 		return;
+ 	if (simple_strtoul(buf, NULL, 0) == 2) {
+ 		err = __b44_readphy(bp, 0, MII_BMCR, &val);
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -974,7 +974,8 @@ static void bgmac_chip_reset(struct bgma
+ 			     BGMAC_CHIPCTL_1_IF_TYPE_MII;
+ 		char buf[4];
+ 
+-		if (bcm47xx_nvram_getenv("et_swtype", buf, sizeof(buf)) > 0) {
++		if (bcm47xx_nvram_getenv(NULL, "et_swtype", buf,
++					 sizeof(buf)) > 0) {
+ 			if (kstrtou8(buf, 0, &et_swtype))
+ 				bgmac_err(bgmac, "Failed to parse et_swtype (%s)\n",
+ 					  buf);
+@@ -1534,7 +1535,7 @@ static int bgmac_probe(struct bcma_devic
+ 	}
+ 
+ 	bgmac->int_mask = BGMAC_IS_ERRMASK | BGMAC_IS_RX | BGMAC_IS_TX_MASK;
+-	if (bcm47xx_nvram_getenv("et0_no_txint", NULL, 0) == 0)
++	if (bcm47xx_nvram_getenv(NULL, "et0_no_txint", NULL, 0) == 0)
+ 		bgmac->int_mask &= ~BGMAC_IS_TX_MASK;
+ 
+ 	/* TODO: reset the external phy. Specs are needed */
+--- a/drivers/ssb/driver_chipcommon_pmu.c
++++ b/drivers/ssb/driver_chipcommon_pmu.c
+@@ -319,7 +319,8 @@ static void ssb_pmu_pll_init(struct ssb_
+ 
+ 	if (bus->bustype == SSB_BUSTYPE_SSB) {
+ 		char buf[20];
+-		if (bcm47xx_nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0)
++		if (bcm47xx_nvram_getenv(NULL, "xtalfreq", buf,
++					 sizeof(buf)) >= 0)
+ 			crystalfreq = simple_strtoul(buf, NULL, 0);
+ 	}
+ 
+--- a/include/linux/bcm47xx_nvram.h
++++ b/include/linux/bcm47xx_nvram.h
+@@ -15,9 +15,11 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ 
++struct device;
++
+ struct nvram_header {
+ 	u32 magic;
+-	u32 len;
++	__le32 len;
+ 	u32 crc_ver_init;	/* 0:7 crc, 8:15 ver, 16:31 sdram_init */
+ 	u32 config_refresh;	/* 0:15 sdram_config, 16:31 sdram_refresh */
+ 	u32 config_ncdl;	/* ncdl values for memc */
+@@ -33,18 +35,21 @@ struct nvram_header {
+ #define NVRAM_MAX_VALUE_LEN 255
+ #define NVRAM_MAX_PARAM_LEN 64
+ 
+-#ifdef CONFIG_BCM47XX
+-int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len);
++#if defined(CONFIG_BCM47XX) || defined(CONFIG_BCM47XX_NVRAM)
++int bcm47xx_nvram_getenv(const struct device *dev, const char *name, char *val,
++			 size_t val_len);
+ 
+-int bcm47xx_nvram_gpio_pin(const char *name);
++int bcm47xx_nvram_gpio_pin(const struct device *dev, const char *name);
+ #else
+-static inline int bcm47xx_nvram_getenv(const char *name, char *val,
++static inline int bcm47xx_nvram_getenv(const struct device *dev,
++				       const char *name, char *val,
+ 				       size_t val_len)
+ {
+ 	return -ENXIO;
+ }
+ 
+-static inline int bcm47xx_nvram_gpio_pin(const char *name)
++static inline int bcm47xx_nvram_gpio_pin(const struct device *dev,
++					 const char *name)
+ {
+ 	return -ENXIO;
+ }

+ 762 - 0
target/linux/bcm53xx/patches-3.18/112-bcm53xx-sprom-add-sprom-driver.patch

@@ -0,0 +1,762 @@
+From 4e0ab3269a6d260a41a3673157753147f5f71341 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Sun, 4 May 2014 13:19:20 +0200
+Subject: [PATCH 03/17] bcm47xx-sprom: add Broadcom sprom parser driver
+
+This driver needs an nvram driver and fetches the sprom values from the
+nvram and provides it to any other driver. The calibration data for the
+wifi chip the mac address and some more board description data is
+stores in the sprom.
+
+This is based on a copy of arch/mips/bcm47xx/sprom.c and my plan is to
+make the bcm47xx MIPS SoCs also use this driver some time later.
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ .../devicetree/bindings/misc/bcm47xx-sprom.txt     |  16 +
+ drivers/misc/Kconfig                               |  11 +
+ drivers/misc/Makefile                              |   1 +
+ drivers/misc/bcm47xx-sprom.c                       | 690 +++++++++++++++++++++
+ 4 files changed, 718 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
+ create mode 100644 drivers/misc/bcm47xx-sprom.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
+@@ -0,0 +1,16 @@
++Broadcom bcm47xx/bcm53xx sprom converter
++
++This driver provbides an sprom based on a given nvram.
++
++Required properties:
++
++- compatible : brcm,bcm47xx-sprom
++
++- nvram : reference to a nvram driver, e.g. bcm47xx-nvram
++
++Example:
++
++sprom0: sprom@0 {
++	compatible = "brcm,bcm47xx-sprom";
++	nvram = <&nvram0>;
++};
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -520,6 +520,17 @@ config BCM47XX_NVRAM
+ 	help
+ 		This adds support for the brcm47xx nvram driver.
+ 
++config BCM47XX_SPROM
++	tristate "BCM47XX sprom driver"
++	help
++	  This driver parses the sprom from a given nvram which is found on
++	  Broadcom bcm47xx and bcm53xx SoCs.
++
++	  The sprom contains board configuration data like the
++	  calibration data fro the wifi chips, the mac addresses used
++	  by the board and many other board configuration data. This
++	  driver will provide the sprom to bcma.
++
+ source "drivers/misc/c2port/Kconfig"
+ source "drivers/misc/eeprom/Kconfig"
+ source "drivers/misc/cb710/Kconfig"
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -57,3 +57,4 @@ obj-$(CONFIG_ECHO)		+= echo/
+ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
+ obj-$(CONFIG_CXL_BASE)		+= cxl/
+ obj-$(CONFIG_BCM47XX_NVRAM)	+= bcm47xx-nvram.o
++obj-$(CONFIG_BCM47XX_SPROM)	+= bcm47xx-sprom.o
+--- /dev/null
++++ b/drivers/misc/bcm47xx-sprom.c
+@@ -0,0 +1,690 @@
++/*
++ * BCM47xx/BCM53xx nvram variable access
++ *
++ * Copyright (C) 2005 Broadcom Corporation
++ * Copyright (C) 2004 Florian Schirmer <[email protected]>
++ * Copyright (C) 2006 Michael Buesch <[email protected]>
++ * Copyright (C) 2010 Waldemar Brodkorb <[email protected]>
++ * Copyright (C) 2006 Felix Fietkau <[email protected]>
++ * Copyright (C) 2010-2014 Hauke Mehrtens <[email protected]>
++ *
++ * 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/types.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/of_address.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/of_platform.h>
++#include <linux/io.h>
++#include <linux/ssb/ssb.h>
++#include <linux/bcm47xx_nvram.h>
++#include <linux/if_ether.h>
++#include <linux/etherdevice.h>
++
++struct bcm47xx_sprom_fill {
++	const char *prefix;
++	bool fallback;
++	int (*getenv)(const struct bcm47xx_sprom_fill *fill, const char *name,
++		      char *val, size_t val_len);
++	const void *priv;
++};
++
++static void create_key(const char *prefix, const char *postfix,
++		       const char *name, char *buf, int len)
++{
++	if (prefix && postfix)
++		snprintf(buf, len, "%s%s%s", prefix, name, postfix);
++	else if (prefix)
++		snprintf(buf, len, "%s%s", prefix, name);
++	else if (postfix)
++		snprintf(buf, len, "%s%s", name, postfix);
++	else
++		snprintf(buf, len, "%s", name);
++}
++
++static int get_nvram_var(const struct bcm47xx_sprom_fill *fill,
++			 const char *postfix, const char *name, char *buf,
++			 int len)
++{
++	char key[40];
++	int err;
++
++	create_key(fill->prefix, postfix, name, key, sizeof(key));
++
++	err = fill->getenv(fill, key, buf, len);
++	if (fill->fallback && err == -ENOENT && fill->prefix) {
++		create_key(NULL, postfix, name, key, sizeof(key));
++		err = fill->getenv(fill, key, buf, len);
++	}
++	return err;
++}
++
++#define NVRAM_READ_VAL(type)						\
++static void nvram_read_ ## type (const struct bcm47xx_sprom_fill *fill,	\
++				 const char *postfix, const char *name, \
++				 type *val, type allset)		\
++{									\
++	char buf[100];							\
++	int err;							\
++	type var;							\
++									\
++	err = get_nvram_var(fill, postfix, name, buf, sizeof(buf));	\
++	if (err < 0)							\
++		return;							\
++	err = kstrto ## type(strim(buf), 0, &var);			\
++	if (err) {							\
++		pr_warn("can not parse nvram name %s%s%s with value %s got %i\n",	\
++			fill->prefix, name, postfix, buf, err);		\
++		return;							\
++	}								\
++	if (allset && var == allset)					\
++		return;							\
++	*val = var;							\
++}
++
++NVRAM_READ_VAL(u8)
++NVRAM_READ_VAL(s8)
++NVRAM_READ_VAL(u16)
++NVRAM_READ_VAL(u32)
++
++#undef NVRAM_READ_VAL
++
++static void nvram_read_u32_2(const struct bcm47xx_sprom_fill *fill,
++			     const char *name, u16 *val_lo, u16 *val_hi)
++{
++	char buf[100];
++	int err;
++	u32 val;
++
++	err = get_nvram_var(fill, NULL, name, buf, sizeof(buf));
++	if (err < 0)
++		return;
++	err = kstrtou32(strim(buf), 0, &val);
++	if (err) {
++		pr_warn("can not parse nvram name %s%s with value %s got %i\n",
++			fill->prefix, name, buf, err);
++		return;
++	}
++	*val_lo = (val & 0x0000FFFFU);
++	*val_hi = (val & 0xFFFF0000U) >> 16;
++}
++
++static void nvram_read_leddc(const struct bcm47xx_sprom_fill *fill,
++			     const char *name, u8 *leddc_on_time,
++			     u8 *leddc_off_time)
++{
++	char buf[100];
++	int err;
++	u32 val;
++
++	err = get_nvram_var(fill, NULL, name, buf, sizeof(buf));
++	if (err < 0)
++		return;
++	err = kstrtou32(strim(buf), 0, &val);
++	if (err) {
++		pr_warn("can not parse nvram name %s%s with value %s got %i\n",
++			fill->prefix, name, buf, err);
++		return;
++	}
++
++	if (val == 0xffff || val == 0xffffffff)
++		return;
++
++	*leddc_on_time = val & 0xff;
++	*leddc_off_time = (val >> 16) & 0xff;
++}
++
++static void nvram_read_macaddr(const struct bcm47xx_sprom_fill *fill,
++			       const char *name, u8 val[6])
++{
++	char buf[100];
++	int err;
++
++	err = get_nvram_var(fill, NULL, name, buf, sizeof(buf));
++	if (err < 0)
++		return;
++
++	bcm47xx_nvram_parse_macaddr(buf, val);
++}
++
++static void nvram_read_alpha2(const struct bcm47xx_sprom_fill *fill,
++			      const char *name, char val[2])
++{
++	char buf[10];
++	int err;
++
++	err = get_nvram_var(fill, NULL, name, buf, sizeof(buf));
++	if (err < 0)
++		return;
++	if (buf[0] == '0')
++		return;
++	if (strlen(buf) > 2) {
++		pr_warn("alpha2 is too long %s\n", buf);
++		return;
++	}
++	memcpy(val, buf, 2);
++}
++
++static void bcm47xx_sprom_fill_r1234589(struct ssb_sprom *sprom,
++					const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u16(fill, NULL, "devid", &sprom->dev_id, 0);
++	nvram_read_u8(fill, NULL, "ledbh0", &sprom->gpio0, 0xff);
++	nvram_read_u8(fill, NULL, "ledbh1", &sprom->gpio1, 0xff);
++	nvram_read_u8(fill, NULL, "ledbh2", &sprom->gpio2, 0xff);
++	nvram_read_u8(fill, NULL, "ledbh3", &sprom->gpio3, 0xff);
++	nvram_read_u8(fill, NULL, "aa2g", &sprom->ant_available_bg, 0);
++	nvram_read_u8(fill, NULL, "aa5g", &sprom->ant_available_a, 0);
++	nvram_read_s8(fill, NULL, "ag0", &sprom->antenna_gain.a0, 0);
++	nvram_read_s8(fill, NULL, "ag1", &sprom->antenna_gain.a1, 0);
++	nvram_read_alpha2(fill, "ccode", sprom->alpha2);
++}
++
++static void bcm47xx_sprom_fill_r12389(struct ssb_sprom *sprom,
++				      const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u16(fill, NULL, "pa0b0", &sprom->pa0b0, 0);
++	nvram_read_u16(fill, NULL, "pa0b1", &sprom->pa0b1, 0);
++	nvram_read_u16(fill, NULL, "pa0b2", &sprom->pa0b2, 0);
++	nvram_read_u8(fill, NULL, "pa0itssit", &sprom->itssi_bg, 0);
++	nvram_read_u8(fill, NULL, "pa0maxpwr", &sprom->maxpwr_bg, 0);
++	nvram_read_u16(fill, NULL, "pa1b0", &sprom->pa1b0, 0);
++	nvram_read_u16(fill, NULL, "pa1b1", &sprom->pa1b1, 0);
++	nvram_read_u16(fill, NULL, "pa1b2", &sprom->pa1b2, 0);
++	nvram_read_u8(fill, NULL, "pa1itssit", &sprom->itssi_a, 0);
++	nvram_read_u8(fill, NULL, "pa1maxpwr", &sprom->maxpwr_a, 0);
++}
++
++static void bcm47xx_sprom_fill_r1(struct ssb_sprom *sprom,
++				  const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u16(fill, NULL, "boardflags", &sprom->boardflags_lo, 0);
++	nvram_read_u8(fill, NULL, "cc", &sprom->country_code, 0);
++}
++
++static void bcm47xx_sprom_fill_r2389(struct ssb_sprom *sprom,
++				     const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u8(fill, NULL, "opo", &sprom->opo, 0);
++	nvram_read_u16(fill, NULL, "pa1lob0", &sprom->pa1lob0, 0);
++	nvram_read_u16(fill, NULL, "pa1lob1", &sprom->pa1lob1, 0);
++	nvram_read_u16(fill, NULL, "pa1lob2", &sprom->pa1lob2, 0);
++	nvram_read_u16(fill, NULL, "pa1hib0", &sprom->pa1hib0, 0);
++	nvram_read_u16(fill, NULL, "pa1hib1", &sprom->pa1hib1, 0);
++	nvram_read_u16(fill, NULL, "pa1hib2", &sprom->pa1hib2, 0);
++	nvram_read_u8(fill, NULL, "pa1lomaxpwr", &sprom->maxpwr_al, 0);
++	nvram_read_u8(fill, NULL, "pa1himaxpwr", &sprom->maxpwr_ah, 0);
++}
++
++static void bcm47xx_sprom_fill_r389(struct ssb_sprom *sprom,
++				    const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u8(fill, NULL, "bxa2g", &sprom->bxa2g, 0);
++	nvram_read_u8(fill, NULL, "rssisav2g", &sprom->rssisav2g, 0);
++	nvram_read_u8(fill, NULL, "rssismc2g", &sprom->rssismc2g, 0);
++	nvram_read_u8(fill, NULL, "rssismf2g", &sprom->rssismf2g, 0);
++	nvram_read_u8(fill, NULL, "bxa5g", &sprom->bxa5g, 0);
++	nvram_read_u8(fill, NULL, "rssisav5g", &sprom->rssisav5g, 0);
++	nvram_read_u8(fill, NULL, "rssismc5g", &sprom->rssismc5g, 0);
++	nvram_read_u8(fill, NULL, "rssismf5g", &sprom->rssismf5g, 0);
++	nvram_read_u8(fill, NULL, "tri2g", &sprom->tri2g, 0);
++	nvram_read_u8(fill, NULL, "tri5g", &sprom->tri5g, 0);
++	nvram_read_u8(fill, NULL, "tri5gl", &sprom->tri5gl, 0);
++	nvram_read_u8(fill, NULL, "tri5gh", &sprom->tri5gh, 0);
++	nvram_read_s8(fill, NULL, "rxpo2g", &sprom->rxpo2g, 0);
++	nvram_read_s8(fill, NULL, "rxpo5g", &sprom->rxpo5g, 0);
++}
++
++static void bcm47xx_sprom_fill_r3(struct ssb_sprom *sprom,
++				  const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u8(fill, NULL, "regrev", &sprom->regrev, 0);
++	nvram_read_leddc(fill, "leddc", &sprom->leddc_on_time,
++			 &sprom->leddc_off_time);
++}
++
++static void bcm47xx_sprom_fill_r4589(struct ssb_sprom *sprom,
++				     const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u8(fill, NULL, "regrev", &sprom->regrev, 0);
++	nvram_read_s8(fill, NULL, "ag2", &sprom->antenna_gain.a2, 0);
++	nvram_read_s8(fill, NULL, "ag3", &sprom->antenna_gain.a3, 0);
++	nvram_read_u8(fill, NULL, "txchain", &sprom->txchain, 0xf);
++	nvram_read_u8(fill, NULL, "rxchain", &sprom->rxchain, 0xf);
++	nvram_read_u8(fill, NULL, "antswitch", &sprom->antswitch, 0xff);
++	nvram_read_leddc(fill, "leddc", &sprom->leddc_on_time,
++			 &sprom->leddc_off_time);
++}
++
++static void bcm47xx_sprom_fill_r458(struct ssb_sprom *sprom,
++				    const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u16(fill, NULL, "cck2gpo", &sprom->cck2gpo, 0);
++	nvram_read_u32(fill, NULL, "ofdm2gpo", &sprom->ofdm2gpo, 0);
++	nvram_read_u32(fill, NULL, "ofdm5gpo", &sprom->ofdm5gpo, 0);
++	nvram_read_u32(fill, NULL, "ofdm5glpo", &sprom->ofdm5glpo, 0);
++	nvram_read_u32(fill, NULL, "ofdm5ghpo", &sprom->ofdm5ghpo, 0);
++	nvram_read_u16(fill, NULL, "cddpo", &sprom->cddpo, 0);
++	nvram_read_u16(fill, NULL, "stbcpo", &sprom->stbcpo, 0);
++	nvram_read_u16(fill, NULL, "bw40po", &sprom->bw40po, 0);
++	nvram_read_u16(fill, NULL, "bwduppo", &sprom->bwduppo, 0);
++	nvram_read_u16(fill, NULL, "mcs2gpo0", &sprom->mcs2gpo[0], 0);
++	nvram_read_u16(fill, NULL, "mcs2gpo1", &sprom->mcs2gpo[1], 0);
++	nvram_read_u16(fill, NULL, "mcs2gpo2", &sprom->mcs2gpo[2], 0);
++	nvram_read_u16(fill, NULL, "mcs2gpo3", &sprom->mcs2gpo[3], 0);
++	nvram_read_u16(fill, NULL, "mcs2gpo4", &sprom->mcs2gpo[4], 0);
++	nvram_read_u16(fill, NULL, "mcs2gpo5", &sprom->mcs2gpo[5], 0);
++	nvram_read_u16(fill, NULL, "mcs2gpo6", &sprom->mcs2gpo[6], 0);
++	nvram_read_u16(fill, NULL, "mcs2gpo7", &sprom->mcs2gpo[7], 0);
++	nvram_read_u16(fill, NULL, "mcs5gpo0", &sprom->mcs5gpo[0], 0);
++	nvram_read_u16(fill, NULL, "mcs5gpo1", &sprom->mcs5gpo[1], 0);
++	nvram_read_u16(fill, NULL, "mcs5gpo2", &sprom->mcs5gpo[2], 0);
++	nvram_read_u16(fill, NULL, "mcs5gpo3", &sprom->mcs5gpo[3], 0);
++	nvram_read_u16(fill, NULL, "mcs5gpo4", &sprom->mcs5gpo[4], 0);
++	nvram_read_u16(fill, NULL, "mcs5gpo5", &sprom->mcs5gpo[5], 0);
++	nvram_read_u16(fill, NULL, "mcs5gpo6", &sprom->mcs5gpo[6], 0);
++	nvram_read_u16(fill, NULL, "mcs5gpo7", &sprom->mcs5gpo[7], 0);
++	nvram_read_u16(fill, NULL, "mcs5glpo0", &sprom->mcs5glpo[0], 0);
++	nvram_read_u16(fill, NULL, "mcs5glpo1", &sprom->mcs5glpo[1], 0);
++	nvram_read_u16(fill, NULL, "mcs5glpo2", &sprom->mcs5glpo[2], 0);
++	nvram_read_u16(fill, NULL, "mcs5glpo3", &sprom->mcs5glpo[3], 0);
++	nvram_read_u16(fill, NULL, "mcs5glpo4", &sprom->mcs5glpo[4], 0);
++	nvram_read_u16(fill, NULL, "mcs5glpo5", &sprom->mcs5glpo[5], 0);
++	nvram_read_u16(fill, NULL, "mcs5glpo6", &sprom->mcs5glpo[6], 0);
++	nvram_read_u16(fill, NULL, "mcs5glpo7", &sprom->mcs5glpo[7], 0);
++	nvram_read_u16(fill, NULL, "mcs5ghpo0", &sprom->mcs5ghpo[0], 0);
++	nvram_read_u16(fill, NULL, "mcs5ghpo1", &sprom->mcs5ghpo[1], 0);
++	nvram_read_u16(fill, NULL, "mcs5ghpo2", &sprom->mcs5ghpo[2], 0);
++	nvram_read_u16(fill, NULL, "mcs5ghpo3", &sprom->mcs5ghpo[3], 0);
++	nvram_read_u16(fill, NULL, "mcs5ghpo4", &sprom->mcs5ghpo[4], 0);
++	nvram_read_u16(fill, NULL, "mcs5ghpo5", &sprom->mcs5ghpo[5], 0);
++	nvram_read_u16(fill, NULL, "mcs5ghpo6", &sprom->mcs5ghpo[6], 0);
++	nvram_read_u16(fill, NULL, "mcs5ghpo7", &sprom->mcs5ghpo[7], 0);
++}
++
++static void bcm47xx_sprom_fill_r45(struct ssb_sprom *sprom,
++				   const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u8(fill, NULL, "txpid2ga0", &sprom->txpid2g[0], 0);
++	nvram_read_u8(fill, NULL, "txpid2ga1", &sprom->txpid2g[1], 0);
++	nvram_read_u8(fill, NULL, "txpid2ga2", &sprom->txpid2g[2], 0);
++	nvram_read_u8(fill, NULL, "txpid2ga3", &sprom->txpid2g[3], 0);
++	nvram_read_u8(fill, NULL, "txpid5ga0", &sprom->txpid5g[0], 0);
++	nvram_read_u8(fill, NULL, "txpid5ga1", &sprom->txpid5g[1], 0);
++	nvram_read_u8(fill, NULL, "txpid5ga2", &sprom->txpid5g[2], 0);
++	nvram_read_u8(fill, NULL, "txpid5ga3", &sprom->txpid5g[3], 0);
++	nvram_read_u8(fill, NULL, "txpid5gla0", &sprom->txpid5gl[0], 0);
++	nvram_read_u8(fill, NULL, "txpid5gla1", &sprom->txpid5gl[1], 0);
++	nvram_read_u8(fill, NULL, "txpid5gla2", &sprom->txpid5gl[2], 0);
++	nvram_read_u8(fill, NULL, "txpid5gla3", &sprom->txpid5gl[3], 0);
++	nvram_read_u8(fill, NULL, "txpid5gha0", &sprom->txpid5gh[0], 0);
++	nvram_read_u8(fill, NULL, "txpid5gha1", &sprom->txpid5gh[1], 0);
++	nvram_read_u8(fill, NULL, "txpid5gha2", &sprom->txpid5gh[2], 0);
++	nvram_read_u8(fill, NULL, "txpid5gha3", &sprom->txpid5gh[3], 0);
++}
++
++static void bcm47xx_sprom_fill_r89(struct ssb_sprom *sprom,
++				   const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u8(fill, NULL, "tssipos2g", &sprom->fem.ghz2.tssipos, 0);
++	nvram_read_u8(fill, NULL, "extpagain2g", &sprom->fem.ghz2.extpa_gain, 0);
++	nvram_read_u8(fill, NULL, "pdetrange2g", &sprom->fem.ghz2.pdet_range, 0);
++	nvram_read_u8(fill, NULL, "triso2g", &sprom->fem.ghz2.tr_iso, 0);
++	nvram_read_u8(fill, NULL, "antswctl2g", &sprom->fem.ghz2.antswlut, 0);
++	nvram_read_u8(fill, NULL, "tssipos5g", &sprom->fem.ghz5.tssipos, 0);
++	nvram_read_u8(fill, NULL, "extpagain5g", &sprom->fem.ghz5.extpa_gain, 0);
++	nvram_read_u8(fill, NULL, "pdetrange5g", &sprom->fem.ghz5.pdet_range, 0);
++	nvram_read_u8(fill, NULL, "triso5g", &sprom->fem.ghz5.tr_iso, 0);
++	nvram_read_u8(fill, NULL, "antswctl5g", &sprom->fem.ghz5.antswlut, 0);
++	nvram_read_u8(fill, NULL, "tempthresh", &sprom->tempthresh, 0);
++	nvram_read_u8(fill, NULL, "tempoffset", &sprom->tempoffset, 0);
++	nvram_read_u16(fill, NULL, "rawtempsense", &sprom->rawtempsense, 0);
++	nvram_read_u8(fill, NULL, "measpower", &sprom->measpower, 0);
++	nvram_read_u8(fill, NULL, "tempsense_slope", &sprom->tempsense_slope, 0);
++	nvram_read_u8(fill, NULL, "tempcorrx", &sprom->tempcorrx, 0);
++	nvram_read_u8(fill, NULL, "tempsense_option", &sprom->tempsense_option, 0);
++	nvram_read_u8(fill, NULL, "freqoffset_corr", &sprom->freqoffset_corr, 0);
++	nvram_read_u8(fill, NULL, "iqcal_swp_dis", &sprom->iqcal_swp_dis, 0);
++	nvram_read_u8(fill, NULL, "hw_iqcal_en", &sprom->hw_iqcal_en, 0);
++	nvram_read_u8(fill, NULL, "elna2g", &sprom->elna2g, 0);
++	nvram_read_u8(fill, NULL, "elna5g", &sprom->elna5g, 0);
++	nvram_read_u8(fill, NULL, "phycal_tempdelta", &sprom->phycal_tempdelta, 0);
++	nvram_read_u8(fill, NULL, "temps_period", &sprom->temps_period, 0);
++	nvram_read_u8(fill, NULL, "temps_hysteresis", &sprom->temps_hysteresis, 0);
++	nvram_read_u8(fill, NULL, "measpower1", &sprom->measpower1, 0);
++	nvram_read_u8(fill, NULL, "measpower2", &sprom->measpower2, 0);
++	nvram_read_u8(fill, NULL, "rxgainerr2ga0", &sprom->rxgainerr2ga[0], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr2ga1", &sprom->rxgainerr2ga[1], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr2ga2", &sprom->rxgainerr2ga[2], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gla0", &sprom->rxgainerr5gla[0], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gla1", &sprom->rxgainerr5gla[1], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gla2", &sprom->rxgainerr5gla[2], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gma0", &sprom->rxgainerr5gma[0], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gma1", &sprom->rxgainerr5gma[1], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gma2", &sprom->rxgainerr5gma[2], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gha0", &sprom->rxgainerr5gha[0], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gha1", &sprom->rxgainerr5gha[1], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gha2", &sprom->rxgainerr5gha[2], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gua0", &sprom->rxgainerr5gua[0], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gua1", &sprom->rxgainerr5gua[1], 0);
++	nvram_read_u8(fill, NULL, "rxgainerr5gua2", &sprom->rxgainerr5gua[2], 0);
++	nvram_read_u8(fill, NULL, "noiselvl2ga0", &sprom->noiselvl2ga[0], 0);
++	nvram_read_u8(fill, NULL, "noiselvl2ga1", &sprom->noiselvl2ga[1], 0);
++	nvram_read_u8(fill, NULL, "noiselvl2ga2", &sprom->noiselvl2ga[2], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gla0", &sprom->noiselvl5gla[0], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gla1", &sprom->noiselvl5gla[1], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gla2", &sprom->noiselvl5gla[2], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gma0", &sprom->noiselvl5gma[0], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gma1", &sprom->noiselvl5gma[1], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gma2", &sprom->noiselvl5gma[2], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gha0", &sprom->noiselvl5gha[0], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gha1", &sprom->noiselvl5gha[1], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gha2", &sprom->noiselvl5gha[2], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gua0", &sprom->noiselvl5gua[0], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gua1", &sprom->noiselvl5gua[1], 0);
++	nvram_read_u8(fill, NULL, "noiselvl5gua2", &sprom->noiselvl5gua[2], 0);
++	nvram_read_u8(fill, NULL, "pcieingress_war", &sprom->pcieingress_war, 0);
++}
++
++static void bcm47xx_sprom_fill_r9(struct ssb_sprom *sprom,
++				  const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u16(fill, NULL, "cckbw202gpo", &sprom->cckbw202gpo, 0);
++	nvram_read_u16(fill, NULL, "cckbw20ul2gpo", &sprom->cckbw20ul2gpo, 0);
++	nvram_read_u32(fill, NULL, "legofdmbw202gpo", &sprom->legofdmbw202gpo, 0);
++	nvram_read_u32(fill, NULL, "legofdmbw20ul2gpo", &sprom->legofdmbw20ul2gpo, 0);
++	nvram_read_u32(fill, NULL, "legofdmbw205glpo", &sprom->legofdmbw205glpo, 0);
++	nvram_read_u32(fill, NULL, "legofdmbw20ul5glpo", &sprom->legofdmbw20ul5glpo, 0);
++	nvram_read_u32(fill, NULL, "legofdmbw205gmpo", &sprom->legofdmbw205gmpo, 0);
++	nvram_read_u32(fill, NULL, "legofdmbw20ul5gmpo", &sprom->legofdmbw20ul5gmpo, 0);
++	nvram_read_u32(fill, NULL, "legofdmbw205ghpo", &sprom->legofdmbw205ghpo, 0);
++	nvram_read_u32(fill, NULL, "legofdmbw20ul5ghpo", &sprom->legofdmbw20ul5ghpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw202gpo", &sprom->mcsbw202gpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw20ul2gpo", &sprom->mcsbw20ul2gpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw402gpo", &sprom->mcsbw402gpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw205glpo", &sprom->mcsbw205glpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw20ul5glpo", &sprom->mcsbw20ul5glpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw405glpo", &sprom->mcsbw405glpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw205gmpo", &sprom->mcsbw205gmpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw20ul5gmpo", &sprom->mcsbw20ul5gmpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw405gmpo", &sprom->mcsbw405gmpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw205ghpo", &sprom->mcsbw205ghpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw20ul5ghpo", &sprom->mcsbw20ul5ghpo, 0);
++	nvram_read_u32(fill, NULL, "mcsbw405ghpo", &sprom->mcsbw405ghpo, 0);
++	nvram_read_u16(fill, NULL, "mcs32po", &sprom->mcs32po, 0);
++	nvram_read_u16(fill, NULL, "legofdm40duppo", &sprom->legofdm40duppo, 0);
++	nvram_read_u8(fill, NULL, "sar2g", &sprom->sar2g, 0);
++	nvram_read_u8(fill, NULL, "sar5g", &sprom->sar5g, 0);
++}
++
++static void bcm47xx_sprom_fill_path_r4589(struct ssb_sprom *sprom,
++					  const struct bcm47xx_sprom_fill *fill)
++{
++	char postfix[2];
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) {
++		struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i];
++
++		snprintf(postfix, sizeof(postfix), "%i", i);
++		nvram_read_u8(fill, postfix, "maxp2ga", &pwr_info->maxpwr_2g, 0);
++		nvram_read_u8(fill, postfix, "itt2ga", &pwr_info->itssi_2g, 0);
++		nvram_read_u8(fill, postfix, "itt5ga", &pwr_info->itssi_5g, 0);
++		nvram_read_u16(fill, postfix, "pa2gw0a", &pwr_info->pa_2g[0], 0);
++		nvram_read_u16(fill, postfix, "pa2gw1a", &pwr_info->pa_2g[1], 0);
++		nvram_read_u16(fill, postfix, "pa2gw2a", &pwr_info->pa_2g[2], 0);
++		nvram_read_u8(fill, postfix, "maxp5ga", &pwr_info->maxpwr_5g, 0);
++		nvram_read_u8(fill, postfix, "maxp5gha", &pwr_info->maxpwr_5gh, 0);
++		nvram_read_u8(fill, postfix, "maxp5gla", &pwr_info->maxpwr_5gl, 0);
++		nvram_read_u16(fill, postfix, "pa5gw0a", &pwr_info->pa_5g[0], 0);
++		nvram_read_u16(fill, postfix, "pa5gw1a", &pwr_info->pa_5g[1], 0);
++		nvram_read_u16(fill, postfix, "pa5gw2a", &pwr_info->pa_5g[2], 0);
++		nvram_read_u16(fill, postfix, "pa5glw0a", &pwr_info->pa_5gl[0], 0);
++		nvram_read_u16(fill, postfix, "pa5glw1a", &pwr_info->pa_5gl[1], 0);
++		nvram_read_u16(fill, postfix, "pa5glw2a", &pwr_info->pa_5gl[2], 0);
++		nvram_read_u16(fill, postfix, "pa5ghw0a", &pwr_info->pa_5gh[0], 0);
++		nvram_read_u16(fill, postfix, "pa5ghw1a", &pwr_info->pa_5gh[1], 0);
++		nvram_read_u16(fill, postfix, "pa5ghw2a", &pwr_info->pa_5gh[2], 0);
++	}
++}
++
++static void bcm47xx_sprom_fill_path_r45(struct ssb_sprom *sprom,
++					const struct bcm47xx_sprom_fill *fill)
++{
++	char postfix[2];
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) {
++		struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i];
++
++		snprintf(postfix, sizeof(postfix), "%i", i);
++		nvram_read_u16(fill, postfix, "pa2gw3a", &pwr_info->pa_2g[3], 0);
++		nvram_read_u16(fill, postfix, "pa5gw3a", &pwr_info->pa_5g[3], 0);
++		nvram_read_u16(fill, postfix, "pa5glw3a", &pwr_info->pa_5gl[3], 0);
++		nvram_read_u16(fill, postfix, "pa5ghw3a", &pwr_info->pa_5gh[3], 0);
++	}
++}
++
++static bool bcm47xx_is_valid_mac(u8 *mac)
++{
++	return mac && !(mac[0] == 0x00 && mac[1] == 0x90 && mac[2] == 0x4c);
++}
++
++static int bcm47xx_increase_mac_addr(u8 *mac, u8 num)
++{
++	u8 *oui = mac + ETH_ALEN/2 - 1;
++	u8 *p = mac + ETH_ALEN - 1;
++
++	do {
++		(*p) += num;
++		if (*p > num)
++			break;
++		p--;
++		num = 1;
++	} while (p != oui);
++
++	if (p == oui) {
++		pr_err("unable to fetch mac address\n");
++		return -ENOENT;
++	}
++	return 0;
++}
++
++/*
++ * This is a global counter because different instances of sprom will
++ * access the same nvram.
++ */
++static int mac_addr_used = 2;
++
++static void bcm47xx_sprom_fill_ethernet(struct ssb_sprom *sprom,
++					const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_macaddr(fill, "et0macaddr", sprom->et0mac);
++	nvram_read_u8(fill, NULL, "et0mdcport", &sprom->et0mdcport, 0);
++	nvram_read_u8(fill, NULL, "et0phyaddr", &sprom->et0phyaddr, 0);
++
++	nvram_read_macaddr(fill, "et1macaddr", sprom->et1mac);
++	nvram_read_u8(fill, NULL, "et1mdcport", &sprom->et1mdcport, 0);
++	nvram_read_u8(fill, NULL, "et1phyaddr", &sprom->et1phyaddr, 0);
++
++	nvram_read_macaddr(fill, "macaddr", sprom->il0mac);
++	nvram_read_macaddr(fill, "il0macaddr", sprom->il0mac);
++
++	/*
++	 * The address prefix 00:90:4C is used by Broadcom in their initial
++	 * configuration. When a mac address with the prefix 00:90:4C is used
++	 * all devices from the same series are sharing the same mac address.
++	 * To prevent mac address collisions we replace them with a mac address
++	 * based on the base address.
++	 */
++	if (!bcm47xx_is_valid_mac(sprom->il0mac)) {
++		u8 mac[6];
++		struct bcm47xx_sprom_fill fill_no_prefix;
++
++		memcpy(&fill_no_prefix, fill, sizeof(fill_no_prefix));
++		fill_no_prefix.prefix = NULL;
++
++		nvram_read_macaddr(&fill_no_prefix, "et0macaddr", mac);
++		if (bcm47xx_is_valid_mac(mac)) {
++			int err = bcm47xx_increase_mac_addr(mac, mac_addr_used);
++
++			if (!err) {
++				ether_addr_copy(sprom->il0mac, mac);
++				mac_addr_used++;
++			}
++		}
++	}
++}
++
++static void bcm47xx_sprom_fill_board_data(struct ssb_sprom *sprom,
++					  const struct bcm47xx_sprom_fill *fill)
++{
++	nvram_read_u16(fill, NULL, "boardrev", &sprom->board_rev, 0);
++	nvram_read_u16(fill, NULL, "boardnum", &sprom->board_num, 0);
++	nvram_read_u16(fill, NULL, "boardtype", &sprom->board_type, 0);
++	nvram_read_u32_2(fill, "boardflags", &sprom->boardflags_lo,
++			 &sprom->boardflags_hi);
++	nvram_read_u32_2(fill, "boardflags2", &sprom->boardflags2_lo,
++			 &sprom->boardflags2_hi);
++}
++
++static void bcm47xx_sprom_fill(struct ssb_sprom *sprom,
++			       const struct bcm47xx_sprom_fill *fill)
++{
++	bcm47xx_sprom_fill_ethernet(sprom, fill);
++	bcm47xx_sprom_fill_board_data(sprom, fill);
++
++	nvram_read_u8(fill, NULL, "sromrev", &sprom->revision, 0);
++
++	switch (sprom->revision) {
++	case 1:
++		bcm47xx_sprom_fill_r1234589(sprom, fill);
++		bcm47xx_sprom_fill_r12389(sprom, fill);
++		bcm47xx_sprom_fill_r1(sprom, fill);
++		break;
++	case 2:
++		bcm47xx_sprom_fill_r1234589(sprom, fill);
++		bcm47xx_sprom_fill_r12389(sprom, fill);
++		bcm47xx_sprom_fill_r2389(sprom, fill);
++		break;
++	case 3:
++		bcm47xx_sprom_fill_r1234589(sprom, fill);
++		bcm47xx_sprom_fill_r12389(sprom, fill);
++		bcm47xx_sprom_fill_r2389(sprom, fill);
++		bcm47xx_sprom_fill_r389(sprom, fill);
++		bcm47xx_sprom_fill_r3(sprom, fill);
++		break;
++	case 4:
++	case 5:
++		bcm47xx_sprom_fill_r1234589(sprom, fill);
++		bcm47xx_sprom_fill_r4589(sprom, fill);
++		bcm47xx_sprom_fill_r458(sprom, fill);
++		bcm47xx_sprom_fill_r45(sprom, fill);
++		bcm47xx_sprom_fill_path_r4589(sprom, fill);
++		bcm47xx_sprom_fill_path_r45(sprom, fill);
++		break;
++	case 8:
++		bcm47xx_sprom_fill_r1234589(sprom, fill);
++		bcm47xx_sprom_fill_r12389(sprom, fill);
++		bcm47xx_sprom_fill_r2389(sprom, fill);
++		bcm47xx_sprom_fill_r389(sprom, fill);
++		bcm47xx_sprom_fill_r4589(sprom, fill);
++		bcm47xx_sprom_fill_r458(sprom, fill);
++		bcm47xx_sprom_fill_r89(sprom, fill);
++		bcm47xx_sprom_fill_path_r4589(sprom, fill);
++		break;
++	case 9:
++		bcm47xx_sprom_fill_r1234589(sprom, fill);
++		bcm47xx_sprom_fill_r12389(sprom, fill);
++		bcm47xx_sprom_fill_r2389(sprom, fill);
++		bcm47xx_sprom_fill_r389(sprom, fill);
++		bcm47xx_sprom_fill_r4589(sprom, fill);
++		bcm47xx_sprom_fill_r89(sprom, fill);
++		bcm47xx_sprom_fill_r9(sprom, fill);
++		bcm47xx_sprom_fill_path_r4589(sprom, fill);
++		break;
++	default:
++		pr_warn("Unsupported SPROM revision %d detected. Will extract v1\n",
++			sprom->revision);
++		sprom->revision = 1;
++		bcm47xx_sprom_fill_r1234589(sprom, fill);
++		bcm47xx_sprom_fill_r12389(sprom, fill);
++		bcm47xx_sprom_fill_r1(sprom, fill);
++	}
++}
++
++static int bcm47xx_sprom_getenv(const struct bcm47xx_sprom_fill *fill,
++				const char *name, char *val, size_t val_len)
++{
++	const struct platform_device *nvram_dev = fill->priv;
++
++	return bcm47xx_nvram_getenv(&nvram_dev->dev, name, val, val_len);
++};
++
++static int bcm47xx_sprom_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *np = dev->of_node;
++	struct ssb_sprom *sprom;
++	const __be32 *handle;
++	struct device_node *nvram_node;
++	struct platform_device *nvram_dev;
++	struct bcm47xx_sprom_fill fill;
++
++	/* Alloc */
++	sprom = devm_kzalloc(dev, sizeof(*sprom), GFP_KERNEL);
++	if (!sprom)
++		return -ENOMEM;
++
++	handle = of_get_property(np, "nvram", NULL);
++	if (!handle)
++		return -ENOMEM;
++
++	nvram_node = of_find_node_by_phandle(be32_to_cpup(handle));
++	if (!nvram_node)
++		return -ENOMEM;
++
++	nvram_dev = of_find_device_by_node(nvram_node);
++	if (!nvram_dev)
++		return -ENOMEM;
++
++	fill.prefix = of_get_property(np, "prefix", NULL);
++
++	fill.fallback = false;
++	fill.getenv = bcm47xx_sprom_getenv;
++	fill.priv = nvram_dev;
++
++	bcm47xx_sprom_fill(sprom, &fill);
++
++	platform_set_drvdata(pdev, sprom);
++
++	return 0;
++}
++
++static const struct of_device_id bcm47xx_sprom_of_match_table[] = {
++	{ .compatible = "brcm,bcm47xx-sprom", },
++	{},
++};
++MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table);
++
++static struct platform_driver bcm47xx_sprom_driver = {
++	.driver = {
++		.owner = THIS_MODULE,
++		.name = "bcm47xx-sprom",
++		.of_match_table = bcm47xx_sprom_of_match_table,
++		/* driver unloading/unbinding currently not supported */
++		.suppress_bind_attrs = true,
++	},
++	.probe = bcm47xx_sprom_probe,
++};
++module_platform_driver(bcm47xx_sprom_driver);
++
++MODULE_AUTHOR("Hauke Mehrtens <[email protected]>");
++MODULE_LICENSE("GPL v2");

+ 38 - 0
target/linux/bcm53xx/patches-3.18/121-bcma-get-irqs-from-dt.patch

@@ -0,0 +1,38 @@
+From 6611afa6c49434780096cdf2c1028f0ac277f9bc Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Thu, 9 Jan 2014 19:40:14 +0100
+Subject: [PATCH v3 2/2] bcma: get IRQ numbers from dt
+
+It is not possible to auto detect the irq numbers used by the cores on
+an arm SoC. If bcma was registered with device tree it will search for
+some device tree nodes with the irq number and add it to the core
+configuration.
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ drivers/bcma/main.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 48 insertions(+), 1 deletion(-)
+ 
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -10,6 +10,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/bcma/bcma.h>
+ #include <linux/slab.h>
++#include <linux/of_irq.h>
+ #include <linux/of_address.h>
+ 
+ MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
+@@ -159,8 +160,10 @@ static void bcma_of_fill_device(struct p
+ 	struct device_node *node;
+ 
+ 	node = bcma_of_find_child_device(parent, core);
+-	if (node)
+-		core->dev.of_node = node;
++	if (!node)
++		return;
++	core->dev.of_node = node;
++	core->irq = irq_of_parse_and_map(node, 0);
+ }
+ #else
+ static void bcma_of_fill_device(struct platform_device *parent,

+ 65 - 0
target/linux/bcm53xx/patches-3.18/122-bcma-fill-core-details-for-every-device.patch

@@ -0,0 +1,65 @@
+From 487b997353e2e3afe9c452b20ff5e4320d76e9c3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Thu, 2 Oct 2014 12:28:54 +0200
+Subject: [PATCH][RFC] bcma: fill core details for every device
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We were setting things like dma_dev, IRQ, etc. during core registration
+only. We need such info for cores handled internally (e.g. ChipCommon)
+as well.
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ drivers/bcma/bcma_private.h | 1 +
+ drivers/bcma/main.c         | 9 ++++++---
+ drivers/bcma/scan.c         | 1 +
+ 3 files changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/bcma/bcma_private.h
++++ b/drivers/bcma/bcma_private.h
+@@ -24,6 +24,7 @@ struct bcma_bus;
+ /* main.c */
+ bool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value,
+ 		     int timeout);
++void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core);
+ int bcma_bus_register(struct bcma_bus *bus);
+ void bcma_bus_unregister(struct bcma_bus *bus);
+ int __init bcma_bus_early_register(struct bcma_bus *bus,
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -172,10 +172,8 @@ static void bcma_of_fill_device(struct p
+ }
+ #endif /* CONFIG_OF */
+ 
+-static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
++void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
+ {
+-	int err;
+-
+ 	core->dev.release = bcma_release_core_dev;
+ 	core->dev.bus = &bcma_bus_type;
+ 	dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
+@@ -199,6 +197,11 @@ static void bcma_register_core(struct bc
+ 	case BCMA_HOSTTYPE_SDIO:
+ 		break;
+ 	}
++}
++
++static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
++{
++	int err;
+ 
+ 	err = device_register(&core->dev);
+ 	if (err) {
+--- a/drivers/bcma/scan.c
++++ b/drivers/bcma/scan.c
+@@ -505,6 +505,7 @@ int bcma_bus_scan(struct bcma_bus *bus)
+ 		bus->nr_cores++;
+ 		other_core = bcma_find_core_reverse(bus, core->id.id);
+ 		core->core_unit = (other_core == NULL) ? 0 : other_core->core_unit + 1;
++		bcma_prepare_core(bus, core);
+ 
+ 		bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
+ 			  core->core_index, bcma_device_name(&core->id),

+ 88 - 0
target/linux/bcm53xx/patches-3.18/123-bcma-get-sprom-from-devicetree.patch

@@ -0,0 +1,88 @@
+From bd9106f5907080b467026bdaaea979fac8c7badb Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Sun, 4 May 2014 14:34:31 +0200
+Subject: [PATCH 06/17] bcma: get sprom from devicetree
+
+This patch make it possible to device an sprom provider in device tree
+and get the sprom from this driver. Every time there is such a provider
+it gets asked for a sprom.
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ drivers/bcma/sprom.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 50 insertions(+), 1 deletion(-)
+
+--- a/drivers/bcma/sprom.c
++++ b/drivers/bcma/sprom.c
+@@ -15,6 +15,8 @@
+ #include <linux/io.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/slab.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
+ 
+ static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out);
+ 
+@@ -46,6 +48,46 @@ int bcma_arch_register_fallback_sprom(in
+ 	return 0;
+ }
+ 
++#ifdef CONFIG_OF
++static int bcma_fill_sprom_with_dt(struct bcma_bus *bus,
++				   struct ssb_sprom *out)
++{
++	const __be32 *handle;
++	struct device_node *sprom_node;
++	struct platform_device *sprom_dev;
++	struct ssb_sprom *sprom;
++
++	if (!bus->host_pdev || !bus->host_pdev->dev.of_node)
++		return -ENOENT;
++
++	handle = of_get_property(bus->host_pdev->dev.of_node, "sprom", NULL);
++	if (!handle)
++		return -ENOENT;
++
++	sprom_node = of_find_node_by_phandle(be32_to_cpup(handle));
++	if (!sprom_node)
++		return -ENOENT;
++
++	sprom_dev = of_find_device_by_node(sprom_node);
++	if (!sprom_dev)
++		return -ENOENT;
++
++	sprom = platform_get_drvdata(sprom_dev);
++	if (!sprom)
++		return -ENOENT;
++
++	memcpy(out, sprom, sizeof(*out));
++
++	return 0;
++}
++#else
++static int bcma_fill_sprom_with_dt(struct bcma_bus *bus,
++				   struct ssb_sprom *out)
++{
++	return -ENOENT;
++}
++#endif
++
+ static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus,
+ 					 struct ssb_sprom *out)
+ {
+@@ -580,7 +622,14 @@ int bcma_sprom_get(struct bcma_bus *bus)
+ 	u16 *sprom;
+ 	size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
+ 				 SSB_SPROMSIZE_WORDS_R10, };
+-	int i, err = 0;
++	int i, err;
++
++	err = bcma_fill_sprom_with_dt(bus, &bus->sprom);
++	if (err == 0) {
++		bcma_info(bus, "Found sprom from device tree provider\n");
++		return 0;
++	}
++	err = 0;
+ 
+ 	if (!bus->drv_cc.core)
+ 		return -EOPNOTSUPP;

+ 114 - 0
target/linux/bcm53xx/patches-3.18/130-ARM-BCM5301X-register-bcma-bus.patch

@@ -0,0 +1,114 @@
+From 414f0ad9b3a8e8ee6eaf09c6d79d5f448ac28630 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Sat, 25 Jan 2014 17:03:07 +0100
+Subject: [PATCH 07/17] ARM: BCM5301X: register bcma bus
+
+---
+ arch/arm/boot/dts/bcm4708.dtsi | 58 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 58 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm5301x.dtsi
++++ b/arch/arm/boot/dts/bcm5301x.dtsi
+@@ -94,18 +94,102 @@
+ 		};
+ 	};
+ 
++	nvram0: nvram@1c000000 {
++		compatible = "brcm,bcm47xx-nvram";
++		reg = <0x1c000000 0x01000000>;
++	};
++
++	sprom0: sprom@0 {
++		compatible = "brcm,bcm47xx-sprom";
++		nvram = <&nvram0>;
++	};
++
+ 	axi@18000000 {
+ 		compatible = "brcm,bus-axi";
+ 		reg = <0x18000000 0x1000>;
+ 		ranges = <0x00000000 0x18000000 0x00100000>;
+ 		#address-cells = <1>;
+ 		#size-cells = <1>;
++		sprom = <&sprom0>;
+ 
+ 		chipcommon: chipcommon@0 {
+ 			reg = <0x00000000 0x1000>;
++			interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+ 
+ 			gpio-controller;
+ 			#gpio-cells = <2>;
+ 		};
++
++		pcie@12000 {
++			reg = <0x00012000 0x1000>;
++			interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		pcie@13000 {
++			reg = <0x00013000 0x1000>;
++			interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		pcie@14000 {
++			reg = <0x00014000 0x1000>;
++			interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		usb2@21000 {
++			reg = <0x00021000 0x1000>;
++			interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		usb3@23000 {
++			reg = <0x00023000 0x1000>;
++			interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		ethernet@24000 {
++			reg = <0x00024000 0x1000>;
++			interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		ethernet@25000 {
++			reg = <0x00025000 0x1000>;
++			interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		ethernet@26000 {
++			reg = <0x00026000 0x1000>;
++			interrupts = <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		ethernet@27000 {
++			reg = <0x00027000 0x1000>;
++			interrupts = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>;
++		};
++
++		nand@28000 {
++			reg = <0x00028000 0x1000>;
++			interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
++		};
+ 	};
+ };

+ 69 - 0
target/linux/bcm53xx/patches-3.18/131-ARM-BCM5301X-add-restart-support.patch

@@ -0,0 +1,69 @@
+From 28b11a8b1258214b3b5d58bb6e3bbcb0fc9fd4fe Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Thu, 31 Jul 2014 07:28:05 +0200
+Subject: [PATCH] ARM: BCM5301X: add restart support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/mach-bcm/bcm_5301x.c | 31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+--- a/arch/arm/mach-bcm/bcm_5301x.c
++++ b/arch/arm/mach-bcm/bcm_5301x.c
+@@ -12,9 +12,26 @@
+ #include <asm/siginfo.h>
+ #include <asm/signal.h>
+ 
++#include <linux/bcma/bcma.h>
+ 
+ static bool first_fault = true;
+ 
++static struct bcma_bus *bcm5301x_get_bcma_bus(void)
++{
++	struct device_node *np;
++	struct platform_device *pdev;
++
++	np = of_find_compatible_node(NULL, NULL, "brcm,bus-axi");
++	if (!np)
++		return NULL;
++
++	pdev = of_find_device_by_node(np);
++	if (!pdev)
++		return NULL;
++
++	return platform_get_drvdata(pdev);
++}
++
+ static int bcm5301x_abort_handler(unsigned long addr, unsigned int fsr,
+ 				 struct pt_regs *regs)
+ {
+@@ -43,6 +60,19 @@ static void __init bcm5301x_init_early(v
+ 			"imprecise external abort");
+ }
+ 
++static void bcm5301x_restart(enum reboot_mode mode, const char *cmd)
++{
++	struct bcma_bus *bus = bcm5301x_get_bcma_bus();
++
++	if (bus)
++		bcma_chipco_watchdog_timer_set(&bus->drv_cc, 1);
++	else
++		pr_warn("Unable to access bcma bus\n");
++
++	while (1)
++		;
++}
++
+ static const char __initconst *bcm5301x_dt_compat[] = {
+ 	"brcm,bcm4708",
+ 	NULL,
+@@ -52,5 +82,6 @@ DT_MACHINE_START(BCM5301X, "BCM5301X")
+ 	.l2c_aux_val	= 0,
+ 	.l2c_aux_mask	= ~0,
+ 	.init_early	= bcm5301x_init_early,
++	.restart	= bcm5301x_restart,
+ 	.dt_compat	= bcm5301x_dt_compat,
+ MACHINE_END

+ 29 - 0
target/linux/bcm53xx/patches-3.18/150-pci-do-not-probe-too-early.patch

@@ -0,0 +1,29 @@
+From cf72936c001056de1cfcb27dd9a232f5484ec59c Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Thu, 29 May 2014 20:54:15 +0200
+Subject: [PATCH 12/17] pci: do not probe too early
+
+Probing is done before the PCIe bridge is fully activated and the
+address spaces does not get assigned to the PCIe devices. Without the
+address space the driver can not register to this device. With this
+patch the driver reregistration is done later.
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ drivers/pci/probe.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/probe.c
++++ b/drivers/pci/probe.c
+@@ -2093,7 +2093,10 @@ struct pci_bus *pci_scan_root_bus(struct
+ 	if (!found)
+ 		pci_bus_update_busn_res_end(b, max);
+ 
+-	pci_bus_add_devices(b);
++	/* this should be done in arch/arm/kernel/bios32.c, because the
++	   resources for the PCI devices are initilized later and doing
++	   it here will fail. */
++	/* pci_bus_add_devices(b); */
+ 	return b;
+ }
+ EXPORT_SYMBOL(pci_scan_root_bus);

+ 670 - 0
target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch

@@ -0,0 +1,670 @@
+From cc2cda651fcbc498bf513a6b802dca19944bcb37 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Mon, 12 May 2014 11:55:20 +0200
+Subject: [PATCH 13/17] pcie2-bcma: add new PCIe2 driver for bcma
+
+This driver supports the PCIe controller found on the BCM4708 and
+similar SoCs. The controller itself is automatically detected by bcma.
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ arch/arm/mach-bcm/Kconfig     |   2 +
+ drivers/pci/host/Kconfig      |   7 +
+ drivers/pci/host/Makefile     |   1 +
+ drivers/pci/host/pcie2-bcma.c | 591 ++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 601 insertions(+)
+ create mode 100644 drivers/pci/host/pcie2-bcma.c
+
+--- a/arch/arm/mach-bcm/Kconfig
++++ b/arch/arm/mach-bcm/Kconfig
+@@ -86,6 +86,7 @@ config ARCH_BCM_5301X
+ 	select HAVE_ARM_TWD if SMP
+ 	select ARM_GLOBAL_TIMER
+ 	select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
++	select PCI_DOMAINS if PCI
+ 	help
+ 	  Support for Broadcom BCM470X and BCM5301X SoCs with ARM CPU cores.
+ 
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -91,4 +91,11 @@ config PCI_XGENE
+ 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
+ 	  and have varied lanes from x1 to x8.
+ 
++config PCI_BCMA
++	bool "BCMA PCIe2 host controller"
++	depends on BCMA && OF
++	help
++	  Say Y here if you want to support a simple generic PCI host
++	  controller, such as the one emulated by kvmtool.
++
+ endmenu
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -11,3 +11,4 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spe
+ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
+ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
+ obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
++obj-$(CONFIG_PCI_BCMA) += pcie2-bcma.o
+--- /dev/null
++++ b/drivers/pci/host/pcie2-bcma.c
+@@ -0,0 +1,619 @@
++/*
++ * Northstar PCI-Express driver
++ * Only supports Root-Complex (RC) mode
++ *
++ * Notes:
++ * PCI Domains are being used to identify the PCIe port 1:1.
++ *
++ * Only MEM access is supported, PAX does not support IO.
++ *
++ * TODO:
++ *	MSI interrupts,
++ *	DRAM > 128 MBytes (e.g. DMA zones)
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/bug.h>
++#include <linux/delay.h>
++#include <linux/pci.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/bcma/bcma.h>
++
++#define SI_ENUM_BASE		0x18000000	/* Enumeration space base */
++
++/*
++ * Register offset definitions
++ */
++#define	SOC_PCIE_CONTROL	0x000	/* a.k.a. CLK_CONTROL reg */
++#define	SOC_PCIE_PM_STATUS	0x008
++#define	SOC_PCIE_PM_CONTROL	0x00c	/* in EP mode only ! */
++
++#define	SOC_PCIE_EXT_CFG_ADDR	0x120
++#define	SOC_PCIE_EXT_CFG_DATA	0x124
++#define	SOC_PCIE_CFG_ADDR	0x1f8
++#define	SOC_PCIE_CFG_DATA	0x1fc
++
++#define	SOC_PCIE_SYS_RC_INTX_EN		0x330
++#define	SOC_PCIE_SYS_RC_INTX_CSR	0x334
++#define	SOC_PCIE_SYS_HOST_INTR_EN	0x344
++#define	SOC_PCIE_SYS_HOST_INTR_CSR	0x348
++
++#define	SOC_PCIE_HDR_OFF	0x400	/* 256 bytes per function */
++
++/* 32-bit 4KB in-bound mapping windows for Function 0..3, n=0..7 */
++#define	SOC_PCIE_SYS_IMAP0(f, n)	(0xc00 + ((f) << 9)((n) << 2))
++/* 64-bit in-bound mapping windows for func 0..3 */
++#define	SOC_PCIE_SYS_IMAP1(f)		(0xc80 + ((f) << 3))
++#define	SOC_PCIE_SYS_IMAP2(f)		(0xcc0 + ((f) << 3))
++/* 64-bit in-bound address range n=0..2 */
++#define	SOC_PCIE_SYS_IARR(n)		(0xd00 + ((n) << 3))
++/* 64-bit out-bound address filter n=0..2 */
++#define	SOC_PCIE_SYS_OARR(n)		(0xd20 + ((n) << 3))
++/* 64-bit out-bound mapping windows n=0..2 */
++#define	SOC_PCIE_SYS_OMAP(n)		(0xd40 + ((n) << 3))
++
++#define BCM4360_D11AC_ID	0x43a0
++#define BCM4360_D11AC2G_ID	0x43a1
++#define BCM4360_D11AC5G_ID	0x43a2
++#define BCM4352_D11AC_ID	0x43b1	/* 4352 802.11ac dualband device */
++#define BCM4352_D11AC2G_ID	0x43b2	/* 4352 802.11ac 2.4G device */
++#define BCM4352_D11AC5G_ID	0x43b3	/* 4352 802.11ac 5G device */
++
++static struct pci_ops bcma_pcie2_ops;
++
++static int bcma_pcie2_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
++{
++	struct pci_sys_data *sys = pdev->sysdata;
++	struct bcma_device *bdev = sys->private_data;
++
++	return bdev->irq;
++}
++
++static u32 bcma_pcie2_cfg_base(struct bcma_device *bdev, int busno,
++			       unsigned int devfn, int where)
++{
++	int slot = PCI_SLOT(devfn);
++	int fn = PCI_FUNC(devfn);
++	u32 addr_reg;
++
++	if (busno == 0) {
++		if (slot >= 1)
++			return 0;
++		bcma_write32(bdev, SOC_PCIE_EXT_CFG_ADDR, where & 0xffc);
++		return SOC_PCIE_EXT_CFG_DATA;
++	}
++	if (fn > 1)
++		return 0;
++	addr_reg = (busno & 0xff) << 20 | (slot << 15) | (fn << 12) |
++		   (where & 0xffc) | (1 & 0x3);
++
++	bcma_write32(bdev, SOC_PCIE_CFG_ADDR, addr_reg);
++	return SOC_PCIE_CFG_DATA;
++}
++
++static u32 bcma_pcie2_read_config(struct bcma_device *bdev, int busno,
++				  unsigned int devfn, int where, int size)
++{
++	u32 base;
++	u32 data_reg;
++	u32 mask;
++	int shift;
++
++	base = bcma_pcie2_cfg_base(bdev, busno, devfn, where);
++
++	if (!base)
++		return ~0UL;
++
++	data_reg = bcma_read32(bdev, base);
++
++	/* NS: CLASS field is R/O, and set to wrong 0x200 value */
++	if (busno == 0 && devfn == 0) {
++		/*
++		 * RC's class is 0x0280, but Linux PCI driver needs 0x604
++		 * for a PCIe bridge. So we must fixup the class code
++		 * to 0x604 here.
++		 */
++		if ((where & 0xffc) == PCI_CLASS_REVISION) {
++			data_reg &= 0xff;
++			data_reg |= 0x604 << 16;
++		}
++	}
++	/* HEADER_TYPE=00 indicates the port in EP mode */
++
++	if (size == 4)
++		return data_reg;
++
++	mask = (1 << (size * 8)) - 1;
++	shift = (where % 4) * 8;
++	return (data_reg >> shift) & mask;
++}
++
++static void bcma_pcie2_write_config(struct bcma_device *bdev, int busno,
++				    unsigned int devfn, int where, int size,
++				    u32 val)
++{
++	u32 base;
++	u32 data_reg;
++
++	base = bcma_pcie2_cfg_base(bdev, busno, devfn, where);
++
++	if (!base)
++		return;
++
++	if (size < 4) {
++		u32 mask = (1 << (size * 8)) - 1;
++		int shift = (where % 4) * 8;
++
++		data_reg = bcma_read32(bdev, base);
++		data_reg &= ~(mask << shift);
++		data_reg |= (val & mask) << shift;
++	} else {
++		data_reg = val;
++	}
++
++	bcma_write32(bdev, base, data_reg);
++}
++
++static u8 bcma_pcie2_read_config8(struct bcma_device *bdev, int busno,
++				  unsigned int devfn, int where)
++{
++	return bcma_pcie2_read_config(bdev, busno, devfn, where, 1);
++}
++
++static u16 bcma_pcie2_read_config16(struct bcma_device *bdev, int busno,
++				    unsigned int devfn, int where)
++{
++	return bcma_pcie2_read_config(bdev, busno, devfn, where, 2);
++}
++
++static u32 bcma_pcie2_read_config32(struct bcma_device *bdev, int busno,
++				    unsigned int devfn, int where)
++{
++	return bcma_pcie2_read_config(bdev, busno, devfn, where, 4);
++}
++
++static void bcma_pcie2_write_config8(struct bcma_device *bdev, int busno,
++				     unsigned int devfn, int where, u8 val)
++{
++	return bcma_pcie2_write_config(bdev, busno, devfn, where, 1, val);
++}
++
++static void bcma_pcie2_write_config16(struct bcma_device *bdev, int busno,
++				      unsigned int devfn, int where, u16 val)
++{
++	return bcma_pcie2_write_config(bdev, busno, devfn, where, 2, val);
++}
++
++static void bcma_pcie2_write_config32(struct bcma_device *bdev, int busno,
++				      unsigned int devfn, int where, u32 val)
++{
++	return bcma_pcie2_write_config(bdev, busno, devfn, where, 4, val);
++}
++
++static int bcma_pcie2_read_config_pci(struct pci_bus *bus, unsigned int devfn,
++				   int where, int size, u32 *val)
++{
++	struct pci_sys_data *sys = bus->sysdata;
++	struct bcma_device *bdev = sys->private_data;
++
++	*val = bcma_pcie2_read_config(bdev, bus->number, devfn, where, size);
++
++	return PCIBIOS_SUCCESSFUL;
++}
++
++static int bcma_pcie2_write_config_pci(struct pci_bus *bus, unsigned int devfn,
++				    int where, int size, u32 val)
++{
++	struct pci_sys_data *sys = bus->sysdata;
++	struct bcma_device *bdev = sys->private_data;
++
++	bcma_pcie2_write_config(bdev, bus->number, devfn, where, size, val);
++
++	return PCIBIOS_SUCCESSFUL;
++}
++
++/*
++ * Check link status, return 0 if link is up in RC mode,
++ * otherwise return non-zero
++ */
++static int bcma_pcie2_check_link(struct bcma_device *bdev,
++				 struct pci_sys_data *sys, u32 allow_gen2)
++{
++	u32 devfn = 0;
++	u32 tmp32;
++	u16 tmp16;
++	u8 tmp8;
++	int pos;
++	bool link = false;
++	/*
++	 * Setup callback (bcma_pcie2_setup) is called in pcibios_init_hw before
++	 * creating bus root, so we don't have it here yet. On the other hand
++	 * we really want to use pci_bus_find_capability helper to check NLW.
++	 * Let's fake simple pci_bus just to query for capabilities.
++	 */
++	struct pci_bus bus = {
++		.number = 0,
++		.ops = &bcma_pcie2_ops,
++		.sysdata = sys,
++	};
++
++	tmp32 = bcma_pcie2_read_config32(bdev, 0, devfn, 0xdc);
++	tmp32 &= ~0xf;
++	if (allow_gen2)
++		tmp32 |= 2;
++	else {
++		/* force PCIE GEN1 */
++		tmp32 |= 1;
++	}
++	bcma_pcie2_write_config32(bdev, 0, devfn, 0xdc, tmp32);
++
++	/* See if the port is in EP mode, indicated by header type 00 */
++	tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_HEADER_TYPE);
++	if (tmp8 != PCI_HEADER_TYPE_BRIDGE) {
++		dev_info(&bdev->dev, "Port %d in End-Point mode - ignored\n",
++			 bdev->core_unit);
++		return -ENODEV;
++	}
++
++	/* NS PAX only changes NLW field when card is present */
++	pos = pci_bus_find_capability(&bus, devfn, PCI_CAP_ID_EXP);
++	if (pos) {
++		u8 nlw;
++
++		pci_bus_read_config_word(&bus, devfn, pos + PCI_EXP_LNKSTA,
++					 &tmp16);
++		nlw = (tmp16 & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
++		link = (tmp16 & PCI_EXP_LNKSTA_DLLLA) || nlw != 0;
++	}
++
++	return link ? 0 : -ENODEV;
++}
++
++/*
++ * Initializte the PCIe controller
++ */
++static void bcma_pcie2_hw_init(struct bcma_device *bdev)
++{
++	u32 devfn = 0;
++	u32 tmp32;
++	u16 tmp16;
++
++	/* Change MPS and MRRS to 512 */
++	tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, 0x4d4);
++	tmp16 &= ~7;
++	tmp16 |= 2;
++	bcma_pcie2_write_config16(bdev, 0, devfn, 0x4d4, tmp16);
++
++	tmp32 = bcma_pcie2_read_config32(bdev, 0, devfn, 0xb4);
++	tmp32 &= ~((7 << 12) | (7 << 5));
++	tmp32 |= (2 << 12) | (2 << 5);
++	bcma_pcie2_write_config32(bdev, 0, devfn, 0xb4, tmp32);
++
++	/* Turn-on Root-Complex (RC) mode, from reset defailt of EP */
++
++	/* The mode is set by straps, can be overwritten via DMU
++	   register <cru_straps_control> bit 5, "1" means RC
++	 */
++
++	/* Send a downstream reset */
++	bcma_write32(bdev, SOC_PCIE_CONTROL, 0x3);
++	udelay(250);
++	bcma_write32(bdev, SOC_PCIE_CONTROL, 0x1);
++	mdelay(250);
++
++	/* TBD: take care of PM, check we're on */
++}
++
++/*
++ * Setup the address translation
++ */
++static void bcma_pcie2_map_init(struct bcma_device *bdev)
++{
++	unsigned size, i;
++	u32 addr;
++
++	/*
++	 * NOTE:
++	 * All PCI-to-CPU address mapping are 1:1 for simplicity
++	 */
++
++	/* Outbound address translation setup */
++	size = SZ_128M;
++	addr = bdev->addr_s[0];
++	BUG_ON(!addr);
++	BUG_ON(addr & ((1 << 25) - 1));	/* 64MB alignment */
++
++	for (i = 0; i < 3; i++) {
++		const unsigned win_size = SZ_64M;
++		/* 64-bit LE regs, write low word, high is 0 at reset */
++		bcma_write32(bdev, SOC_PCIE_SYS_OMAP(i), addr);
++		bcma_write32(bdev, SOC_PCIE_SYS_OARR(i), addr|0x1);
++		addr += win_size;
++		if (size >= win_size)
++			size -= win_size;
++		if (size == 0)
++			break;
++	}
++	WARN_ON(size > 0);
++
++	/*
++	 * Inbound address translation setup
++	 * Northstar only maps up to 128 MiB inbound, DRAM could be up to 1 GiB.
++	 *
++	 * For now allow access to entire DRAM, assuming it is less than 128MiB,
++	 * otherwise DMA bouncing mechanism may be required.
++	 * Also consider DMA mask to limit DMA physical address
++	 */
++	size = SZ_128M;
++	addr = PHYS_OFFSET;
++
++	size >>= 20;	/* In MB */
++	size &= 0xff;	/* Size is an 8-bit field */
++
++	WARN_ON(size == 0);
++	/* 64-bit LE regs, write low word, high is 0 at reset */
++	bcma_write32(bdev, SOC_PCIE_SYS_IMAP1(0), addr | 0x1);
++	bcma_write32(bdev, SOC_PCIE_SYS_IARR(1), addr | size);
++
++#ifdef CONFIG_SPARSEMEM
++	addr = PHYS_OFFSET2;
++	bcma_write32(bdev, SOC_PCIE_SYS_IMAP2(0), addr | 0x1);
++	bcma_write32(bdev, SOC_PCIE_SYS_IARR(2), addr | size);
++#endif
++}
++
++/*
++ * Setup PCIE Host bridge
++ */
++static void bcma_pcie2_bridge_init(struct bcma_device *bdev)
++{
++	u32 devfn = 0;
++	u8 tmp8;
++	u16 tmp16;
++
++	bcma_pcie2_write_config8(bdev, 0, devfn, PCI_PRIMARY_BUS, 0);
++	bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SECONDARY_BUS, 1);
++	bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SUBORDINATE_BUS, 4);
++
++	tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_PRIMARY_BUS);
++	tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_SECONDARY_BUS);
++	tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_SUBORDINATE_BUS);
++
++	/* MEM_BASE, MEM_LIM require 1MB alignment */
++	BUG_ON((bdev->addr_s[0] >> 16) & 0xf);
++	bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_BASE,
++		bdev->addr_s[0] >> 16);
++	BUG_ON(((bdev->addr_s[0] + SZ_128M) >> 16) & 0xf);
++	bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT,
++		(bdev->addr_s[0] + SZ_128M) >> 16);
++
++	/* These registers are not supported on the NS */
++	bcma_pcie2_write_config16(bdev, 0, devfn, PCI_IO_BASE_UPPER16, 0);
++	bcma_pcie2_write_config16(bdev, 0, devfn, PCI_IO_LIMIT_UPPER16, 0);
++
++	/* Force class to that of a Bridge */
++	bcma_pcie2_write_config16(bdev, 0, devfn, PCI_CLASS_DEVICE,
++				  PCI_CLASS_BRIDGE_PCI);
++
++	tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_CLASS_DEVICE);
++	tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_BASE);
++	tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT);
++}
++
++static int bcma_pcie2_allow_gen2_rc(struct bcma_device *bdev)
++{
++	u32 vendorid, devid, chipid, chiprev;
++	u32 val, bar;
++	void __iomem *base;
++	int allow = 1;
++
++	/* Read PCI vendor/device ID's */
++	bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x0);
++	val = bcma_read32(bdev, SOC_PCIE_CFG_DATA);
++	vendorid = val & 0xffff;
++	devid = val >> 16;
++	if (vendorid == PCI_VENDOR_ID_BROADCOM &&
++	    (devid == BCMA_CHIP_ID_BCM4360 || devid == BCM4360_D11AC_ID ||
++	     devid == BCM4360_D11AC2G_ID || devid == BCM4360_D11AC5G_ID ||
++	     devid == BCM4352_D11AC_ID || devid == BCM4352_D11AC2G_ID ||
++	     devid == BCM4352_D11AC5G_ID)) {
++		/* Config BAR0 */
++		bar = bdev->addr_s[0];
++		bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x10);
++		bcma_write32(bdev, SOC_PCIE_CFG_DATA, bar);
++		/* Config BAR0 window to access chipc */
++		bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x80);
++		bcma_write32(bdev, SOC_PCIE_CFG_DATA, SI_ENUM_BASE);
++
++		/* Enable memory resource */
++		bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x4);
++		val = bcma_read32(bdev, SOC_PCIE_CFG_DATA);
++		val |= PCI_COMMAND_MEMORY;
++		bcma_write32(bdev, SOC_PCIE_CFG_DATA, val);
++		/* Enable memory and bus master */
++		bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6);
++
++		/* Read CHIP ID */
++		base = ioremap(bar, 0x1000);
++		val = __raw_readl(base);
++		iounmap(base);
++		chipid = val & 0xffff;
++		chiprev = (val >> 16) & 0xf;
++		if ((chipid == BCMA_CHIP_ID_BCM4360 ||
++		     chipid == BCMA_CHIP_ID_BCM43460 ||
++		     chipid == BCMA_CHIP_ID_BCM4352) && (chiprev < 3))
++			allow = 0;
++	}
++	return allow;
++}
++
++static void bcma_pcie2_3rd_init(struct bcma_bus *bus)
++{
++	/* PCIE PLL block register (base 0x8000) */
++	bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x00000088, 0x57fe8000);
++	/* Check PCIE PLL lock status */
++	bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x00000088, 0x67c60000);
++}
++
++/* To improve PCIE phy jitter */
++static void bcma_pcie2_improve_phy_jitter(struct bcma_bus *bus, int phyaddr)
++{
++	u32 val;
++
++	/* Change blkaddr */
++	val = (1 << 30) | (1 << 28) | (phyaddr << 23) | (0x1f << 18) |
++		(2 << 16) | (0x863 << 4);
++	bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x0000009a, val);
++
++	/* Write 0x0190 to 0x13 regaddr */
++	val = (1 << 30) | (1 << 28) | (phyaddr << 23) | (0x13 << 18) |
++		(2 << 16) | 0x0190;
++	bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x0000009a, val);
++
++	/* Write 0x0191 to 0x19 regaddr */
++	val = (1 << 30) | (1 << 28) | (phyaddr << 23) | (0x19 << 18) |
++		(2 << 16) | 0x0191;
++	bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x0000009a, val);
++}
++
++static int bcma_pcie2_setup(int nr, struct pci_sys_data *sys)
++{
++	struct bcma_device *bdev = sys->private_data;
++	struct bcma_bus *bus = bdev->bus;
++	struct resource *res;
++	struct bcma_device *arm_core;
++	u32 cru_straps_ctrl;
++	int allow_gen2, linkfail;
++	int phyaddr;
++
++	if (bdev->core_unit == 2) {
++		arm_core = bcma_find_core(bus, BCMA_CORE_ARMCA9);
++		cru_straps_ctrl = bcma_read32(arm_core, 0x2a0);
++
++		/* 3rd PCIE is not selected */
++		if (cru_straps_ctrl & 0x10)
++			return -ENODEV;
++
++		bcma_pcie2_3rd_init(bus);
++		phyaddr = 0xf;
++	} else {
++		phyaddr = bdev->core_unit;
++	}
++	bcma_pcie2_improve_phy_jitter(bus, phyaddr);
++
++	/* create mem resource */
++	res = devm_kzalloc(&bdev->dev, sizeof(*res), GFP_KERNEL);
++	if (!res)
++		return -EINVAL;
++
++	res->start = bdev->addr_s[0];
++	res->end = res->start + SZ_128M - 1;
++	res->name = "PCIe Configuration Space";
++	res->flags = IORESOURCE_MEM;
++
++	pci_add_resource(&sys->resources, res);
++
++	/* This PCIe controller does not support IO Mem, so use a dummy one. */
++	res = devm_kzalloc(&bdev->dev, sizeof(*res), GFP_KERNEL);
++	if (!res)
++		return -EINVAL;
++
++	res->start = bdev->addr_s[0];
++	res->end = res->start + SZ_128M - 1;
++	res->name = "PCIe Configuration Space";
++	res->flags = IORESOURCE_IO;
++
++	pci_add_resource(&sys->resources, res);
++
++	for (allow_gen2 = 0; allow_gen2 <= 1; allow_gen2++) {
++		bcma_pcie2_hw_init(bdev);
++		bcma_pcie2_map_init(bdev);
++
++		/*
++		 * Skip inactive ports -
++		 * will need to change this for hot-plugging
++		 */
++		linkfail = bcma_pcie2_check_link(bdev, sys, allow_gen2);
++		if (linkfail)
++			break;
++
++		bcma_pcie2_bridge_init(bdev);
++
++		if (allow_gen2 == 0) {
++			if (bcma_pcie2_allow_gen2_rc(bdev) == 0)
++				break;
++			dev_info(&bdev->dev, "switching to GEN2\n");
++		}
++	}
++
++	if (linkfail)
++		return -1;
++
++	return 1;
++}
++
++/*
++ * Methods for accessing configuration registers
++ */
++static struct pci_ops bcma_pcie2_ops = {
++	.read = bcma_pcie2_read_config_pci,
++	.write = bcma_pcie2_write_config_pci,
++};
++
++static int bcma_pcie2_probe(struct bcma_device *bdev)
++{
++	struct hw_pci hw;
++
++	dev_info(&bdev->dev, "scanning bus\n");
++
++	hw = (struct hw_pci) {
++		.nr_controllers = 1,
++		.domain		= bdev->core_unit,
++		.private_data	= (void **)&bdev,
++		.setup		= bcma_pcie2_setup,
++		.map_irq	= bcma_pcie2_map_irq,
++		.ops		= &bcma_pcie2_ops,
++	};
++
++	/* Announce this port to ARM/PCI common code */
++	pci_common_init_dev(&bdev->dev, &hw);
++
++	/* Setup virtual-wire interrupts */
++	bcma_write32(bdev, SOC_PCIE_SYS_RC_INTX_EN, 0xf);
++
++	/* Enable memory and bus master */
++	bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6);
++
++	return 0;
++}
++
++static const struct bcma_device_id bcma_pcie2_table[] = {
++	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_PCIEG2, BCMA_ANY_REV, BCMA_ANY_CLASS),
++	BCMA_CORETABLE_END
++};
++MODULE_DEVICE_TABLE(bcma, bcma_pcie2_table);
++
++static struct bcma_driver bcma_pcie2_driver = {
++	.name		= KBUILD_MODNAME,
++	.id_table	= bcma_pcie2_table,
++	.probe		= bcma_pcie2_probe,
++};
++
++static int __init bcma_pcie2_init(void)
++{
++	return bcma_driver_register(&bcma_pcie2_driver);
++}
++module_init(bcma_pcie2_init);
++
++static void __exit bcma_pcie2_exit(void)
++{
++	bcma_driver_unregister(&bcma_pcie2_driver);
++}
++module_exit(bcma_pcie2_exit);
++
++MODULE_AUTHOR("Hauke Mehrtens");
++MODULE_DESCRIPTION("PCIe Gen2 driver for BCMA");
++MODULE_LICENSE("GPLv2");

+ 195 - 0
target/linux/bcm53xx/patches-3.18/300-ARM-BCM5301X-Disable-MMU-and-Dcache-for-decompression.patch

@@ -0,0 +1,195 @@
+From 26023cdfacaf116545b1087b9d1fe50dc6fbda10 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Wed, 24 Sep 2014 22:14:07 +0200
+Subject: [PATCH] ARM: BCM5301X: Disable MMU and Dcache for decompression
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Without this fix kernel was randomly hanging in ~25% of tries during
+early init. Hangs used to happen at random places in the start_kernel.
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/boot/compressed/Makefile                |   5 +
+ arch/arm/boot/compressed/head-bcm_5301x-mpcore.S |  37 +++++++
+ arch/arm/boot/compressed/mpcore_cache.S          | 118 +++++++++++++++++++++++
+ 3 files changed, 160 insertions(+)
+ create mode 100644 arch/arm/boot/compressed/head-bcm_5301x-mpcore.S
+ create mode 100644 arch/arm/boot/compressed/mpcore_cache.S
+
+--- a/arch/arm/boot/compressed/Makefile
++++ b/arch/arm/boot/compressed/Makefile
+@@ -46,6 +46,11 @@ ifeq ($(CONFIG_ARCH_ACORN),y)
+ OBJS		+= ll_char_wr.o font.o
+ endif
+ 
++ifeq ($(CONFIG_ARCH_BCM_5301X),y)
++OBJS		+= head-bcm_5301x-mpcore.o
++OBJS		+= mpcore_cache.o
++endif
++
+ ifeq ($(CONFIG_ARCH_SA1100),y)
+ OBJS		+= head-sa1100.o
+ endif
+--- /dev/null
++++ b/arch/arm/boot/compressed/head-bcm_5301x-mpcore.S
+@@ -0,0 +1,37 @@
++/*
++ *
++ * Platform specific tweaks.  This is merged into head.S by the linker.
++ *
++ */
++
++#include <linux/linkage.h>
++#include <asm/assembler.h>
++#include <asm/cp15.h>
++
++		.section        ".start", "ax"
++
++/*
++ * This code section is spliced into the head code by the linker
++ */
++
++__plat_uncompress_start:
++
++	@ Preserve r8/r7 i.e. kernel entry values
++	mov	r12, r8
++
++	@ Clear MMU enable and Dcache enable bits
++	mrc	p15, 0, r0, c1, c0, 0		@ Read SCTLR
++	bic	r0, #CR_C|CR_M
++	mcr	p15, 0, r0, c1, c0, 0		@ Write SCTLR
++	nop
++
++	@ Call the cache invalidation routine
++	bl	v7_all_dcache_invalidate
++	nop
++	mov	r0,#0
++	ldr	r3, =0x19022000			@ L2 cache controller, control reg
++	str	r0, [r3, #0x100]		@ Disable L2 cache
++	nop
++
++	@ Restore
++	mov	r8, r12
+--- /dev/null
++++ b/arch/arm/boot/compressed/mpcore_cache.S
+@@ -0,0 +1,118 @@
++/*****************************************************************************
++* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
++*
++* Unless you and Broadcom execute a separate written software license
++* agreement governing use of this software, this software is licensed to you
++* under the terms of the GNU General Public License version 2, available at
++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
++*
++* Notwithstanding the above, under no circumstances may you combine this
++* software in any way with any other Broadcom software provided under a
++* license other than the GPL, without Broadcom's express prior written
++* consent.
++*****************************************************************************/
++
++#include <linux/linkage.h>
++#include <linux/init.h>
++
++	__INIT
++
++/*
++ * v7_l1_cache_invalidate
++ *
++ * Invalidate contents of L1 cache without flushing its contents
++ * into outer cache and memory. This is needed when the contents
++ * of the cache are unpredictable after power-up.
++ *
++ * corrupts r0-r6
++ */
++
++ENTRY(v7_l1_cache_invalidate)
++	mov	r0, #0
++	mcr	p15, 2, r0, c0, c0, 0	@ set cache level to 1
++	mrc	p15, 1, r0, c0, c0, 0	@ read CLIDR
++
++	ldr	r1, =0x7fff
++	and	r2, r1, r0, lsr #13	@ get max # of index size
++
++	ldr	r1, =0x3ff
++	and	r3, r1, r0, lsr #3	@ NumWays - 1
++	add	r2, r2, #1		@ NumSets
++
++	and	r0, r0, #0x7
++	add	r0, r0, #4		@ SetShift
++
++	clz	r1, r3			@ WayShift
++	add	r4, r3, #1		@ NumWays
++1:	sub	r2, r2, #1		@ NumSets--
++	mov	r3, r4			@ Temp = NumWays
++2:	subs	r3, r3, #1		@ Temp--
++	mov	r5, r3, lsl r1
++	mov	r6, r2, lsl r0
++	orr	r5, r5, r6		@ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
++	mcr	p15, 0, r5, c7, c6, 2	@ Invalidate line
++	bgt	2b
++	cmp	r2, #0
++	bgt	1b
++	dsb
++	mov	r0,#0
++	mcr	p15,0,r0,c7,c5,0	/* Invalidate icache */
++	isb
++	mov	pc, lr
++ENDPROC(v7_l1_cache_invalidate)
++
++/*
++ * v7_all_dcache_invalidate
++ *
++ * Invalidate without flushing the contents of all cache levels
++ * accesible by the current processor core.
++ * This is useful when the contents of cache memory are undetermined
++ * at power-up.
++ *	Corrupted registers: r0-r7, r9-r11
++ *
++ * Based on cache-v7.S: v7_flush_dcache_all()
++ */
++
++ENTRY(v7_all_dcache_invalidate)
++	mrc	p15, 1, r0, c0, c0, 1	@ read clidr
++	ands	r3, r0, #0x7000000	@ extract loc from clidr
++	mov	r3, r3, lsr #23		@ left align loc bit field
++	beq	finished		@ if loc is 0, then no need to clean
++	mov	r10, #0			@ start clean at cache level 0
++loop1:
++	add	r2, r10, r10, lsr #1	@ work out 3x current cache level
++	mov	r1, r0, lsr r2		@ extract cache type bits from clidr
++	and	r1, r1, #7		@ mask of bits for current cache only
++	cmp	r1, #2			@ see what cache we have at this level
++	blt	skip			@ skip if no cache, or just i-cache
++	mcr	p15, 2, r10, c0, c0, 0	@ select current cache level in cssr
++	isb				@ isb to sych the new cssr&csidr
++	mrc	p15, 1, r1, c0, c0, 0	@ read the new csidr
++	and	r2, r1, #7		@ extract the length of the cache lines
++	add	r2, r2, #4		@ add 4 (line length offset)
++	ldr	r4, =0x3ff
++	ands	r4, r4, r1, lsr #3	@ find maximum number on the way size
++	clz	r5, r4			@ find bit pos of way size increment
++	ldr	r7, =0x7fff
++	ands	r7, r7, r1, lsr #13	@ extract max number of the index size
++loop2:
++	mov	r9, r4			@ create working copy of max way size
++loop3:
++	orr	r11, r10, r9, lsl r5	@ factor way and cache number into r11
++	orr	r11, r11, r7, lsl r2	@ factor index number into r11
++	mcr	p15, 0, r11, c7, c6, 2	@ Invalidate line
++	subs	r9, r9, #1		@ decrement the way
++	bge	loop3
++	subs	r7, r7, #1		@ decrement the index
++	bge	loop2
++skip:
++	add	r10, r10, #2		@ increment cache number
++	cmp	r3, r10
++	bgt	loop1
++finished:
++	mov	r10, #0			@ swith back to cache level 0
++	mcr	p15, 2, r10, c0, c0, 0	@ select current cache level in cssr
++	dsb
++	isb
++	mov	pc, lr
++ENDPROC(v7_all_dcache_invalidate)

+ 59 - 0
target/linux/bcm53xx/patches-3.18/301-ARM-BCM5301X-Add-buttons-support-for-Netgear-R6250.patch

@@ -0,0 +1,59 @@
+From e1b44fc2e3cf76be1213bde07fc37c47eff39158 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Thu, 2 Oct 2014 13:49:13 +0200
+Subject: [PATCH] ARM: BCM5301X: Add buttons support for Netgear R6250
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We use "gpio-keys-polled" for now, as ChipCommon/GPIO interrupts are
+not implemented yet.
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/boot/dts/bcm4708-netgear-r6250.dts | 25 +++++++++++++++++++++++++
+ arch/arm/boot/dts/bcm5301x.dtsi             |  1 +
+ 2 files changed, 26 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm4708-netgear-r6250.dts
++++ b/arch/arm/boot/dts/bcm4708-netgear-r6250.dts
+@@ -66,4 +66,29 @@
+ 			linux,default-trigger = "default-off";
+ 		};
+ 	};
++
++	gpio-keys {
++		compatible = "gpio-keys-polled";
++		#address-cells = <1>;
++		#size-cells = <0>;
++		poll-interval = <200>;
++
++		wps {
++			label = "WPS";
++			linux,code = <KEY_WPS_BUTTON>;
++			gpios = <&chipcommon 4 GPIO_ACTIVE_LOW>;
++		};
++
++		rfkill {
++			label = "WiFi";
++			linux,code = <KEY_RFKILL>;
++			gpios = <&chipcommon 5 GPIO_ACTIVE_LOW>;
++		};
++
++		restart {
++			label = "Reset";
++			linux,code = <KEY_RESTART>;
++			gpios = <&chipcommon 6 GPIO_ACTIVE_LOW>;
++		};
++	};
+ };
+--- a/arch/arm/boot/dts/bcm5301x.dtsi
++++ b/arch/arm/boot/dts/bcm5301x.dtsi
+@@ -9,6 +9,7 @@
+  */
+ 
+ #include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/input/input.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include "skeleton.dtsi"

+ 125 - 0
target/linux/bcm53xx/patches-3.18/302-ARM-BCM5301X-Add-DT-for-Netgear-R6300-V2.patch

@@ -0,0 +1,125 @@
+From 788069f86c7fc1ce54661651e695943fb47a5188 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Thu, 2 Oct 2014 21:02:33 +0200
+Subject: [PATCH] ARM: BCM5301X: Add DT for Netgear R6300 V2
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/boot/dts/Makefile                     |  4 ++-
+ arch/arm/boot/dts/bcm4708-netgear-r6300-v2.dts | 35 ++++++++++++++++++++++++++
+ 2 files changed, 38 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/boot/dts/bcm4708-netgear-r6300-v2.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -54,7 +54,9 @@ dtb-$(CONFIG_ARCH_AT91)	+= at91-sama5d4e
+ dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb
+ dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb
+-dtb-$(CONFIG_ARCH_BCM_5301X) += bcm4708-netgear-r6250.dtb
++dtb-$(CONFIG_ARCH_BCM_5301X) += \
++	bcm4708-netgear-r6250.dtb \
++	bcm4708-netgear-r6300-v2.dtb
+ dtb-$(CONFIG_ARCH_BCM_63XX) += bcm963138dvt.dtb
+ dtb-$(CONFIG_ARCH_BCM_MOBILE) += bcm28155-ap.dtb \
+ 	bcm21664-garnet.dtb
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm4708-netgear-r6300-v2.dts
+@@ -0,0 +1,94 @@
++/*
++ * Broadcom BCM470X / BCM5301X arm platform code.
++ * DTS for Netgear R6300 V2
++ *
++ * Copyright © 2014 Rafał Miłecki <[email protected]>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++/dts-v1/;
++
++#include "bcm4708.dtsi"
++
++/ {
++	compatible = "netgear,r6300v2", "brcm,bcm4708";
++	model = "Netgear R6300 V2 (BCM4708)";
++
++	chosen {
++		bootargs = "console=ttyS0,115200";
++	};
++
++	memory {
++		reg = <0x00000000 0x08000000>;
++	};
++
++	chipcommonA {
++		uart0: serial@0300 {
++			status = "okay";
++		};
++
++		uart1: serial@0400 {
++			status = "okay";
++		};
++	};
++
++	leds {
++		compatible = "gpio-leds";
++
++		logo {
++			label = "bcm53xx:white:logo";
++			gpios = <&chipcommon 1 GPIO_ACTIVE_HIGH>;
++			linux,default-trigger = "default-on";
++		};
++
++		power0 {
++			label = "bcm53xx:green:power";
++			gpios = <&chipcommon 2 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-off";
++		};
++
++		power1 {
++			label = "bcm53xx:amber:power";
++			gpios = <&chipcommon 3 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-on";
++		};
++
++		usb {
++			label = "bcm53xx:blue:usb";
++			gpios = <&chipcommon 8 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-off";
++		};
++
++		wireless {
++			label = "bcm53xx:blue:wireless";
++			gpios = <&chipcommon 11 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-off";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys-polled";
++		#address-cells = <1>;
++		#size-cells = <0>;
++		poll-interval = <200>;
++
++		wps {
++			label = "WPS";
++			linux,code = <KEY_WPS_BUTTON>;
++			gpios = <&chipcommon 4 GPIO_ACTIVE_LOW>;
++		};
++
++		rfkill {
++			label = "WiFi";
++			linux,code = <KEY_RFKILL>;
++			gpios = <&chipcommon 5 GPIO_ACTIVE_LOW>;
++		};
++
++		restart {
++			label = "Reset";
++			linux,code = <KEY_RESTART>;
++			gpios = <&chipcommon 6 GPIO_ACTIVE_LOW>;
++		};
++	};
++};

+ 102 - 0
target/linux/bcm53xx/patches-3.18/303-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-1750DHP.patch

@@ -0,0 +1,102 @@
+From b7620da56595c5505e4a10b8779cec0362b59db2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Thu, 9 Oct 2014 18:04:28 +0200
+Subject: [PATCH] ARM: BCM5301X: Add DT for Buffalo WZR-1750DHP
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/boot/dts/Makefile                        |  1 +
+ arch/arm/boot/dts/bcm4708-buffalo-wzr-1750dhp.dts | 74 +++++++++++++++++++++++
+ 2 files changed, 75 insertions(+)
+ create mode 100644 arch/arm/boot/dts/bcm4708-buffalo-wzr-1750dhp.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -55,6 +55,7 @@ dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.
+ dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb
+ dtb-$(CONFIG_ARCH_BCM_5301X) += \
++	bcm4708-buffalo-wzr-1750dhp.dtb \
+ 	bcm4708-netgear-r6250.dtb \
+ 	bcm4708-netgear-r6300-v2.dtb
+ dtb-$(CONFIG_ARCH_BCM_63XX) += bcm963138dvt.dtb
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm4708-buffalo-wzr-1750dhp.dts
+@@ -0,0 +1,74 @@
++/*
++ * Broadcom BCM470X / BCM5301X arm platform code.
++ * DTS for Buffalo WZR-1750DHP
++ *
++ * Copyright © 2014 Rafał Miłecki <[email protected]>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++/dts-v1/;
++
++#include "bcm4708.dtsi"
++
++/ {
++	compatible = "buffalo,wzr-1750dhp", "brcm,bcm4708";
++	model = "Buffalo WZR-1750DHP (BCM4708)";
++
++	chosen {
++		bootargs = "console=ttyS0,115200";
++	};
++
++	memory {
++		reg = <0x00000000 0x08000000>;
++	};
++
++	chipcommonA {
++		uart0: serial@0300 {
++			status = "okay";
++		};
++
++		uart1: serial@0400 {
++			status = "okay";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys-polled";
++		#address-cells = <1>;
++		#size-cells = <0>;
++		poll-interval = <200>;
++
++		restart {
++			label = "Reset";
++			linux,code = <KEY_RESTART>;
++			gpios = <&chipcommon 11 GPIO_ACTIVE_LOW>;
++		};
++
++		aoss {
++			label = "AOSS";
++			linux,code = <KEY_WPS_BUTTON>;
++			gpios = <&chipcommon 12 GPIO_ACTIVE_LOW>;
++		};
++
++		/* Commit mode set by switch? */
++		mode {
++			label = "Mode";
++			linux,code = <KEY_SETUP>;
++			gpios = <&chipcommon 13 GPIO_ACTIVE_LOW>;
++		};
++
++		/* Switch: AP mode */
++		sw_ap {
++			label = "AP";
++			linux,code = <BTN_0>;
++			gpios = <&chipcommon 14 GPIO_ACTIVE_LOW>;
++		};
++
++		eject {
++			label = "USB eject";
++			linux,code = <KEY_EJECTCD>;
++			gpios = <&chipcommon 15 GPIO_ACTIVE_LOW>;
++		};
++	};
++};

+ 160 - 0
target/linux/bcm53xx/patches-3.18/304-ARM-BCM5301X-Add-DT-for-Asus-RT-N18U.patch

@@ -0,0 +1,160 @@
+From 89fe6f9b7875f74e7d63a90ae3a51d84d3cf9369 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Thu, 9 Oct 2014 18:16:26 +0200
+Subject: [PATCH] ARM: BCM5301X: Add DT for Asus RT-N18U
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/boot/dts/Makefile                  |  3 +-
+ arch/arm/boot/dts/bcm47081-asus-rt-n18u.dts | 88 +++++++++++++++++++++++++++++
+ arch/arm/boot/dts/bcm47081.dtsi             | 26 +++++++++
+ arch/arm/mach-bcm/bcm_5301x.c               |  1 +
+ 4 files changed, 117 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/boot/dts/bcm47081-asus-rt-n18u.dts
+ create mode 100644 arch/arm/boot/dts/bcm47081.dtsi
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -57,7 +57,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rp
+ dtb-$(CONFIG_ARCH_BCM_5301X) += \
+ 	bcm4708-buffalo-wzr-1750dhp.dtb \
+ 	bcm4708-netgear-r6250.dtb \
+-	bcm4708-netgear-r6300-v2.dtb
++	bcm4708-netgear-r6300-v2.dtb \
++	bcm47081-asus-rt-n18u.dtb
+ dtb-$(CONFIG_ARCH_BCM_63XX) += bcm963138dvt.dtb
+ dtb-$(CONFIG_ARCH_BCM_MOBILE) += bcm28155-ap.dtb \
+ 	bcm21664-garnet.dtb
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm47081-asus-rt-n18u.dts
+@@ -0,0 +1,88 @@
++/*
++ * Broadcom BCM470X / BCM5301X arm platform code.
++ * DTS for Asus RT-N18U
++ *
++ * Copyright © 2014 Rafał Miłecki <[email protected]>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++/dts-v1/;
++
++#include "bcm47081.dtsi"
++
++/ {
++	compatible = "asus,rt-n18u", "brcm,bcm47081";
++	model = "Asus RT-N18U (BCM47081)";
++
++	chosen {
++		bootargs = "console=ttyS0,115200";
++	};
++
++	memory {
++		reg = <0x00000000 0x08000000>;
++	};
++
++	chipcommonA {
++		uart0: serial@0300 {
++			status = "okay";
++		};
++
++		uart1: serial@0400 {
++			status = "okay";
++		};
++	};
++
++	leds {
++		compatible = "gpio-leds";
++
++		power {
++			label = "bcm53xx:blue:power";
++			gpios = <&chipcommon 0 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-on";
++		};
++
++		usb2 {
++			label = "bcm53xx:blue:usb2";
++			gpios = <&chipcommon 3 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-off";
++		};
++
++		wan {
++			label = "bcm53xx:blue:wan";
++			gpios = <&chipcommon 6 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-on";
++		};
++
++		lan {
++			label = "bcm53xx:blue:lan";
++			gpios = <&chipcommon 9 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-on";
++		};
++
++		usb3 {
++			label = "bcm53xx:blue:usb3";
++			gpios = <&chipcommon 14 GPIO_ACTIVE_LOW>;
++			linux,default-trigger = "default-off";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys-polled";
++		#address-cells = <1>;
++		#size-cells = <0>;
++		poll-interval = <200>;
++
++		restart {
++			label = "Reset";
++			linux,code = <KEY_RESTART>;
++			gpios = <&chipcommon 7 GPIO_ACTIVE_LOW>;
++		};
++
++		wps {
++			label = "WPS";
++			linux,code = <KEY_WPS_BUTTON>;
++			gpios = <&chipcommon 11 GPIO_ACTIVE_LOW>;
++		};
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm47081.dtsi
+@@ -0,0 +1,26 @@
++/*
++ * Broadcom BCM470X / BCM5301X ARM platform code.
++ * DTS for BCM47081 SoC.
++ *
++ * Copyright © 2014 Rafał Miłecki <[email protected]>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++#include "bcm5301x.dtsi"
++
++/ {
++	compatible = "brcm,bcm47081";
++
++	cpus {
++		#address-cells = <1>;
++		#size-cells = <0>;
++
++		cpu@0 {
++			device_type = "cpu";
++			compatible = "arm,cortex-a9";
++			next-level-cache = <&L2>;
++			reg = <0x0>;
++		};
++	};
++};
+--- a/arch/arm/mach-bcm/bcm_5301x.c
++++ b/arch/arm/mach-bcm/bcm_5301x.c
+@@ -75,6 +75,7 @@ static void bcm5301x_restart(enum reboot
+ 
+ static const char __initconst *bcm5301x_dt_compat[] = {
+ 	"brcm,bcm4708",
++	"brcm,bcm47081",
+ 	NULL,
+ };
+ 

+ 97 - 0
target/linux/bcm53xx/patches-3.18/305-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-600DHP2.patch

@@ -0,0 +1,97 @@
+From 4812cd75bc85a9f7050e2b58c1cf17e3bd4dc7f8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Wed, 15 Oct 2014 09:01:50 +0200
+Subject: [PATCH] ARM: BCM5301X: Add DT for Buffalo WZR-600DHP2
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ arch/arm/boot/dts/Makefile                         |  3 +-
+ arch/arm/boot/dts/bcm47081-buffalo-wzr-600dhp2.dts | 67 ++++++++++++++++++++++
+ 2 files changed, 69 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/boot/dts/bcm47081-buffalo-wzr-600dhp2.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -58,7 +58,8 @@ dtb-$(CONFIG_ARCH_BCM_5301X) += \
+ 	bcm4708-buffalo-wzr-1750dhp.dtb \
+ 	bcm4708-netgear-r6250.dtb \
+ 	bcm4708-netgear-r6300-v2.dtb \
+-	bcm47081-asus-rt-n18u.dtb
++	bcm47081-asus-rt-n18u.dtb \
++	bcm47081-buffalo-wzr-600dhp2.dtb
+ dtb-$(CONFIG_ARCH_BCM_63XX) += bcm963138dvt.dtb
+ dtb-$(CONFIG_ARCH_BCM_MOBILE) += bcm28155-ap.dtb \
+ 	bcm21664-garnet.dtb
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm47081-buffalo-wzr-600dhp2.dts
+@@ -0,0 +1,67 @@
++/*
++ * Broadcom BCM470X / BCM5301X arm platform code.
++ * DTS for Buffalo WZR-600DHP2
++ *
++ * Copyright © 2014 Rafał Miłecki <[email protected]>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++/dts-v1/;
++
++#include "bcm47081.dtsi"
++
++/ {
++	compatible = "buffalo,wzr-600dhp2", "brcm,bcm47081";
++	model = "Buffalo WZR-600DHP2 (BCM47081)";
++
++	chosen {
++		bootargs = "console=ttyS0,115200";
++	};
++
++	memory {
++		reg = <0x00000000 0x08000000>;
++	};
++
++	chipcommonA {
++		uart0: serial@0300 {
++			status = "okay";
++		};
++
++		uart1: serial@0400 {
++			status = "okay";
++		};
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys-polled";
++		#address-cells = <1>;
++		#size-cells = <0>;
++		poll-interval = <200>;
++
++		aoss {
++			label = "AOSS";
++			linux,code = <KEY_WPS_BUTTON>;
++			gpios = <&chipcommon 9 GPIO_ACTIVE_LOW>;
++		};
++
++		restart {
++			label = "Reset";
++			linux,code = <KEY_RESTART>;
++			gpios = <&chipcommon 11 GPIO_ACTIVE_LOW>;
++		};
++
++		/* Switch device mode? */
++		mode {
++			label = "Mode";
++			linux,code = <KEY_SETUP>;
++			gpios = <&chipcommon 14 GPIO_ACTIVE_LOW>;
++		};
++
++		eject {
++			label = "USB eject";
++			linux,code = <KEY_EJECTCD>;
++			gpios = <&chipcommon 15 GPIO_ACTIVE_LOW>;
++		};
++	};
++};

+ 46 - 0
target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch

@@ -0,0 +1,46 @@
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -926,29 +926,23 @@ int spi_nor_scan(struct spi_nor *nor, co
+ 	if (ret)
+ 		return ret;
+ 
+-	info = (void *)id->driver_data;
+-
+-	if (info->jedec_id) {
+-		const struct spi_device_id *jid;
+-
+-		jid = nor->read_id(nor);
+-		if (IS_ERR(jid)) {
+-			return PTR_ERR(jid);
+-		} else if (jid != id) {
+-			/*
+-			 * JEDEC knows better, so overwrite platform ID. We
+-			 * can't trust partitions any longer, but we'll let
+-			 * mtd apply them anyway, since some partitions may be
+-			 * marked read-only, and we don't want to lose that
+-			 * information, even if it's not 100% accurate.
+-			 */
+-			dev_warn(dev, "found %s, expected %s\n",
+-				 jid->name, id->name);
+-			id = jid;
+-			info = (void *)jid->driver_data;
++	if (id) {
++		info = (void *)id->driver_data;
++		if (info->jedec_id) {
++			dev_warn(dev,
++				 "passed SPI device ID (%s) contains JEDEC, ignoring it, driver should be fixed!\n",
++				 id->name);
++			id = NULL;
+ 		}
+ 	}
+ 
++	if (!id) {
++		id = nor->read_id(nor);
++		if (IS_ERR(id))
++			return PTR_ERR(id);
++	}
++	info = (void *)id->driver_data;
++
+ 	mutex_init(&nor->lock);
+ 
+ 	/*

+ 374 - 0
target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch

@@ -0,0 +1,374 @@
+--- a/drivers/mtd/spi-nor/fsl-quadspi.c
++++ b/drivers/mtd/spi-nor/fsl-quadspi.c
+@@ -719,16 +719,10 @@ static int fsl_qspi_read(struct spi_nor
+ {
+ 	struct fsl_qspi *q = nor->priv;
+ 	u8 cmd = nor->read_opcode;
+-	int ret;
+ 
+ 	dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n",
+ 		cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len);
+ 
+-	/* Wait until the previous command is finished. */
+-	ret = nor->wait_till_ready(nor);
+-	if (ret)
+-		return ret;
+-
+ 	/* Read out the data directly from the AHB buffer.*/
+ 	memcpy(buf, q->ahb_base + q->chip_base_addr + from, len);
+ 
+@@ -744,16 +738,6 @@ static int fsl_qspi_erase(struct spi_nor
+ 	dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
+ 		nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs);
+ 
+-	/* Wait until finished previous write command. */
+-	ret = nor->wait_till_ready(nor);
+-	if (ret)
+-		return ret;
+-
+-	/* Send write enable, then erase commands. */
+-	ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+-	if (ret)
+-		return ret;
+-
+ 	ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
+ 	if (ret)
+ 		return ret;
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -163,81 +163,69 @@ static inline int set_4byte(struct spi_n
+ 		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
+ 	}
+ }
+-
+-static int spi_nor_wait_till_ready(struct spi_nor *nor)
++static inline int spi_nor_sr_ready(struct spi_nor *nor)
+ {
+-	unsigned long deadline;
+-	int sr;
+-
+-	deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+-
+-	do {
+-		cond_resched();
++	int sr = read_sr(nor);
++	if (sr < 0)
++		return sr;
++	else
++		return !(sr & SR_WIP);
++}
+ 
+-		sr = read_sr(nor);
+-		if (sr < 0)
+-			break;
+-		else if (!(sr & SR_WIP))
+-			return 0;
+-	} while (!time_after_eq(jiffies, deadline));
++static inline int spi_nor_fsr_ready(struct spi_nor *nor)
++{
++	int fsr = read_fsr(nor);
++	if (fsr < 0)
++		return fsr;
++	else
++		return fsr & FSR_READY;
++}
+ 
+-	return -ETIMEDOUT;
++static int spi_nor_ready(struct spi_nor *nor)
++{
++	int sr, fsr;
++	sr = spi_nor_sr_ready(nor);
++	if (sr < 0)
++		return sr;
++	fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
++	if (fsr < 0)
++		return sr;
++	return sr && fsr;
+ }
+ 
+-static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
++/*
++ * Service routine to read status register until ready, or timeout occurs.
++ * Returns non-zero if error.
++ */
++static int spi_nor_wait_till_ready(struct spi_nor *nor)
+ {
+ 	unsigned long deadline;
+-	int sr;
+-	int fsr;
++	int ret;
+ 
+ 	deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+ 
+ 	do {
+ 		cond_resched();
+ 
+-		sr = read_sr(nor);
+-		if (sr < 0) {
+-			break;
+-		} else if (!(sr & SR_WIP)) {
+-			fsr = read_fsr(nor);
+-			if (fsr < 0)
+-				break;
+-			if (fsr & FSR_READY)
+-				return 0;
+-		}
++		ret = spi_nor_ready(nor);
++		if (ret < 0)
++			return ret;
++		if (ret)
++			return 0;
+ 	} while (!time_after_eq(jiffies, deadline));
+ 
+ 	return -ETIMEDOUT;
+ }
+ 
+ /*
+- * Service routine to read status register until ready, or timeout occurs.
+- * Returns non-zero if error.
+- */
+-static int wait_till_ready(struct spi_nor *nor)
+-{
+-	return nor->wait_till_ready(nor);
+-}
+-
+-/*
+  * Erase the whole flash memory
+  *
+  * Returns 0 if successful, non-zero otherwise.
+  */
+ static int erase_chip(struct spi_nor *nor)
+ {
+-	int ret;
+-
+ 	dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
+ 
+-	/* Wait until finished previous write command. */
+-	ret = wait_till_ready(nor);
+-	if (ret)
+-		return ret;
+-
+-	/* Send write enable, then erase commands. */
+-	write_enable(nor);
+-
+ 	return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
+ }
+ 
+@@ -290,6 +278,8 @@ static int spi_nor_erase(struct mtd_info
+ 	if (ret)
+ 		return ret;
+ 
++	write_enable(nor);
++
+ 	/* whole-chip erase? */
+ 	if (len == mtd->size) {
+ 		if (erase_chip(nor)) {
+@@ -297,6 +287,10 @@ static int spi_nor_erase(struct mtd_info
+ 			goto erase_err;
+ 		}
+ 
++		ret = spi_nor_wait_till_ready(nor);
++		if (ret)
++			goto erase_err;
++
+ 	/* REVISIT in some cases we could speed up erasing large regions
+ 	 * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K.  We may have set up
+ 	 * to use "small sector erase", but that's not always optimal.
+@@ -312,9 +306,15 @@ static int spi_nor_erase(struct mtd_info
+ 
+ 			addr += mtd->erasesize;
+ 			len -= mtd->erasesize;
++
++			ret = spi_nor_wait_till_ready(nor);
++			if (ret)
++				goto erase_err;
+ 		}
+ 	}
+ 
++	write_disable(nor);
++
+ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
+ 
+ 	instr->state = MTD_ERASE_DONE;
+@@ -339,11 +339,6 @@ static int spi_nor_lock(struct mtd_info
+ 	if (ret)
+ 		return ret;
+ 
+-	/* Wait until finished previous command */
+-	ret = wait_till_ready(nor);
+-	if (ret)
+-		goto err;
+-
+ 	status_old = read_sr(nor);
+ 
+ 	if (offset < mtd->size - (mtd->size / 2))
+@@ -386,11 +381,6 @@ static int spi_nor_unlock(struct mtd_inf
+ 	if (ret)
+ 		return ret;
+ 
+-	/* Wait until finished previous command */
+-	ret = wait_till_ready(nor);
+-	if (ret)
+-		goto err;
+-
+ 	status_old = read_sr(nor);
+ 
+ 	if (offset+len > mtd->size - (mtd->size / 64))
+@@ -703,11 +693,6 @@ static int sst_write(struct mtd_info *mt
+ 	if (ret)
+ 		return ret;
+ 
+-	/* Wait until finished previous write command. */
+-	ret = wait_till_ready(nor);
+-	if (ret)
+-		goto time_out;
+-
+ 	write_enable(nor);
+ 
+ 	nor->sst_write_second = false;
+@@ -719,7 +704,7 @@ static int sst_write(struct mtd_info *mt
+ 
+ 		/* write one byte. */
+ 		nor->write(nor, to, 1, retlen, buf);
+-		ret = wait_till_ready(nor);
++		ret = spi_nor_wait_till_ready(nor);
+ 		if (ret)
+ 			goto time_out;
+ 	}
+@@ -731,7 +716,7 @@ static int sst_write(struct mtd_info *mt
+ 
+ 		/* write two bytes. */
+ 		nor->write(nor, to, 2, retlen, buf + actual);
+-		ret = wait_till_ready(nor);
++		ret = spi_nor_wait_till_ready(nor);
+ 		if (ret)
+ 			goto time_out;
+ 		to += 2;
+@@ -740,7 +725,7 @@ static int sst_write(struct mtd_info *mt
+ 	nor->sst_write_second = false;
+ 
+ 	write_disable(nor);
+-	ret = wait_till_ready(nor);
++	ret = spi_nor_wait_till_ready(nor);
+ 	if (ret)
+ 		goto time_out;
+ 
+@@ -751,7 +736,7 @@ static int sst_write(struct mtd_info *mt
+ 		nor->program_opcode = SPINOR_OP_BP;
+ 		nor->write(nor, to, 1, retlen, buf + actual);
+ 
+-		ret = wait_till_ready(nor);
++		ret = spi_nor_wait_till_ready(nor);
+ 		if (ret)
+ 			goto time_out;
+ 		write_disable(nor);
+@@ -779,11 +764,6 @@ static int spi_nor_write(struct mtd_info
+ 	if (ret)
+ 		return ret;
+ 
+-	/* Wait until finished previous write command. */
+-	ret = wait_till_ready(nor);
+-	if (ret)
+-		goto write_err;
+-
+ 	write_enable(nor);
+ 
+ 	page_offset = to & (nor->page_size - 1);
+@@ -802,16 +782,20 @@ static int spi_nor_write(struct mtd_info
+ 			if (page_size > nor->page_size)
+ 				page_size = nor->page_size;
+ 
+-			wait_till_ready(nor);
++			ret = spi_nor_wait_till_ready(nor);
++			if (ret)
++				goto write_err;
++
+ 			write_enable(nor);
+ 
+ 			nor->write(nor, to + i, page_size, retlen, buf + i);
+ 		}
+ 	}
+ 
++	ret = spi_nor_wait_till_ready(nor);
+ write_err:
+ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
+-	return 0;
++	return ret;
+ }
+ 
+ static int macronix_quad_enable(struct spi_nor *nor)
+@@ -824,7 +808,7 @@ static int macronix_quad_enable(struct s
+ 	nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
+ 	nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
+ 
+-	if (wait_till_ready(nor))
++	if (spi_nor_wait_till_ready(nor))
+ 		return 1;
+ 
+ 	ret = read_sr(nor);
+@@ -906,8 +890,6 @@ static int spi_nor_check(struct spi_nor
+ 
+ 	if (!nor->read_id)
+ 		nor->read_id = spi_nor_read_id;
+-	if (!nor->wait_till_ready)
+-		nor->wait_till_ready = spi_nor_wait_till_ready;
+ 
+ 	return 0;
+ }
+@@ -978,9 +960,8 @@ int spi_nor_scan(struct spi_nor *nor, co
+ 	else
+ 		mtd->_write = spi_nor_write;
+ 
+-	if ((info->flags & USE_FSR) &&
+-	    nor->wait_till_ready == spi_nor_wait_till_ready)
+-		nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
++	if (info->flags & USE_FSR)
++		nor->flags |= SNOR_F_USE_FSR;
+ 
+ #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ 	/* prefer "small sector" erase if possible */
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -116,6 +116,10 @@ enum spi_nor_ops {
+ 	SPI_NOR_OPS_UNLOCK,
+ };
+ 
++enum spi_nor_option_flags {
++	SNOR_F_USE_FSR		= BIT(0),
++};
++
+ /**
+  * struct spi_nor - Structure for defining a the SPI NOR layer
+  * @mtd:		point to a mtd_info structure
+@@ -129,6 +133,7 @@ enum spi_nor_ops {
+  * @program_opcode:	the program opcode
+  * @flash_read:		the mode of the read
+  * @sst_write_second:	used by the SST write operation
++ * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
+  * @cfg:		used by the read_xfer/write_xfer
+  * @cmd_buf:		used by the write_reg
+  * @prepare:		[OPTIONAL] do some preparations for the
+@@ -141,7 +146,6 @@ enum spi_nor_ops {
+  * @write_reg:		[DRIVER-SPECIFIC] write data to the register
+  * @read_id:		[REPLACEABLE] read out the ID data, and find
+  *			the proper spi_device_id
+- * @wait_till_ready:	[REPLACEABLE] wait till the NOR becomes ready
+  * @read:		[DRIVER-SPECIFIC] read data from the SPI NOR
+  * @write:		[DRIVER-SPECIFIC] write data to the SPI NOR
+  * @erase:		[DRIVER-SPECIFIC] erase a sector of the SPI NOR
+@@ -160,6 +164,7 @@ struct spi_nor {
+ 	u8			program_opcode;
+ 	enum read_mode		flash_read;
+ 	bool			sst_write_second;
++	u32			flags;
+ 	struct spi_nor_xfer_cfg	cfg;
+ 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
+ 
+@@ -173,7 +178,6 @@ struct spi_nor {
+ 	int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
+ 			int write_enable);
+ 	const struct spi_device_id *(*read_id)(struct spi_nor *nor);
+-	int (*wait_till_ready)(struct spi_nor *nor);
+ 
+ 	int (*read)(struct spi_nor *nor, loff_t from,
+ 			size_t len, size_t *retlen, u_char *read_buf);

+ 263 - 0
target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch

@@ -0,0 +1,263 @@
+--- a/drivers/mtd/spi-nor/Kconfig
++++ b/drivers/mtd/spi-nor/Kconfig
+@@ -28,4 +28,10 @@ config SPI_FSL_QUADSPI
+ 	  This enables support for the Quad SPI controller in master mode.
+ 	  We only connect the NOR to this controller now.
+ 
++config MTD_SPI_BCM53XXSPIFLASH
++	tristate "SPI-NOR flashes connected to the Broadcom ARM SoC"
++	depends on MTD_SPI_NOR
++	help
++	  SPI driver for flashes used on Broadcom ARM SoCs.
++
+ endif # MTD_SPI_NOR
+--- a/drivers/mtd/spi-nor/Makefile
++++ b/drivers/mtd/spi-nor/Makefile
+@@ -1,2 +1,3 @@
+ obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor.o
+ obj-$(CONFIG_SPI_FSL_QUADSPI)	+= fsl-quadspi.o
++obj-$(CONFIG_MTD_SPI_BCM53XXSPIFLASH)	+= bcm53xxspiflash.o
+--- /dev/null
++++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c
+@@ -0,0 +1,241 @@
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/spi/spi.h>
++#include <linux/mtd/spi-nor.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/cfi.h>
++
++static const char * const probes[] = { "bcm47xxpart", NULL };
++
++struct bcm53xxsf {
++	struct spi_device *spi;
++	struct mtd_info mtd;
++	struct spi_nor nor;
++};
++
++/**************************************************
++ * spi-nor API
++ **************************************************/
++
++static int bcm53xxspiflash_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
++				   int len)
++{
++	struct bcm53xxsf *b53sf = nor->priv;
++
++	return spi_write_then_read(b53sf->spi, &opcode, 1, buf, len);
++}
++
++static int bcm53xxspiflash_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
++				     int len, int write_enable)
++{
++	struct bcm53xxsf *b53sf = nor->priv;
++	u8 *cmd = kzalloc(len + 1, GFP_KERNEL);
++	int err;
++
++	if (!cmd)
++		return -ENOMEM;
++
++	cmd[0] = opcode;
++	memcpy(&cmd[1], buf, len);
++	err = spi_write(b53sf->spi, cmd, len + 1);
++
++	kfree(cmd);
++
++	return err;
++}
++
++static int bcm53xxspiflash_read(struct spi_nor *nor, loff_t from, size_t len,
++				size_t *retlen, u_char *buf)
++{
++	struct bcm53xxsf *b53sf = nor->priv;
++	struct spi_message m;
++	struct spi_transfer t[2] = { { 0 }, { 0 } };
++	unsigned char cmd[5];
++	int cmd_len = 0;
++	int err;
++
++	spi_message_init(&m);
++
++	cmd[cmd_len++] = SPINOR_OP_READ;
++	if (b53sf->mtd.size > 0x1000000)
++		cmd[cmd_len++] = (from & 0xFF000000) >> 24;
++	cmd[cmd_len++] = (from & 0x00FF0000) >> 16;
++	cmd[cmd_len++] = (from & 0x0000FF00) >> 8;
++	cmd[cmd_len++] = (from & 0x000000FF) >> 0;
++
++	t[0].tx_buf = cmd;
++	t[0].len = cmd_len;
++	spi_message_add_tail(&t[0], &m);
++
++	t[1].rx_buf = buf;
++	t[1].len = len;
++	spi_message_add_tail(&t[1], &m);
++
++	err = spi_sync(b53sf->spi, &m);
++	if (err)
++		return err;
++
++	if (retlen && m.actual_length > cmd_len)
++		*retlen = m.actual_length - cmd_len;
++
++	return 0;
++}
++
++static void bcm53xxspiflash_write(struct spi_nor *nor, loff_t to, size_t len,
++				  size_t *retlen, const u_char *buf)
++{
++	struct bcm53xxsf *b53sf = nor->priv;
++	struct spi_message m;
++	struct spi_transfer t = { 0 };
++	u8 *cmd = kzalloc(len + 5, GFP_KERNEL);
++	int cmd_len = 0;
++	int err;
++
++	if (!cmd)
++		return;
++
++	spi_message_init(&m);
++
++	cmd[cmd_len++] = nor->program_opcode;
++	if (b53sf->mtd.size > 0x1000000)
++		cmd[cmd_len++] = (to & 0xFF000000) >> 24;
++	cmd[cmd_len++] = (to & 0x00FF0000) >> 16;
++	cmd[cmd_len++] = (to & 0x0000FF00) >> 8;
++	cmd[cmd_len++] = (to & 0x000000FF) >> 0;
++	memcpy(&cmd[cmd_len], buf, len);
++
++	t.tx_buf = cmd;
++	t.len = cmd_len + len;
++	spi_message_add_tail(&t, &m);
++
++	err = spi_sync(b53sf->spi, &m);
++	if (err)
++		goto out;
++
++	if (retlen && m.actual_length > cmd_len)
++		*retlen += m.actual_length - cmd_len;
++
++out:
++	kfree(cmd);
++}
++
++static int bcm53xxspiflash_erase(struct spi_nor *nor, loff_t offs)
++{
++	struct bcm53xxsf *b53sf = nor->priv;
++	unsigned char cmd[5];
++	int i;
++
++	i = 0;
++	cmd[i++] = nor->erase_opcode;
++	if (b53sf->mtd.size > 0x1000000)
++		cmd[i++] = (offs & 0xFF000000) >> 24;
++	cmd[i++] = ((offs & 0x00FF0000) >> 16);
++	cmd[i++] = ((offs & 0x0000FF00) >> 8);
++	cmd[i++] = ((offs & 0x000000FF) >> 0);
++
++	return spi_write(b53sf->spi, cmd, i);
++}
++
++static const struct spi_device_id *bcm53xxspiflash_read_id(struct spi_nor *nor)
++{
++	struct bcm53xxsf *b53sf = nor->priv;
++	struct device *dev = &b53sf->spi->dev;
++	const struct spi_device_id *id;
++	unsigned char cmd[4];
++	unsigned char resp[2];
++	char *name = NULL;
++	int err;
++
++	/* SST and Winbond/NexFlash specific command */
++	cmd[0] = 0x90; /* Read Manufacturer / Device ID */
++	cmd[1] = 0;
++	cmd[2] = 0;
++	cmd[3] = 0;
++	err = spi_write_then_read(b53sf->spi, cmd, 4, resp, 2);
++	if (err < 0) {
++		dev_err(dev, "error reading SPI flash id\n");
++		return ERR_PTR(-EBUSY);
++	}
++	switch (resp[0]) {
++	case 0xef: /* Winbond/NexFlash */
++		switch (resp[1]) {
++		case 0x17:
++			name = "w25q128";
++			break;
++		}
++		if (!name) {
++			dev_err(dev, "Unknown Winbond/NexFlash flash: %02X %02X\n",
++				resp[0], resp[1]);
++			return ERR_PTR(-ENOTSUPP);
++		}
++		goto found_name;
++	}
++
++	/* TODO: Try more ID commands */
++
++	return ERR_PTR(-ENODEV);
++
++found_name:
++	id = spi_nor_match_id(name);
++	if (!id) {
++		dev_err(dev, "No matching entry for %s flash\n", name);
++		return ERR_PTR(-ENOENT);
++	}
++
++	return id;
++}
++
++/**************************************************
++ * SPI driver
++ **************************************************/
++
++static int bcm53xxspiflash_probe(struct spi_device *spi)
++{
++	struct bcm53xxsf *b53sf;
++	int err;
++
++	b53sf = devm_kzalloc(&spi->dev, sizeof(*b53sf), GFP_KERNEL);
++	if (!b53sf)
++		return -ENOMEM;
++	spi_set_drvdata(spi, b53sf);
++
++	b53sf->spi = spi;
++
++	b53sf->mtd.priv = &b53sf->nor;
++
++	b53sf->nor.mtd = &b53sf->mtd;
++	b53sf->nor.dev = &spi->dev;
++	b53sf->nor.read_reg = bcm53xxspiflash_read_reg;
++	b53sf->nor.write_reg = bcm53xxspiflash_write_reg;
++	b53sf->nor.read = bcm53xxspiflash_read;
++	b53sf->nor.write = bcm53xxspiflash_write;
++	b53sf->nor.erase = bcm53xxspiflash_erase;
++	b53sf->nor.read_id = bcm53xxspiflash_read_id;
++	b53sf->nor.priv = b53sf;
++
++	err = spi_nor_scan(&b53sf->nor, NULL, SPI_NOR_NORMAL);
++	if (err)
++		return err;
++
++	err = mtd_device_parse_register(&b53sf->mtd, probes, NULL, NULL, 0);
++	if (err)
++		return err;
++
++	return 0;
++}
++
++static int bcm53xxspiflash_remove(struct spi_device *spi)
++{
++	return 0;
++}
++
++static struct spi_driver bcm53xxspiflash_driver = {
++	.driver = {
++		.name	= "bcm53xxspiflash",
++		.owner	= THIS_MODULE,
++	},
++	.probe		= bcm53xxspiflash_probe,
++	.remove		= bcm53xxspiflash_remove,
++};
++
++module_spi_driver(bcm53xxspiflash_driver);

+ 42 - 0
target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch

@@ -0,0 +1,42 @@
+--- a/drivers/mtd/spi-nor/bcm53xxspiflash.c
++++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c
+@@ -173,7 +173,8 @@ static const struct spi_device_id *bcm53
+ 
+ 	/* TODO: Try more ID commands */
+ 
+-	return ERR_PTR(-ENODEV);
++	/* Some chips used by Broadcom may actually support JEDEC */
++	return spi_nor_read_id(nor);
+ 
+ found_name:
+ 	id = spi_nor_match_id(name);
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -630,7 +630,7 @@ const struct spi_device_id spi_nor_ids[]
+ };
+ EXPORT_SYMBOL_GPL(spi_nor_ids);
+ 
+-static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
++const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
+ {
+ 	int			tmp;
+ 	u8			id[5];
+@@ -661,6 +661,7 @@ static const struct spi_device_id *spi_n
+ 	dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
+ 	return ERR_PTR(-ENODEV);
+ }
++EXPORT_SYMBOL_GPL(spi_nor_read_id);
+ 
+ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
+ 			size_t *retlen, u_char *buf)
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -188,6 +188,8 @@ struct spi_nor {
+ 	void *priv;
+ };
+ 
++const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor);
++
+ /**
+  * spi_nor_scan() - scan the SPI NOR
+  * @nor:	the spi_nor structure

+ 32 - 0
target/linux/bcm53xx/patches-3.18/410-mtd-bcm47xxpart-alloc-memory-for-more-partitions.patch

@@ -0,0 +1,32 @@
+From 6b833541d73894b5afd40d69949f8f6099db2abf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Thu, 2 Oct 2014 11:33:40 +0200
+Subject: [PATCH] mtd: bcm47xxpart: alloc memory for more partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is needed for some new Netgear devices (e.g. R6250).
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ drivers/mtd/bcm47xxpart.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/bcm47xxpart.c
++++ b/drivers/mtd/bcm47xxpart.c
+@@ -15,8 +15,12 @@
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+ 
+-/* 10 parts were found on sflash on Netgear WNDR4500 */
+-#define BCM47XXPART_MAX_PARTS		12
++/*
++ * NAND flash on Netgear R6250 was verified to contain 15 partitions.
++ * This will result in allocating too big array for some old devices, but the
++ * memory will be freed soon anyway (see mtd_device_parse_register).
++ */
++#define BCM47XXPART_MAX_PARTS		20
+ 
+ /*
+  * Amount of bytes we read when analyzing each block of flash memory.

+ 1616 - 0
target/linux/bcm53xx/patches-3.18/420-mtd-bcm5301x_nand.patch

@@ -0,0 +1,1616 @@
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -516,4 +516,10 @@ config MTD_NAND_XWAY
+ 	  Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
+ 	  to the External Bus Unit (EBU).
+ 
++config MTD_NAND_BCM
++	tristate "Support for NAND on some Broadcom SoC"
++	help
++	  This driver is currently used for the NAND flash controller on the
++	  Broadcom BCM5301X (NorthStar) SoCs.
++
+ endif # MTD_NAND
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -50,5 +50,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740
+ obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
+ obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
++obj-$(CONFIG_MTD_NAND_BCM)		+= bcm_nand.o
+ 
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
+--- /dev/null
++++ b/drivers/mtd/nand/bcm_nand.c
+@@ -0,0 +1,1591 @@
++/*
++ * Nortstar NAND controller driver
++ * for Linux NAND library and MTD interface
++ *
++ * (c) Broadcom, Inc. 2012 All Rights Reserved.
++ * Copyright 2014 Hauke Mehrtens <[email protected]>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ *
++ * This module interfaces the NAND controller and hardware ECC capabilities
++ * tp the generic NAND chip support in the NAND library.
++ *
++ * Notes:
++ *	This driver depends on generic NAND driver, but works at the
++ *	page level for operations.
++ *
++ *	When a page is written, the ECC calculated also protects the OOB
++ *	bytes not taken by ECC, and so the OOB must be combined with any
++ *	OOB data that preceded the page-write operation in order for the
++ *	ECC to be calculated correctly.
++ *	Also, when the page is erased, but OOB data is not, HW ECC will
++ *	indicate an error, because it checks OOB too, which calls for some
++ *	help from the software in this driver.
++ *
++ * TBD:
++ *	Block locking/unlocking support, OTP support
++ */
++
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/slab.h>
++#include <linux/bcma/bcma.h>
++#include <linux/of_irq.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++
++#define NANDC_MAX_CHIPS		2	/* Only 2 CSn supported in NorthStar */
++
++#define DRV_NAME	"bcmnand"
++#define DRV_DESC	"Northstar on-chip NAND Flash Controller driver"
++
++/*
++ * Driver private control structure
++ */
++struct bcmnand_ctrl {
++	struct mtd_info		mtd;
++	struct nand_chip	nand;
++	struct bcma_device	*core;
++
++	struct completion	op_completion;
++
++	struct nand_ecclayout	ecclayout;
++	int			cmd_ret;	/* saved error code */
++	unsigned char		oob_index;
++	unsigned char		id_byte_index;
++	unsigned char		chip_num;
++	unsigned char		last_cmd;
++	unsigned char		ecc_level;
++	unsigned char		sector_size_shift;
++	unsigned char		sec_per_page_shift;
++};
++
++
++/*
++ * IRQ numbers - offset from first irq in nandc_irq resource
++ */
++#define NANDC_IRQ_RD_MISS		0
++#define NANDC_IRQ_ERASE_COMPLETE	1
++#define NANDC_IRQ_COPYBACK_COMPLETE	2
++#define NANDC_IRQ_PROGRAM_COMPLETE	3
++#define NANDC_IRQ_CONTROLLER_RDY	4
++#define NANDC_IRQ_RDBSY_RDY		5
++#define NANDC_IRQ_ECC_UNCORRECTABLE	6
++#define NANDC_IRQ_ECC_CORRECTABLE	7
++#define NANDC_IRQ_NUM			8
++
++struct bcmnand_reg_field {
++	unsigned int reg;
++	unsigned int pos;
++	unsigned int width;
++};
++
++/*
++ * REGISTERS
++ *
++ * Individual bit-fields aof registers are specificed here
++ * for clarity, and the rest of the code will access each field
++ * as if it was its own register.
++ *
++ * Following registers are off <reg_base>:
++ */
++#define REG_BIT_FIELD(r, p, w)	((struct bcmnand_reg_field){(r), (p), (w)})
++
++#define NANDC_8KB_PAGE_SUPPORT		REG_BIT_FIELD(0x0, 31, 1)
++#define NANDC_REV_MAJOR			REG_BIT_FIELD(0x0, 8, 8)
++#define NANDC_REV_MINOR			REG_BIT_FIELD(0x0, 0, 8)
++
++#define NANDC_CMD_START_OPCODE		REG_BIT_FIELD(0x4, 24, 5)
++
++#define NANDC_CMD_CS_SEL		REG_BIT_FIELD(0x8, 16, 3)
++#define NANDC_CMD_EXT_ADDR		REG_BIT_FIELD(0x8, 0, 16)
++
++#define NANDC_CMD_ADDRESS		REG_BIT_FIELD(0xc, 0, 32)
++#define NANDC_CMD_END_ADDRESS		REG_BIT_FIELD(0x10, 0, 32)
++
++#define NANDC_INT_STATUS		REG_BIT_FIELD(0x14, 0, 32)
++#define NANDC_INT_STAT_CTLR_RDY		REG_BIT_FIELD(0x14, 31, 1)
++#define NANDC_INT_STAT_FLASH_RDY	REG_BIT_FIELD(0x14, 30, 1)
++#define NANDC_INT_STAT_CACHE_VALID	REG_BIT_FIELD(0x14, 29, 1)
++#define NANDC_INT_STAT_SPARE_VALID	REG_BIT_FIELD(0x14, 28, 1)
++#define NANDC_INT_STAT_ERASED		REG_BIT_FIELD(0x14, 27, 1)
++#define NANDC_INT_STAT_PLANE_RDY	REG_BIT_FIELD(0x14, 26, 1)
++#define NANDC_INT_STAT_FLASH_STATUS	REG_BIT_FIELD(0x14, 0, 8)
++
++#define NANDC_CS_LOCK			REG_BIT_FIELD(0x18, 31, 1)
++#define NANDC_CS_AUTO_CONFIG		REG_BIT_FIELD(0x18, 30, 1)
++#define NANDC_CS_NAND_WP		REG_BIT_FIELD(0x18, 29, 1)
++#define NANDC_CS_BLK0_WP		REG_BIT_FIELD(0x18, 28, 1)
++#define NANDC_CS_SW_USING_CS(n)		REG_BIT_FIELD(0x18, 8+(n), 1)
++#define NANDC_CS_MAP_SEL_CS(n)		REG_BIT_FIELD(0x18, 0+(n), 1)
++
++#define NANDC_XOR_ADDR_BLK0_ONLY	REG_BIT_FIELD(0x1c, 31, 1)
++#define NANDC_XOR_ADDR_CS(n)		REG_BIT_FIELD(0x1c, 0+(n), 1)
++
++#define NANDC_LL_OP_RET_IDLE		REG_BIT_FIELD(0x20, 31, 1)
++#define NANDC_LL_OP_CLE			REG_BIT_FIELD(0x20, 19, 1)
++#define NANDC_LL_OP_ALE			REG_BIT_FIELD(0x20, 18, 1)
++#define NANDC_LL_OP_WE			REG_BIT_FIELD(0x20, 17, 1)
++#define NANDC_LL_OP_RE			REG_BIT_FIELD(0x20, 16, 1)
++#define NANDC_LL_OP_DATA		REG_BIT_FIELD(0x20, 0, 16)
++
++#define NANDC_MPLANE_ADDR_EXT		REG_BIT_FIELD(0x24, 0, 16)
++#define NANDC_MPLANE_ADDR		REG_BIT_FIELD(0x28, 0, 32)
++
++#define NANDC_ACC_CTRL_CS(n)		REG_BIT_FIELD(0x50+((n)<<4), 0, 32)
++#define NANDC_ACC_CTRL_RD_ECC(n)	REG_BIT_FIELD(0x50+((n)<<4), 31, 1)
++#define NANDC_ACC_CTRL_WR_ECC(n)	REG_BIT_FIELD(0x50+((n)<<4), 30, 1)
++#define NANDC_ACC_CTRL_CE_CARE(n)	REG_BIT_FIELD(0x50+((n)<<4), 29, 1)
++#define NANDC_ACC_CTRL_PGM_RDIN(n)	REG_BIT_FIELD(0x50+((n)<<4), 28, 1)
++#define NANDC_ACC_CTRL_ERA_ECC_ERR(n)	REG_BIT_FIELD(0x50+((n)<<4), 27, 1)
++#define NANDC_ACC_CTRL_PGM_PARTIAL(n)	REG_BIT_FIELD(0x50+((n)<<4), 26, 1)
++#define NANDC_ACC_CTRL_WR_PREEMPT(n)	REG_BIT_FIELD(0x50+((n)<<4), 25, 1)
++#define NANDC_ACC_CTRL_PG_HIT(n)	REG_BIT_FIELD(0x50+((n)<<4), 24, 1)
++#define NANDC_ACC_CTRL_PREFETCH(n)	REG_BIT_FIELD(0x50+((n)<<4), 23, 1)
++#define NANDC_ACC_CTRL_CACHE_MODE(n)	REG_BIT_FIELD(0x50+((n)<<4), 22, 1)
++#define NANDC_ACC_CTRL_CACHE_LASTPG(n)	REG_BIT_FIELD(0x50+((n)<<4), 21, 1)
++#define NANDC_ACC_CTRL_ECC_LEVEL(n)	REG_BIT_FIELD(0x50+((n)<<4), 16, 5)
++#define NANDC_ACC_CTRL_SECTOR_1K(n)	REG_BIT_FIELD(0x50+((n)<<4), 7, 1)
++#define NANDC_ACC_CTRL_SPARE_SIZE(n)	REG_BIT_FIELD(0x50+((n)<<4), 0, 7)
++
++#define NANDC_CONFIG_CS(n)		REG_BIT_FIELD(0x54+((n)<<4), 0, 32)
++#define NANDC_CONFIG_LOCK(n)		REG_BIT_FIELD(0x54+((n)<<4), 31, 1)
++#define NANDC_CONFIG_BLK_SIZE(n)	REG_BIT_FIELD(0x54+((n)<<4), 28, 3)
++#define NANDC_CONFIG_CHIP_SIZE(n)	REG_BIT_FIELD(0x54+((n)<<4), 24, 4)
++#define NANDC_CONFIG_CHIP_WIDTH(n)	REG_BIT_FIELD(0x54+((n)<<4), 23, 1)
++#define NANDC_CONFIG_PAGE_SIZE(n)	REG_BIT_FIELD(0x54+((n)<<4), 20, 2)
++#define NANDC_CONFIG_FUL_ADDR_BYTES(n)	REG_BIT_FIELD(0x54+((n)<<4), 16, 3)
++#define NANDC_CONFIG_COL_ADDR_BYTES(n)	REG_BIT_FIELD(0x54+((n)<<4), 12, 3)
++#define NANDC_CONFIG_BLK_ADDR_BYTES(n)	REG_BIT_FIELD(0x54+((n)<<4), 8, 3)
++
++#define NANDC_TIMING_1_CS(n)		REG_BIT_FIELD(0x58+((n)<<4), 0, 32)
++#define NANDC_TIMING_2_CS(n)		REG_BIT_FIELD(0x5c+((n)<<4), 0, 32)
++	/* Individual bits for Timing registers - TBD */
++
++#define NANDC_CORR_STAT_THRESH_CS(n)	REG_BIT_FIELD(0xc0, 6*(n), 6)
++
++#define NANDC_BLK_WP_END_ADDR		REG_BIT_FIELD(0xc8, 0, 32)
++
++#define NANDC_MPLANE_ERASE_CYC2_OPCODE	REG_BIT_FIELD(0xcc, 24, 8)
++#define NANDC_MPLANE_READ_STAT_OPCODE	REG_BIT_FIELD(0xcc, 16, 8)
++#define NANDC_MPLANE_PROG_ODD_OPCODE	REG_BIT_FIELD(0xcc, 8, 8)
++#define NANDC_MPLANE_PROG_TRL_OPCODE	REG_BIT_FIELD(0xcc, 0, 8)
++
++#define NANDC_MPLANE_PGCACHE_TRL_OPCODE	REG_BIT_FIELD(0xd0, 24, 8)
++#define NANDC_MPLANE_READ_STAT2_OPCODE	REG_BIT_FIELD(0xd0, 16, 8)
++#define NANDC_MPLANE_READ_EVEN_OPCODE	REG_BIT_FIELD(0xd0, 8, 8)
++#define NANDC_MPLANE_READ_ODD__OPCODE	REG_BIT_FIELD(0xd0, 0, 8)
++
++#define NANDC_MPLANE_CTRL_ERASE_CYC2_EN	REG_BIT_FIELD(0xd4, 31, 1)
++#define NANDC_MPLANE_CTRL_RD_ADDR_SIZE	REG_BIT_FIELD(0xd4, 30, 1)
++#define NANDC_MPLANE_CTRL_RD_CYC_ADDR	REG_BIT_FIELD(0xd4, 29, 1)
++#define NANDC_MPLANE_CTRL_RD_COL_ADDR	REG_BIT_FIELD(0xd4, 28, 1)
++
++#define NANDC_UNCORR_ERR_COUNT		REG_BIT_FIELD(0xfc, 0, 32)
++
++#define NANDC_CORR_ERR_COUNT		REG_BIT_FIELD(0x100, 0, 32)
++
++#define NANDC_READ_CORR_BIT_COUNT	REG_BIT_FIELD(0x104, 0, 32)
++
++#define NANDC_BLOCK_LOCK_STATUS		REG_BIT_FIELD(0x108, 0, 8)
++
++#define NANDC_ECC_CORR_ADDR_CS		REG_BIT_FIELD(0x10c, 16, 3)
++#define NANDC_ECC_CORR_ADDR_EXT		REG_BIT_FIELD(0x10c, 0, 16)
++
++#define NANDC_ECC_CORR_ADDR		REG_BIT_FIELD(0x110, 0, 32)
++
++#define NANDC_ECC_UNC_ADDR_CS		REG_BIT_FIELD(0x114, 16, 3)
++#define NANDC_ECC_UNC_ADDR_EXT		REG_BIT_FIELD(0x114, 0, 16)
++
++#define NANDC_ECC_UNC_ADDR		REG_BIT_FIELD(0x118, 0, 32)
++
++#define NANDC_READ_ADDR_CS		REG_BIT_FIELD(0x11c, 16, 3)
++#define NANDC_READ_ADDR_EXT		REG_BIT_FIELD(0x11c, 0, 16)
++#define NANDC_READ_ADDR			REG_BIT_FIELD(0x120, 0, 32)
++
++#define NANDC_PROG_ADDR_CS		REG_BIT_FIELD(0x124, 16, 3)
++#define NANDC_PROG_ADDR_EXT		REG_BIT_FIELD(0x124, 0, 16)
++#define NANDC_PROG_ADDR			REG_BIT_FIELD(0x128, 0, 32)
++
++#define NANDC_CPYBK_ADDR_CS		REG_BIT_FIELD(0x12c, 16, 3)
++#define NANDC_CPYBK_ADDR_EXT		REG_BIT_FIELD(0x12c, 0, 16)
++#define NANDC_CPYBK_ADDR		REG_BIT_FIELD(0x130, 0, 32)
++
++#define NANDC_ERASE_ADDR_CS		REG_BIT_FIELD(0x134, 16, 3)
++#define NANDC_ERASE_ADDR_EXT		REG_BIT_FIELD(0x134, 0, 16)
++#define NANDC_ERASE_ADDR		REG_BIT_FIELD(0x138, 0, 32)
++
++#define NANDC_INV_READ_ADDR_CS		REG_BIT_FIELD(0x13c, 16, 3)
++#define NANDC_INV_READ_ADDR_EXT		REG_BIT_FIELD(0x13c, 0, 16)
++#define NANDC_INV_READ_ADDR		REG_BIT_FIELD(0x140, 0, 32)
++
++#define NANDC_INIT_STAT			REG_BIT_FIELD(0x144, 0, 32)
++#define NANDC_INIT_ONFI_DONE		REG_BIT_FIELD(0x144, 31, 1)
++#define NANDC_INIT_DEVID_DONE		REG_BIT_FIELD(0x144, 30, 1)
++#define NANDC_INIT_SUCCESS		REG_BIT_FIELD(0x144, 29, 1)
++#define NANDC_INIT_FAIL			REG_BIT_FIELD(0x144, 28, 1)
++#define NANDC_INIT_BLANK		REG_BIT_FIELD(0x144, 27, 1)
++#define NANDC_INIT_TIMEOUT		REG_BIT_FIELD(0x144, 26, 1)
++#define NANDC_INIT_UNC_ERROR		REG_BIT_FIELD(0x144, 25, 1)
++#define NANDC_INIT_CORR_ERROR		REG_BIT_FIELD(0x144, 24, 1)
++#define NANDC_INIT_PARAM_RDY		REG_BIT_FIELD(0x144, 23, 1)
++#define NANDC_INIT_AUTH_FAIL		REG_BIT_FIELD(0x144, 22, 1)
++
++#define NANDC_ONFI_STAT			REG_BIT_FIELD(0x148, 0, 32)
++#define NANDC_ONFI_DEBUG		REG_BIT_FIELD(0x148, 28, 4)
++#define NANDC_ONFI_PRESENT		REG_BIT_FIELD(0x148, 27, 1)
++#define NANDC_ONFI_BADID_PG2		REG_BIT_FIELD(0x148, 5, 1)
++#define NANDC_ONFI_BADID_PG1		REG_BIT_FIELD(0x148, 4, 1)
++#define NANDC_ONFI_BADID_PG0		REG_BIT_FIELD(0x148, 3, 1)
++#define NANDC_ONFI_BADCRC_PG2		REG_BIT_FIELD(0x148, 2, 1)
++#define NANDC_ONFI_BADCRC_PG1		REG_BIT_FIELD(0x148, 1, 1)
++#define NANDC_ONFI_BADCRC_PG0		REG_BIT_FIELD(0x148, 0, 1)
++
++#define NANDC_ONFI_DEBUG_DATA		REG_BIT_FIELD(0x14c, 0, 32)
++
++#define NANDC_SEMAPHORE			REG_BIT_FIELD(0x150, 0, 8)
++
++#define NANDC_DEVID_BYTE(b)		REG_BIT_FIELD(0x194+((b)&0x4), \
++						24-(((b)&3)<<3), 8)
++
++#define NANDC_LL_RDDATA			REG_BIT_FIELD(0x19c, 0, 16)
++
++#define NANDC_INT_N_REG(n)		REG_BIT_FIELD(0xf00|((n)<<2), 0, 1)
++#define NANDC_INT_DIREC_READ_MISS	REG_BIT_FIELD(0xf00, 0, 1)
++#define NANDC_INT_ERASE_DONE		REG_BIT_FIELD(0xf04, 0, 1)
++#define NANDC_INT_CPYBK_DONE		REG_BIT_FIELD(0xf08, 0, 1)
++#define NANDC_INT_PROGRAM_DONE		REG_BIT_FIELD(0xf0c, 0, 1)
++#define NANDC_INT_CONTROLLER_RDY	REG_BIT_FIELD(0xf10, 0, 1)
++#define NANDC_INT_RDBSY_RDY		REG_BIT_FIELD(0xf14, 0, 1)
++#define NANDC_INT_ECC_UNCORRECTABLE	REG_BIT_FIELD(0xf18, 0, 1)
++#define NANDC_INT_ECC_CORRECTABLE	REG_BIT_FIELD(0xf1c, 0, 1)
++
++/*
++ * Following  registers are treated as contigous IO memory, offset is from
++ * <reg_base>, and the data is in big-endian byte order
++ */
++#define NANDC_SPARE_AREA_READ_OFF	0x200
++#define NANDC_SPARE_AREA_WRITE_OFF	0x280
++#define NANDC_CACHE_OFF			0x400
++#define NANDC_CACHE_SIZE		(128*4)
++
++struct bcmnand_areg_field {
++	unsigned int reg;
++	unsigned int pos;
++	unsigned int width;
++};
++
++/*
++ * Following are IDM (a.k.a. Slave Wrapper) registers are off <idm_base>:
++ */
++#define IDMREG_BIT_FIELD(r, p, w)	((struct bcmnand_areg_field){(r), (p), (w)})
++
++#define NANDC_IDM_AXI_BIG_ENDIAN	IDMREG_BIT_FIELD(0x408, 28, 1)
++#define NANDC_IDM_APB_LITTLE_ENDIAN	IDMREG_BIT_FIELD(0x408, 24, 1)
++#define NANDC_IDM_TM			IDMREG_BIT_FIELD(0x408, 16, 5)
++#define NANDC_IDM_IRQ_CORRECABLE_EN	IDMREG_BIT_FIELD(0x408, 9, 1)
++#define NANDC_IDM_IRQ_UNCORRECABLE_EN	IDMREG_BIT_FIELD(0x408, 8, 1)
++#define NANDC_IDM_IRQ_RDYBSY_RDY_EN	IDMREG_BIT_FIELD(0x408, 7, 1)
++#define NANDC_IDM_IRQ_CONTROLLER_RDY_EN	IDMREG_BIT_FIELD(0x408, 6, 1)
++#define NANDC_IDM_IRQ_PRPOGRAM_COMP_EN	IDMREG_BIT_FIELD(0x408, 5, 1)
++#define NANDC_IDM_IRQ_COPYBK_COMP_EN	IDMREG_BIT_FIELD(0x408, 4, 1)
++#define NANDC_IDM_IRQ_ERASE_COMP_EN	IDMREG_BIT_FIELD(0x408, 3, 1)
++#define NANDC_IDM_IRQ_READ_MISS_EN	IDMREG_BIT_FIELD(0x408, 2, 1)
++#define NANDC_IDM_IRQ_N_EN(n)		IDMREG_BIT_FIELD(0x408, 2+(n), 1)
++
++#define NANDC_IDM_CLOCK_EN		IDMREG_BIT_FIELD(0x408, 0, 1)
++
++#define NANDC_IDM_IO_ECC_CORR		IDMREG_BIT_FIELD(0x500, 3, 1)
++#define NANDC_IDM_IO_ECC_UNCORR		IDMREG_BIT_FIELD(0x500, 2, 1)
++#define NANDC_IDM_IO_RDYBSY		IDMREG_BIT_FIELD(0x500, 1, 1)
++#define NANDC_IDM_IO_CTRL_RDY		IDMREG_BIT_FIELD(0x500, 0, 1)
++
++#define NANDC_IDM_RESET			IDMREG_BIT_FIELD(0x800, 0, 1)
++	/* Remaining IDM registers do not seem to be useful, skipped */
++
++/*
++ * NAND Controller has its own command opcodes
++ * different from opcodes sent to the actual flash chip
++ */
++#define NANDC_CMD_OPCODE_NULL		0
++#define NANDC_CMD_OPCODE_PAGE_READ	1
++#define NANDC_CMD_OPCODE_SPARE_READ	2
++#define NANDC_CMD_OPCODE_STATUS_READ	3
++#define NANDC_CMD_OPCODE_PAGE_PROG	4
++#define NANDC_CMD_OPCODE_SPARE_PROG	5
++#define NANDC_CMD_OPCODE_DEVID_READ	7
++#define NANDC_CMD_OPCODE_BLOCK_ERASE	8
++#define NANDC_CMD_OPCODE_FLASH_RESET	9
++
++/*
++ * NAND Controller hardware ECC data size
++ *
++ * The following table contains the number of bytes needed for
++ * each of the ECC levels, per "sector", which is either 512 or 1024 bytes.
++ * The actual layout is as follows:
++ * The entire spare area is equally divided into as many sections as there
++ * are sectors per page, and the ECC data is located at the end of each
++ * of these sections.
++ * For example, given a 2K per page and 64 bytes spare device, configured for
++ * sector size 1k and ECC level of 4, the spare area will be divided into 2
++ * sections 32 bytes each, and the last 14 bytes of 32 in each section will
++ * be filled with ECC data.
++ * Note: the name of the algorythm and the number of error bits it can correct
++ * is of no consequence to this driver, therefore omitted.
++ */
++struct bcmnand_ecc_size_s {
++	unsigned char sector_size_shift;
++	unsigned char ecc_level;
++	unsigned char ecc_bytes_per_sec;
++	unsigned char reserved;
++};
++
++static const struct bcmnand_ecc_size_s bcmnand_ecc_sizes[] = {
++	{ 9,	0,	0 },
++	{ 10,	0,	0 },
++	{ 9,	1,	2 },
++	{ 10,	1,	4 },
++	{ 9,	2,	4 },
++	{ 10,	2,	7 },
++	{ 9,	3,	6 },
++	{ 10,	3,	11 },
++	{ 9,	4,	7 },
++	{ 10,	4,	14 },
++	{ 9,	5,	9 },
++	{ 10,	5,	18 },
++	{ 9,	6,	11 },
++	{ 10,	6,	21 },
++	{ 9,	7,	13 },
++	{ 10,	7,	25 },
++	{ 9,	8,	14 },
++	{ 10,	8,	28 },
++
++	{ 9,	9,	16 },
++	{ 9,	10,	18 },
++	{ 9,	11,	20 },
++	{ 9,	12,	21 },
++
++	{ 10,	9,	32 },
++	{ 10,	10,	35 },
++	{ 10,	11,	39 },
++	{ 10,	12,	42 },
++};
++
++/*
++ * Populate the various fields that depend on how
++ * the hardware ECC data is located in the spare area
++ *
++ * For this controiller, it is easier to fill-in these
++ * structures at run time.
++ *
++ * The bad-block marker is assumed to occupy one byte
++ * at chip->badblockpos, which must be in the first
++ * sector of the spare area, namely it is either
++ * at offset 0 or 5.
++ * Some chips use both for manufacturer's bad block
++ * markers, but we ingore that issue here, and assume only
++ * one byte is used as bad-block marker always.
++ */
++static int bcmnand_hw_ecc_layout(struct bcmnand_ctrl *ctrl)
++{
++	struct nand_ecclayout *layout;
++	unsigned int i, j, k;
++	unsigned int ecc_per_sec, oob_per_sec;
++	unsigned int bbm_pos = ctrl->nand.badblockpos;
++
++	/* Caclculate spare area per sector size */
++	oob_per_sec = ctrl->mtd.oobsize >> ctrl->sec_per_page_shift;
++
++	/* Try to calculate the amount of ECC bytes per sector with a formula */
++	if (ctrl->sector_size_shift == 9)
++		ecc_per_sec = ((ctrl->ecc_level * 14) + 7) >> 3;
++	else if (ctrl->sector_size_shift == 10)
++		ecc_per_sec = ((ctrl->ecc_level * 14) + 3) >> 2;
++	else
++		ecc_per_sec = oob_per_sec + 1;	/* cause an error if not in table */
++
++	/* Now find out the answer according to the table */
++	for (i = 0; i < ARRAY_SIZE(bcmnand_ecc_sizes); i++) {
++		if (bcmnand_ecc_sizes[i].ecc_level == ctrl->ecc_level &&
++		    bcmnand_ecc_sizes[i].sector_size_shift ==
++				ctrl->sector_size_shift) {
++			break;
++		}
++	}
++
++	/* Table match overrides formula */
++	if (bcmnand_ecc_sizes[i].ecc_level == ctrl->ecc_level &&
++	    bcmnand_ecc_sizes[i].sector_size_shift == ctrl->sector_size_shift)
++		ecc_per_sec = bcmnand_ecc_sizes[i].ecc_bytes_per_sec;
++
++	/* Return an error if calculated ECC leaves no room for OOB */
++	if ((ctrl->sec_per_page_shift != 0 && ecc_per_sec >= oob_per_sec) ||
++	    (ctrl->sec_per_page_shift == 0 && ecc_per_sec >= (oob_per_sec - 1))) {
++		pr_err("%s: ECC level %d too high, leaves no room for OOB data\n",
++		       DRV_NAME, ctrl->ecc_level);
++		return -EINVAL;
++	}
++
++	/* Fill in the needed fields */
++	ctrl->nand.ecc.size = ctrl->mtd.writesize >> ctrl->sec_per_page_shift;
++	ctrl->nand.ecc.bytes = ecc_per_sec;
++	ctrl->nand.ecc.steps = 1 << ctrl->sec_per_page_shift;
++	ctrl->nand.ecc.total = ecc_per_sec << ctrl->sec_per_page_shift;
++	ctrl->nand.ecc.strength = ctrl->ecc_level;
++
++	/* Build an ecc layout data structure */
++	layout = &ctrl->ecclayout;
++	memset(layout, 0, sizeof(*layout));
++
++	/* Total number of bytes used by HW ECC */
++	layout->eccbytes = ecc_per_sec << ctrl->sec_per_page_shift;
++
++	/* Location for each of the HW ECC bytes */
++	for (i = j = 0, k = 1;
++	     i < ARRAY_SIZE(layout->eccpos) && i < layout->eccbytes;
++	     i++, j++) {
++		/* switch sector # */
++		if (j == ecc_per_sec) {
++			j = 0;
++			k++;
++		}
++		/* save position of each HW-generated ECC byte */
++		layout->eccpos[i] = (oob_per_sec * k) - ecc_per_sec + j;
++
++		/* Check that HW ECC does not overlap bad-block marker */
++		if (bbm_pos == layout->eccpos[i]) {
++			pr_err("%s: ECC level %d too high, HW ECC collides with bad-block marker position\n",
++			       DRV_NAME, ctrl->ecc_level);
++			return -EINVAL;
++		}
++	}
++
++	/* Location of all user-available OOB byte-ranges */
++	for (i = 0; i < ARRAY_SIZE(layout->oobfree); i++) {
++		struct nand_oobfree *oobfree = &layout->oobfree[i];
++
++		if (i >= (1 << ctrl->sec_per_page_shift))
++			break;
++		oobfree->offset = oob_per_sec * i;
++		oobfree->length = oob_per_sec - ecc_per_sec;
++
++		/* Bad-block marker must be in the first sector spare area */
++		if (WARN_ON(bbm_pos >= (oobfree->offset + oobfree->length)))
++			return -EINVAL;
++
++		if (i != 0)
++			continue;
++
++		/* Remove bad-block marker from available byte range */
++		if (bbm_pos == oobfree->offset) {
++			oobfree->offset += 1;
++			oobfree->length -= 1;
++		} else if (bbm_pos == (oobfree->offset + oobfree->length - 1)) {
++			oobfree->length -= 1;
++		} else {
++			layout->oobfree[i + 1].offset = bbm_pos + 1;
++			layout->oobfree[i + 1].length =
++				oobfree->length - bbm_pos - 1;
++			oobfree->length = bbm_pos;
++			i++;
++		}
++	}
++
++	layout->oobavail = ((oob_per_sec - ecc_per_sec)
++		<< ctrl->sec_per_page_shift) - 1;
++
++	ctrl->mtd.oobavail = layout->oobavail;
++	ctrl->nand.ecc.layout = layout;
++
++	/* Output layout for debugging */
++	pr_debug("%s: Spare area=%d eccbytes %d, ecc bytes located at:\n",
++		 DRV_NAME, ctrl->mtd.oobsize, layout->eccbytes);
++	for (i = j = 0;
++	     i < ARRAY_SIZE(layout->eccpos) && i < layout->eccbytes; i++)
++		pr_debug(" %d", layout->eccpos[i]);
++
++	pr_debug("\n%s: Available %d bytes at (off,len):\n", DRV_NAME,
++		 layout->oobavail);
++	for (i = 0; i < ARRAY_SIZE(layout->oobfree); i++)
++		pr_debug("(%d,%d) ", layout->oobfree[i].offset,
++			 layout->oobfree[i].length);
++
++	pr_debug("\n");
++
++	return 0;
++}
++
++/*
++ * Register bit-field manipulation routines
++ */
++
++static inline unsigned int bcmnand_reg_read(struct bcmnand_ctrl *ctrl,
++					    struct bcmnand_reg_field rbf)
++{
++	u32 val;
++
++	val = bcma_read32(ctrl->core, rbf.reg);
++	val >>= rbf.pos;
++	val &= (1 << rbf.width) - 1;
++
++	return val;
++}
++
++static inline void bcmnand_reg_write(struct bcmnand_ctrl *ctrl,
++				     struct bcmnand_reg_field rbf,
++				     unsigned newval)
++{
++	u32 val, msk;
++
++	msk = (1 << rbf.width) - 1;
++	msk <<= rbf.pos;
++	newval <<= rbf.pos;
++	newval &= msk;
++
++	val = bcma_read32(ctrl->core, rbf.reg);
++	val &= ~msk;
++	val |= newval;
++	bcma_write32(ctrl->core, rbf.reg, val);
++}
++
++static inline unsigned int bcmnand_reg_aread(struct bcmnand_ctrl *ctrl,
++					     struct bcmnand_areg_field rbf)
++{
++	u32 val;
++
++	val = bcma_aread32(ctrl->core, rbf.reg);
++	val >>= rbf.pos;
++	val &= (1 << rbf.width) - 1;
++
++	return val;
++}
++
++static inline void bcmnand_reg_awrite(struct bcmnand_ctrl *ctrl,
++				      struct bcmnand_areg_field rbf,
++				      unsigned int newval)
++{
++	u32 val, msk;
++
++	msk = (1 << rbf.width) - 1;
++	msk <<= rbf.pos;
++	newval <<= rbf.pos;
++	newval &= msk;
++
++	val = bcma_aread32(ctrl->core, rbf.reg);
++	val &= ~msk;
++	val |= newval;
++	bcma_awrite32(ctrl->core, rbf.reg, val);
++}
++
++/*
++ * NAND Interface - dev_ready
++ *
++ * Return 1 iff device is ready, 0 otherwise
++ */
++static int bcmnand_dev_ready(struct mtd_info *mtd)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct bcmnand_ctrl *ctrl = chip->priv;
++
++	return bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY);
++}
++
++/*
++ * Interrupt service routines
++ */
++static irqreturn_t bcmnand_isr(int irq, void *dev_id)
++{
++	struct bcmnand_ctrl *ctrl = dev_id;
++	int irq_off;
++
++	irq_off = irq - ctrl->core->irq;
++	WARN_ON(irq_off < 0 || irq_off >= NANDC_IRQ_NUM);
++
++	if (!bcmnand_reg_read(ctrl, NANDC_INT_N_REG(irq_off)))
++		return IRQ_NONE;
++
++	/* Acknowledge interrupt */
++	bcmnand_reg_write(ctrl, NANDC_INT_N_REG(irq_off), 1);
++
++	/* Wake up task */
++	complete(&ctrl->op_completion);
++
++	return IRQ_HANDLED;
++}
++
++static int bcmnand_wait_interrupt(struct bcmnand_ctrl *ctrl,
++				  unsigned int irq_off,
++				  unsigned int timeout_usec)
++{
++	long timeout_jiffies;
++	int ret = 0;
++
++	reinit_completion(&ctrl->op_completion);
++
++	/* Acknowledge interrupt */
++	bcmnand_reg_write(ctrl, NANDC_INT_N_REG(irq_off), 1);
++
++	/* Enable IRQ to wait on */
++	bcmnand_reg_awrite(ctrl, NANDC_IDM_IRQ_N_EN(irq_off), 1);
++
++	timeout_jiffies = 1 + usecs_to_jiffies(timeout_usec);
++
++	if (irq_off != NANDC_IRQ_CONTROLLER_RDY ||
++		0 == bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY)) {
++
++		timeout_jiffies = wait_for_completion_interruptible_timeout(
++					&ctrl->op_completion, timeout_jiffies);
++
++		if (timeout_jiffies < 0)
++			ret =  timeout_jiffies;
++		if (timeout_jiffies == 0)
++			ret = -ETIME;
++	}
++
++	/* Disable IRQ, we're done waiting */
++	bcmnand_reg_awrite(ctrl, NANDC_IDM_IRQ_N_EN(irq_off), 0);
++
++	if (bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY))
++		ret = 0;
++
++	return ret;
++}
++
++/*
++ * wait for command completion
++ */
++static int bcmnand_wait_cmd(struct bcmnand_ctrl *ctrl, unsigned int timeout_usec)
++{
++	unsigned int retries;
++
++	if (bcmnand_reg_read(ctrl, NANDC_INT_STAT_CTLR_RDY))
++		return 0;
++
++	/* If the timeout is long, wait for interrupt */
++	if (timeout_usec >= jiffies_to_usecs(1) >> 4)
++		return bcmnand_wait_interrupt(
++			ctrl, NANDC_IRQ_CONTROLLER_RDY, timeout_usec);
++
++	/* Wait for completion of the prior command */
++	retries = (timeout_usec >> 3) + 1;
++
++	while (retries-- &&
++		0 == bcmnand_reg_read(ctrl, NANDC_INT_STAT_CTLR_RDY)) {
++		cpu_relax();
++		udelay(6);
++	}
++
++	if (retries == 0)
++		return -ETIME;
++
++	return 0;
++}
++
++
++/*
++ * NAND Interface - waitfunc
++ */
++static int bcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
++{
++	struct bcmnand_ctrl *ctrl = chip->priv;
++	unsigned int to;
++	int ret;
++
++	/* figure out timeout based on what command is on */
++	switch (ctrl->last_cmd) {
++	default:
++	case NAND_CMD_ERASE1:
++	case NAND_CMD_ERASE2:
++		to = 1 << 16;
++		break;
++	case NAND_CMD_STATUS:
++	case NAND_CMD_RESET:
++		to = 256;
++		break;
++	case NAND_CMD_READID:
++		to = 1024;
++		break;
++	case NAND_CMD_READ1:
++	case NAND_CMD_READ0:
++		to = 2048;
++		break;
++	case NAND_CMD_PAGEPROG:
++		to = 4096;
++		break;
++	case NAND_CMD_READOOB:
++		to = 512;
++		break;
++	}
++
++	/* deliver deferred error code if any */
++	ret = ctrl->cmd_ret;
++	if (ret < 0)
++		ctrl->cmd_ret = 0;
++	else
++		ret = bcmnand_wait_cmd(ctrl, to);
++
++	/* Timeout */
++	if (ret < 0)
++		return NAND_STATUS_FAIL;
++
++	ret = bcmnand_reg_read(ctrl, NANDC_INT_STAT_FLASH_STATUS);
++
++	return ret;
++}
++
++/*
++ * NAND Interface - read_oob
++ */
++static int bcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
++			    int page)
++{
++	struct bcmnand_ctrl *ctrl = chip->priv;
++	unsigned int n = ctrl->chip_num;
++	void __iomem *ctrl_spare;
++	unsigned int spare_per_sec, sector;
++	u64 nand_addr;
++
++	ctrl_spare = ctrl->core->io_addr + NANDC_SPARE_AREA_READ_OFF;
++
++	/* Set the page address for the following commands */
++	nand_addr = ((u64)page << chip->page_shift);
++	bcmnand_reg_write(ctrl, NANDC_CMD_EXT_ADDR, nand_addr >> 32);
++
++	spare_per_sec = mtd->oobsize >> ctrl->sec_per_page_shift;
++
++	/* Disable ECC validation for spare area reads */
++	bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_RD_ECC(n), 0);
++
++	/* Loop all sectors in page */
++	for (sector = 0; sector < (1<<ctrl->sec_per_page_shift); sector++) {
++		unsigned int col;
++
++		col = (sector << ctrl->sector_size_shift);
++
++		/* Issue command to read partial page */
++		bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS, nand_addr + col);
++
++		bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++				  NANDC_CMD_OPCODE_SPARE_READ);
++
++		/* Wait for the command to complete */
++		if (bcmnand_wait_cmd(ctrl, (sector == 0) ? 10000 : 100))
++			return -EIO;
++
++		if (!bcmnand_reg_read(ctrl, NANDC_INT_STAT_SPARE_VALID))
++			return -EIO;
++
++		/* Set controller to Little Endian mode for copying */
++		bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
++
++		memcpy(chip->oob_poi + sector * spare_per_sec,
++			ctrl_spare,
++			spare_per_sec);
++
++		/* Return to Big Endian mode for commands etc */
++		bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
++	}
++
++	return 0;
++}
++
++/*
++ * NAND Interface - write_oob
++ */
++static int bcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
++			     int page)
++{
++	struct bcmnand_ctrl *ctrl = chip->priv;
++	unsigned int n = ctrl->chip_num;
++	void __iomem *ctrl_spare;
++	unsigned int spare_per_sec, sector, num_sec;
++	u64 nand_addr;
++	int to, status = 0;
++
++	ctrl_spare = ctrl->core->io_addr + NANDC_SPARE_AREA_WRITE_OFF;
++
++	/* Disable ECC generation for spare area writes */
++	bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_WR_ECC(n), 0);
++
++	spare_per_sec = mtd->oobsize >> ctrl->sec_per_page_shift;
++
++	/* Set the page address for the following commands */
++	nand_addr = ((u64)page << chip->page_shift);
++	bcmnand_reg_write(ctrl, NANDC_CMD_EXT_ADDR, nand_addr >> 32);
++
++	/* Must allow partial programming to change spare area only */
++	bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_PGM_PARTIAL(n), 1);
++
++	num_sec = 1 << ctrl->sec_per_page_shift;
++	/* Loop all sectors in page */
++	for (sector = 0; sector < num_sec; sector++) {
++		unsigned int col;
++
++		/* Spare area accessed by the data sector offset */
++		col = (sector << ctrl->sector_size_shift);
++
++		bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS, nand_addr + col);
++
++		/* Set controller to Little Endian mode for copying */
++		bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
++
++		memcpy(ctrl_spare,
++			chip->oob_poi + sector * spare_per_sec,
++			spare_per_sec);
++
++		/* Return to Big Endian mode for commands etc */
++		bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
++
++		/* Push spare bytes into internal buffer, last goes to flash */
++		bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++				  NANDC_CMD_OPCODE_SPARE_PROG);
++
++		if (sector == (num_sec - 1))
++			to = 1 << 16;
++		else
++			to = 1 << 10;
++
++		if (bcmnand_wait_cmd(ctrl, to))
++			return -EIO;
++	}
++
++	/* Restore partial programming inhibition */
++	bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_PGM_PARTIAL(n), 0);
++
++	status = bcmnand_waitfunc(mtd, chip);
++	return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/*
++ * verify that a buffer is all erased
++ */
++static bool bcmnand_buf_erased(const void *buf, unsigned int len)
++{
++	unsigned int i;
++	const u32 *p = buf;
++
++	for (i = 0; i < (len >> 2); i++) {
++		if (p[i] != 0xffffffff)
++			return false;
++	}
++	return true;
++}
++
++/*
++ * read a page, with or without ECC checking
++ */
++static int bcmnand_read_page_do(struct mtd_info *mtd, struct nand_chip *chip,
++				uint8_t *buf, int page, bool ecc)
++{
++	struct bcmnand_ctrl *ctrl = chip->priv;
++	unsigned int n = ctrl->chip_num;
++	void __iomem *ctrl_cache;
++	void __iomem *ctrl_spare;
++	unsigned int data_bytes;
++	unsigned int spare_per_sec;
++	unsigned int sector, to = 1 << 16;
++	u32 err_soft_reg, err_hard_reg;
++	unsigned int hard_err_count = 0;
++	int ret;
++	u64 nand_addr;
++
++	ctrl_cache = ctrl->core->io_addr + NANDC_CACHE_OFF;
++	ctrl_spare = ctrl->core->io_addr + NANDC_SPARE_AREA_READ_OFF;
++
++	/* Reset  ECC error stats */
++	err_hard_reg = bcmnand_reg_read(ctrl, NANDC_UNCORR_ERR_COUNT);
++	err_soft_reg = bcmnand_reg_read(ctrl, NANDC_READ_CORR_BIT_COUNT);
++
++	spare_per_sec = mtd->oobsize >> ctrl->sec_per_page_shift;
++
++	/* Set the page address for the following commands */
++	nand_addr = ((u64)page << chip->page_shift);
++	bcmnand_reg_write(ctrl, NANDC_CMD_EXT_ADDR, nand_addr >> 32);
++
++	/* Enable ECC validation for ecc page reads */
++	bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_RD_ECC(n), ecc);
++
++	/* Loop all sectors in page */
++	for (sector = 0; sector < (1 << ctrl->sec_per_page_shift); sector++) {
++		data_bytes  = 0;
++
++		/* Copy partial sectors sized by cache reg */
++		while (data_bytes < (1<<ctrl->sector_size_shift)) {
++			unsigned int col;
++
++			col = data_bytes + (sector << ctrl->sector_size_shift);
++
++			bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS,
++					  nand_addr + col);
++
++			/* Issue command to read partial page */
++			bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++					  NANDC_CMD_OPCODE_PAGE_READ);
++
++			/* Wait for the command to complete */
++			ret = bcmnand_wait_cmd(ctrl, to);
++			if (ret < 0)
++				return ret;
++
++			/* Set controller to Little Endian mode for copying */
++			bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
++
++			if (data_bytes == 0) {
++				memcpy(chip->oob_poi + sector * spare_per_sec,
++				       ctrl_spare, spare_per_sec);
++			}
++
++			memcpy(buf + col, ctrl_cache, NANDC_CACHE_SIZE);
++			data_bytes += NANDC_CACHE_SIZE;
++
++			/* Return to Big Endian mode for commands etc */
++			bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
++
++			/* Next iterations should go fast */
++			to = 1 << 10;
++
++			/* capture hard errors for each partial */
++			if (err_hard_reg != bcmnand_reg_read(ctrl, NANDC_UNCORR_ERR_COUNT)) {
++				int era = bcmnand_reg_read(ctrl, NANDC_INT_STAT_ERASED);
++
++				if (!era &&
++				    !bcmnand_buf_erased(buf + col, NANDC_CACHE_SIZE))
++					hard_err_count++;
++
++				err_hard_reg = bcmnand_reg_read(ctrl,
++							NANDC_UNCORR_ERR_COUNT);
++			}
++		}
++	}
++
++	if (!ecc)
++		return 0;
++
++	/* Report hard ECC errors */
++	if (hard_err_count)
++		mtd->ecc_stats.failed++;
++
++	/* Get ECC soft error stats */
++	mtd->ecc_stats.corrected += err_soft_reg -
++			bcmnand_reg_read(ctrl, NANDC_READ_CORR_BIT_COUNT);
++
++	return 0;
++}
++
++/*
++ * NAND Interface - read_page_ecc
++ */
++static int bcmnand_read_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
++				 uint8_t *buf, int oob_required, int page)
++{
++	return bcmnand_read_page_do(mtd, chip, buf, page, true);
++}
++
++/*
++ * NAND Interface - read_page_raw
++ */
++static int bcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				 uint8_t *buf, int oob_required, int page)
++{
++	return bcmnand_read_page_do(mtd, chip, buf, page, true);
++}
++
++/*
++ * do page write, with or without ECC generation enabled
++ */
++static int bcmnand_write_page_do(struct mtd_info *mtd, struct nand_chip *chip,
++				 const uint8_t *buf, bool ecc)
++{
++	struct bcmnand_ctrl *ctrl = chip->priv;
++	unsigned int n = ctrl->chip_num;
++	void __iomem *ctrl_cache;
++	void __iomem *ctrl_spare;
++	unsigned int spare_per_sec, sector, num_sec;
++	unsigned int data_bytes, spare_bytes;
++	int i, to;
++	uint8_t *tmp_poi;
++	u32 nand_addr;
++
++	ctrl_cache = ctrl->core->io_addr + NANDC_CACHE_OFF;
++	ctrl_spare = ctrl->core->io_addr + NANDC_SPARE_AREA_WRITE_OFF;
++
++	/* Get start-of-page address */
++	nand_addr = bcmnand_reg_read(ctrl, NANDC_CMD_ADDRESS);
++
++	tmp_poi = kmalloc(mtd->oobsize, GFP_KERNEL);
++	if (!tmp_poi)
++		return -ENOMEM;
++
++	/* Retreive pre-existing OOB values */
++	memcpy(tmp_poi, chip->oob_poi, mtd->oobsize);
++	ctrl->cmd_ret = bcmnand_read_oob(mtd, chip,
++					 nand_addr >> chip->page_shift);
++	if (ctrl->cmd_ret < 0) {
++		kfree(tmp_poi);
++		return ctrl->cmd_ret;
++	}
++
++	/* Apply new OOB data bytes just like they would end up on the chip */
++	for (i = 0; i < mtd->oobsize; i++)
++		chip->oob_poi[i] &= tmp_poi[i];
++	kfree(tmp_poi);
++
++	spare_per_sec = mtd->oobsize >> ctrl->sec_per_page_shift;
++
++	/* Enable ECC generation for ecc page write, if requested */
++	bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_WR_ECC(n), ecc);
++
++	spare_bytes = 0;
++	num_sec = 1 << ctrl->sec_per_page_shift;
++
++	/* Loop all sectors in page */
++	for (sector = 0; sector < num_sec; sector++) {
++		data_bytes  = 0;
++
++		/* Copy partial sectors sized by cache reg */
++		while (data_bytes < (1<<ctrl->sector_size_shift)) {
++			unsigned int col;
++
++			col = data_bytes +
++				(sector << ctrl->sector_size_shift);
++
++			/* Set address of 512-byte sub-page */
++			bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS,
++					  nand_addr + col);
++
++			/* Set controller to Little Endian mode for copying */
++			bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN,
++					   1);
++
++			/* Set spare area is written at each sector start */
++			if (data_bytes == 0) {
++				memcpy(ctrl_spare,
++					chip->oob_poi + spare_bytes,
++					spare_per_sec);
++				spare_bytes += spare_per_sec;
++			}
++
++			/* Copy sub-page data */
++			memcpy(ctrl_cache, buf + col, NANDC_CACHE_SIZE);
++			data_bytes += NANDC_CACHE_SIZE;
++
++			/* Return to Big Endian mode for commands etc */
++			bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
++
++			/* Push data into internal cache */
++			bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++					  NANDC_CMD_OPCODE_PAGE_PROG);
++
++			/* Wait for the command to complete */
++			if (sector == (num_sec - 1))
++				to = 1 << 16;
++			else
++				to = 1 << 10;
++			ctrl->cmd_ret = bcmnand_wait_cmd(ctrl, to);
++			if (ctrl->cmd_ret < 0)
++				return ctrl->cmd_ret;
++		}
++	}
++	return 0;
++}
++
++/*
++ * NAND Interface = write_page_ecc
++ */
++static int bcmnand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
++				  const uint8_t *buf, int oob_required)
++{
++	return bcmnand_write_page_do(mtd, chip, buf, true);
++}
++
++/*
++ * NAND Interface = write_page_raw
++ */
++static int bcmnand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				  const uint8_t *buf, int oob_required)
++{
++	return bcmnand_write_page_do(mtd, chip, buf, false);
++}
++
++/*
++ * MTD Interface - read_byte
++ *
++ * This function emulates simple controllers behavior
++ * for just a few relevant commands
++ */
++static uint8_t bcmnand_read_byte(struct mtd_info *mtd)
++{
++	struct nand_chip *nand = mtd->priv;
++	struct bcmnand_ctrl *ctrl = nand->priv;
++	uint8_t b = ~0;
++
++	switch (ctrl->last_cmd) {
++	case NAND_CMD_READID:
++		if (ctrl->id_byte_index < 8) {
++			b = bcmnand_reg_read(ctrl, NANDC_DEVID_BYTE(
++							ctrl->id_byte_index));
++			ctrl->id_byte_index++;
++		}
++		break;
++	case NAND_CMD_READOOB:
++		if (ctrl->oob_index < mtd->oobsize)
++			b = nand->oob_poi[ctrl->oob_index++];
++		break;
++	case NAND_CMD_STATUS:
++		b = bcmnand_reg_read(ctrl, NANDC_INT_STAT_FLASH_STATUS);
++		break;
++	default:
++		pr_err("%s: got unkown command: 0x%x in %s\n", DRV_NAME,
++		       ctrl->last_cmd, __func__);
++	}
++	return b;
++}
++
++/*
++ * MTD Interface - read_word
++ *
++ * Can not be tested without x16 chip, but the SoC does not support x16 i/f.
++ */
++static u16 bcmnand_read_word(struct mtd_info *mtd)
++{
++	u16 w = ~0;
++
++	w = bcmnand_read_byte(mtd);
++	barrier();
++	w |= bcmnand_read_byte(mtd) << 8;
++
++	return w;
++}
++
++/*
++ * MTD Interface - select a chip from an array
++ */
++static void bcmnand_select_chip(struct mtd_info *mtd, int chip)
++{
++	struct nand_chip *nand = mtd->priv;
++	struct bcmnand_ctrl *ctrl = nand->priv;
++
++	ctrl->chip_num = chip;
++	bcmnand_reg_write(ctrl, NANDC_CMD_CS_SEL, chip);
++}
++
++/*
++ * NAND Interface - emulate low-level NAND commands
++ *
++ * Only a few low-level commands are really needed by generic NAND,
++ * and they do not call for CMD_LL operations the controller can support.
++ */
++static void bcmnand_cmdfunc(struct mtd_info *mtd, unsigned int command,
++			    int column, int page_addr)
++{
++	struct nand_chip *nand = mtd->priv;
++	struct bcmnand_ctrl *ctrl = nand->priv;
++	u64 nand_addr;
++	unsigned int to = 1;
++
++	ctrl->last_cmd = command;
++
++	/* Set address for some commands */
++	switch (command) {
++	case NAND_CMD_ERASE1:
++		column = 0;
++		/*FALLTHROUGH*/
++	case NAND_CMD_SEQIN:
++	case NAND_CMD_READ0:
++	case NAND_CMD_READ1:
++		WARN_ON(column >= mtd->writesize);
++		nand_addr = (u64) column |
++			((u64)page_addr << nand->page_shift);
++		bcmnand_reg_write(ctrl, NANDC_CMD_EXT_ADDR, nand_addr >> 32);
++		bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS, nand_addr);
++		break;
++	case NAND_CMD_ERASE2:
++	case NAND_CMD_RESET:
++	case NAND_CMD_READID:
++	case NAND_CMD_READOOB:
++	case NAND_CMD_PAGEPROG:
++	default:
++		/* Do nothing, address not used */
++		break;
++	}
++
++	/* Issue appropriate command to controller */
++	switch (command) {
++	case NAND_CMD_SEQIN:
++		/* Only need to load command address, done */
++		return;
++
++	case NAND_CMD_RESET:
++		bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++				  NANDC_CMD_OPCODE_FLASH_RESET);
++		to = 1 << 8;
++		break;
++
++	case NAND_CMD_READID:
++		bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++				  NANDC_CMD_OPCODE_DEVID_READ);
++		ctrl->id_byte_index = 0;
++		to = 1 << 8;
++		break;
++
++	case NAND_CMD_READ0:
++	case NAND_CMD_READ1:
++		bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++				  NANDC_CMD_OPCODE_PAGE_READ);
++		to = 1 << 15;
++		break;
++	case NAND_CMD_STATUS:
++		bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++				  NANDC_CMD_OPCODE_STATUS_READ);
++		to = 1 << 8;
++		break;
++	case NAND_CMD_ERASE1:
++		return;
++
++	case NAND_CMD_ERASE2:
++		bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
++				  NANDC_CMD_OPCODE_BLOCK_ERASE);
++		to = 1 << 18;
++		break;
++
++	case NAND_CMD_PAGEPROG:
++		/* Cmd already set from write_page */
++		return;
++
++	case NAND_CMD_READOOB:
++		/* Emulate simple interface */
++		bcmnand_read_oob(mtd, nand, page_addr);
++		ctrl->oob_index = 0;
++		return;
++
++	default:
++		pr_err("%s: got unkown command: 0x%x in %s\n", DRV_NAME,
++		       ctrl->last_cmd, __func__);
++	}
++
++	/* Wait for command to complete */
++	ctrl->cmd_ret = bcmnand_wait_cmd(ctrl, to);
++
++}
++
++static int bcmnand_scan(struct mtd_info *mtd)
++{
++	struct nand_chip *nand = mtd->priv;
++	struct bcmnand_ctrl *ctrl = nand->priv;
++	bool sector_1k = false;
++	unsigned int chip_num = 0;
++	int ecc_level = 0;
++	int ret;
++
++	ret = nand_scan_ident(mtd, NANDC_MAX_CHIPS, NULL);
++	if (ret)
++		return ret;
++
++	/* Get configuration from first chip */
++	sector_1k = bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_SECTOR_1K(0));
++	ecc_level = bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(0));
++	mtd->writesize_shift = nand->page_shift;
++
++	ctrl->ecc_level = ecc_level;
++	ctrl->sector_size_shift = sector_1k ? 10 : 9;
++
++	/* Configure spare area, tweak as needed */
++	do {
++		ctrl->sec_per_page_shift =
++			mtd->writesize_shift - ctrl->sector_size_shift;
++
++		/* will return -EINVAL if OOB space exhausted */
++		ret = bcmnand_hw_ecc_layout(ctrl);
++
++		/* First try to bump sector size to 1k, then decrease level */
++		if (ret && nand->page_shift > 9 && ctrl->sector_size_shift < 10)
++			ctrl->sector_size_shift = 10;
++		else if (ret)
++			ctrl->ecc_level--;
++
++	} while (ret && ctrl->ecc_level > 0);
++
++	if (WARN_ON(ctrl->ecc_level == 0))
++		return -ENOENT;
++
++	if ((ctrl->sector_size_shift  > 9) != (sector_1k == 1)) {
++		pr_info("%s: sector size adjusted to 1k\n", DRV_NAME);
++		sector_1k = 1;
++	}
++
++	if (ecc_level != ctrl->ecc_level) {
++		pr_info("%s: ECC level adjusted from %u to %u\n",
++			DRV_NAME, ecc_level, ctrl->ecc_level);
++		ecc_level = ctrl->ecc_level;
++	}
++
++	/* handle the hardware chip config registers */
++	for (chip_num = 0; chip_num < nand->numchips; chip_num++) {
++		bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_SECTOR_1K(chip_num),
++				  sector_1k);
++		bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(chip_num),
++				  ecc_level);
++
++		/* Large pages: no partial page programming */
++		if (mtd->writesize > 512) {
++			bcmnand_reg_write(ctrl,
++				NANDC_ACC_CTRL_PGM_RDIN(chip_num), 0);
++			bcmnand_reg_write(ctrl,
++				NANDC_ACC_CTRL_PGM_PARTIAL(chip_num), 0);
++		}
++
++		/* Do not raise ECC error when reading erased pages */
++		/* This bit has only partial effect, driver needs to help */
++		bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_ERA_ECC_ERR(chip_num),
++				  0);
++
++		bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_PG_HIT(chip_num), 0);
++		bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_PREFETCH(chip_num), 0);
++		bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_CACHE_MODE(chip_num), 0);
++		bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_CACHE_LASTPG(chip_num),
++				  0);
++
++		/* TBD: consolidate or at least verify the s/w and h/w geometries agree */
++	}
++
++	/* Allow writing on device */
++	if (!(nand->options & NAND_ROM))
++		bcmnand_reg_write(ctrl, NANDC_CS_NAND_WP, 0);
++
++	pr_debug("%s: layout.oobavail=%d\n", DRV_NAME,
++		 nand->ecc.layout->oobavail);
++
++	ret = nand_scan_tail(mtd);
++
++	if (nand->badblockbits == 0)
++		nand->badblockbits = 8;
++	if (WARN_ON((1 << nand->page_shift) != mtd->writesize))
++		return -EIO;
++
++	/* Spit out some key chip parameters as detected by nand_base */
++	pr_debug("%s: erasesize=%d writesize=%d oobsize=%d page_shift=%d badblockpos=%d badblockbits=%d\n",
++		 DRV_NAME, mtd->erasesize, mtd->writesize, mtd->oobsize,
++		 nand->page_shift, nand->badblockpos, nand->badblockbits);
++
++	return ret;
++}
++
++/*
++ * main intiailization function
++ */
++static int bcmnand_ctrl_init(struct bcmnand_ctrl *ctrl)
++{
++	unsigned int chip;
++	struct nand_chip *nand;
++	struct mtd_info *mtd;
++	unsigned int n = 0;
++	int ret;
++
++	/* Software variables init */
++	nand = &ctrl->nand;
++	mtd = &ctrl->mtd;
++
++	init_completion(&ctrl->op_completion);
++
++	mtd->priv = nand;
++	mtd->owner = THIS_MODULE;
++	mtd->name = DRV_NAME;
++
++	nand->priv = ctrl;
++
++	nand->chip_delay = 5;	/* not used */
++	nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)~0L;
++
++	if (bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_WIDTH(n)))
++		nand->options |= NAND_BUSWIDTH_16;
++	nand->options |= NAND_SKIP_BBTSCAN;	/* Dont need BBTs */
++
++	nand->options |= NAND_NO_SUBPAGE_WRITE; /* Subpages unsupported */
++
++	nand->dev_ready			= bcmnand_dev_ready;
++	nand->read_byte			= bcmnand_read_byte;
++	nand->read_word			= bcmnand_read_word;
++	nand->select_chip		= bcmnand_select_chip;
++	nand->cmdfunc			= bcmnand_cmdfunc;
++	nand->waitfunc			= bcmnand_waitfunc;
++
++	nand->ecc.mode			= NAND_ECC_HW;
++	nand->ecc.read_page_raw		= bcmnand_read_page_raw;
++	nand->ecc.write_page_raw	= bcmnand_write_page_raw;
++	nand->ecc.read_page		= bcmnand_read_page_ecc;
++	nand->ecc.write_page		= bcmnand_write_page_ecc;
++	nand->ecc.read_oob		= bcmnand_read_oob;
++	nand->ecc.write_oob		= bcmnand_write_oob;
++
++	/* Set AUTO_CNFIG bit - try to auto-detect chips */
++	bcmnand_reg_write(ctrl, NANDC_CS_AUTO_CONFIG, 1);
++
++	usleep_range(1000, 1500);
++
++	/* Print out current chip config */
++	for (chip = 0; chip < NANDC_MAX_CHIPS; chip++) {
++		pr_debug("%s: chip[%d]: size=%#x block=%#x page=%#x ecc_level=%#x\n",
++			 DRV_NAME, chip,
++			 bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_SIZE(chip)),
++			 bcmnand_reg_read(ctrl, NANDC_CONFIG_BLK_SIZE(chip)),
++			 bcmnand_reg_read(ctrl, NANDC_CONFIG_PAGE_SIZE(chip)),
++			 bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(chip)));
++	}
++
++	pr_debug("%s: Nand controller is reads=%d\n", DRV_NAME,
++		bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY));
++
++	ret = bcmnand_scan(mtd);
++	if (ret) {
++		pr_err("%s: scanning the nand flash chip failed with %i\n",
++		       DRV_NAME, ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static int __init bcmnand_idm_init(struct bcmnand_ctrl *ctrl)
++{
++	int irq_off;
++	unsigned int retries = 0x1000;
++
++	if (bcmnand_reg_aread(ctrl, NANDC_IDM_RESET))
++		pr_err("%s: stuck in reset\n", DRV_NAME);
++
++	bcmnand_reg_awrite(ctrl, NANDC_IDM_RESET, 1);
++	if (!bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) {
++		pr_err("%s: reset of failed\n", DRV_NAME);
++		return -EIO;
++	}
++
++	while (bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) {
++		bcmnand_reg_awrite(ctrl, NANDC_IDM_RESET, 0);
++		cpu_relax();
++		usleep_range(100, 150);
++		if (!(retries--)) {
++			pr_err("%s: did not came back from reset\n",
++			       DRV_NAME);
++			return -ETIMEDOUT;
++		}
++	}
++
++	bcmnand_reg_awrite(ctrl, NANDC_IDM_CLOCK_EN, 1);
++	bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
++	udelay(10);
++
++	pr_info("%s: NAND Controller rev %d.%d\n", DRV_NAME,
++		bcmnand_reg_read(ctrl, NANDC_REV_MAJOR),
++		bcmnand_reg_read(ctrl, NANDC_REV_MINOR));
++
++	usleep_range(250, 350);
++
++	/* Disable all IRQs */
++	for (irq_off = 0; irq_off < NANDC_IRQ_NUM; irq_off++)
++		bcmnand_reg_awrite(ctrl, NANDC_IDM_IRQ_N_EN(irq_off), 0);
++
++	return 0;
++}
++
++static const char * const part_probes[] = { "bcm47xxpart", "cmdlinepart", NULL };
++
++/*
++ * Top-level init function
++ */
++static int bcmnand_probe(struct bcma_device *core)
++{
++	struct device *dev = &core->dev;
++	struct device_node *np = dev->of_node;
++	struct bcmnand_ctrl *ctrl;
++	int res, i, irq;
++
++	if (!np) {
++		pr_err("%s: no device tree node found\n", DRV_NAME);
++		return -ENOENT;
++	}
++
++	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
++	if (!ctrl)
++		return -ENOMEM;
++
++	bcma_set_drvdata(core, ctrl);
++
++	ctrl->mtd.dev.parent = &core->dev;
++	ctrl->core = core;
++
++	/* Acquire all interrupt lines */
++	for (i = 0; i < of_irq_count(np); i++) {
++		irq = irq_of_parse_and_map(np, i);
++		res = devm_request_irq(dev, irq, bcmnand_isr, 0, DRV_NAME, ctrl);
++		if (res < 0) {
++			pr_err("%s: problem requesting irq: %i (idx: %i)\n",
++			       DRV_NAME, irq, i);
++			return res;
++		}
++	}
++
++	res = bcmnand_idm_init(ctrl);
++	if (res)
++		return res;
++
++	res = bcmnand_ctrl_init(ctrl);
++	if (res)
++		return res;
++
++	res = mtd_device_parse_register(&ctrl->mtd, part_probes, NULL, NULL, 0);
++	if (res) {
++		pr_err("%s: Failed to register MTD device: %d\n", DRV_NAME, res);
++		return res;
++	}
++	return 0;
++}
++
++static void bcmnand_remove(struct bcma_device *core)
++{
++	struct bcmnand_ctrl *ctrl = bcma_get_drvdata(core);
++
++	mtd_device_unregister(&ctrl->mtd);
++}
++
++static const struct bcma_device_id bcmnand_bcma_tbl[] = {
++	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_NAND, BCMA_ANY_REV, BCMA_ANY_CLASS),
++	BCMA_CORETABLE_END
++};
++MODULE_DEVICE_TABLE(bcma, bgmac_bcma_tbl);
++
++static struct bcma_driver bcmnand_bcma_driver = {
++	.name		= KBUILD_MODNAME,
++	.id_table	= bcmnand_bcma_tbl,
++	.probe		= bcmnand_probe,
++	.remove		= bcmnand_remove,
++};
++
++static int __init bcmnand_init(void)
++{
++	int err;
++
++	err = bcma_driver_register(&bcmnand_bcma_driver);
++	if (err)
++		return err;
++	pr_info("%s: Broadcom NAND Controller driver loaded\n", DRV_NAME);
++
++	return 0;
++}
++
++static void __exit bcmnand_exit(void)
++{
++	bcma_driver_unregister(&bcmnand_bcma_driver);
++}
++
++module_init(bcmnand_init)
++module_exit(bcmnand_exit)
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION(DRV_DESC);

+ 34 - 0
target/linux/bcm53xx/patches-3.18/500-UBI-Detect-EOF-mark-and-erase-all-remaining-blocks.patch

@@ -0,0 +1,34 @@
+From f41f8b42db092e505382f7120994de21590dff48 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Thu, 16 Oct 2014 20:52:16 +0200
+Subject: [PATCH] UBI: Detect EOF mark and erase all remaining blocks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ drivers/mtd/ubi/io.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/mtd/ubi/io.c
++++ b/drivers/mtd/ubi/io.c
+@@ -727,6 +727,7 @@ bad:
+  * o %UBI_IO_FF if only 0xFF bytes were read (the PEB is supposedly empty)
+  * o a negative error code in case of failure.
+  */
++static bool erase_all_next = false;
+ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
+ 		       struct ubi_ec_hdr *ec_hdr, int verbose)
+ {
+@@ -753,6 +754,10 @@ int ubi_io_read_ec_hdr(struct ubi_device
+ 	}
+ 
+ 	magic = be32_to_cpu(ec_hdr->magic);
++	if (magic == 0xdeadc0de)
++		erase_all_next = true;
++	if (erase_all_next)
++		return read_err ? UBI_IO_FF_BITFLIPS : UBI_IO_FF;
+ 	if (magic != UBI_EC_HDR_MAGIC) {
+ 		if (mtd_is_eccerr(read_err))
+ 			return UBI_IO_BAD_HDR_EBADMSG;

+ 33 - 0
target/linux/bcm53xx/patches-3.18/900-bgmac-some-fixes-to-get-bgmac-work.patch

@@ -0,0 +1,33 @@
+From 0bd576e93a188fd3aab769b622fb3d35fa9bc7a7 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <[email protected]>
+Date: Sat, 3 May 2014 19:55:38 +0200
+Subject: [PATCH 15/15] bgmac: some fixes to get bgmac work
+
+Signed-off-by: Hauke Mehrtens <[email protected]>
+---
+ drivers/net/ethernet/broadcom/Kconfig | 2 +-
+ drivers/net/phy/phy_device.c          | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/broadcom/Kconfig
++++ b/drivers/net/ethernet/broadcom/Kconfig
+@@ -143,7 +143,7 @@ config BNX2X_SRIOV
+ 
+ config BGMAC
+ 	tristate "BCMA bus GBit core support"
+-	depends on BCMA_HOST_SOC && HAS_DMA && BCM47XX
++	depends on BCMA_HOST_SOC && HAS_DMA
+ 	select PHYLIB
+ 	---help---
+ 	  This driver supports GBit MAC and BCM4706 GBit MAC cores on BCMA bus.
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -931,7 +931,7 @@ int genphy_update_link(struct phy_device
+ 		return status;
+ 
+ 	if ((status & BMSR_LSTATUS) == 0)
+-		phydev->link = 0;
++		phydev->link = 1;
+ 	else
+ 		phydev->link = 1;
+ 

+ 42 - 0
target/linux/bcm53xx/patches-3.18/901-bcma-register-SoC-later-as-a-module.patch

@@ -0,0 +1,42 @@
+From fee1501c494954f6e889563ca44aadfe4a83a643 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
+Date: Tue, 14 Oct 2014 00:05:42 +0200
+Subject: [PATCH] bcma: register SoC later (as a module)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is temporary workaround required for easier debugging.
+
+Signed-off-by: Rafał Miłecki <[email protected]>
+---
+ drivers/bcma/host_soc.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+--- a/drivers/bcma/host_soc.c
++++ b/drivers/bcma/host_soc.c
+@@ -265,14 +265,22 @@ static struct platform_driver bcma_host_
+ 	.probe		= bcma_host_soc_probe,
+ 	.remove		= bcma_host_soc_remove,
+ };
++/* FIXME: Using module_platform_driver is a temp hack to get bcma SoC
++ * initialzed *after* serial console. This way we get some logs in case of hang
++ * inside bcma or related driver. We need that for debugging problems and it's
++ * also useful for development. Otherwise any hang (in flash driver, PCIe
++ * driver, USB driver, etc.) would result in not getting logs at all.
++ */
++module_platform_driver(bcma_host_soc_driver);
+ 
+ int __init bcma_host_soc_register_driver(void)
+ {
+-	return platform_driver_register(&bcma_host_soc_driver);
++	/* return platform_driver_register(&bcma_host_soc_driver); */
++	return 0;
+ }
+ 
+ void __exit bcma_host_soc_unregister_driver(void)
+ {
+-	platform_driver_unregister(&bcma_host_soc_driver);
++	/* platform_driver_unregister(&bcma_host_soc_driver); */
+ }
+ #endif /* CONFIG_OF */