bcm53xx: initial support for kernel 3.18
authorHauke Mehrtens <hauke@hauke-m.de>
Mon, 27 Oct 2014 23:02:08 +0000 (23:02 +0000)
committerHauke Mehrtens <hauke@hauke-m.de>
Mon, 27 Oct 2014 23:02:08 +0000 (23:02 +0000)
This adds initial support for kernel 3.18.

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
SVN-Revision: 43097

30 files changed:
target/linux/bcm53xx/config-3.18 [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/044-ARM-BCM5301X-fix-early-serial-console.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/045-ARM-BCM5301X-select-GPIOLIB-as-optional.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/046-ARM-BCM5301X-Add-Broadcom-s-bus-axi-to-the-DTS-file.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/047-ARM-BCM5301X-Add-LEDs-for-Netgear-R6250-V1.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/110-bcm47xx-move-the-nvram-header-file-into-common-space.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/111-bcm47xx-nvram-add-new-nvram-driver-with-dt-support.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/112-bcm53xx-sprom-add-sprom-driver.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/121-bcma-get-irqs-from-dt.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/122-bcma-fill-core-details-for-every-device.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/123-bcma-get-sprom-from-devicetree.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/130-ARM-BCM5301X-register-bcma-bus.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/131-ARM-BCM5301X-add-restart-support.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/150-pci-do-not-probe-too-early.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/300-ARM-BCM5301X-Disable-MMU-and-Dcache-for-decompression.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/301-ARM-BCM5301X-Add-buttons-support-for-Netgear-R6250.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/302-ARM-BCM5301X-Add-DT-for-Netgear-R6300-V2.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/303-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-1750DHP.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/304-ARM-BCM5301X-Add-DT-for-Asus-RT-N18U.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/305-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-600DHP2.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/410-mtd-bcm47xxpart-alloc-memory-for-more-partitions.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/420-mtd-bcm5301x_nand.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/500-UBI-Detect-EOF-mark-and-erase-all-remaining-blocks.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/900-bgmac-some-fixes-to-get-bgmac-work.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.18/901-bcma-register-SoC-later-as-a-module.patch [new file with mode: 0644]

diff --git a/target/linux/bcm53xx/config-3.18 b/target/linux/bcm53xx/config-3.18
new file mode 100644 (file)
index 0000000..e31a24f
--- /dev/null
@@ -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
diff --git a/target/linux/bcm53xx/patches-3.18/044-ARM-BCM5301X-fix-early-serial-console.patch b/target/linux/bcm53xx/patches-3.18/044-ARM-BCM5301X-fix-early-serial-console.patch
new file mode 100644 (file)
index 0000000..4d100da
--- /dev/null
@@ -0,0 +1,33 @@
+From 310a267714f7565dba8934dd51cdead6adc3b630 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ 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
diff --git a/target/linux/bcm53xx/patches-3.18/045-ARM-BCM5301X-select-GPIOLIB-as-optional.patch b/target/linux/bcm53xx/patches-3.18/045-ARM-BCM5301X-select-GPIOLIB-as-optional.patch
new file mode 100644 (file)
index 0000000..da17347
--- /dev/null
@@ -0,0 +1,29 @@
+From e7b1065712e769eb4de7b9d4aa222a4531c2b8fd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+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
diff --git a/target/linux/bcm53xx/patches-3.18/046-ARM-BCM5301X-Add-Broadcom-s-bus-axi-to-the-DTS-file.patch b/target/linux/bcm53xx/patches-3.18/046-ARM-BCM5301X-Add-Broadcom-s-bus-axi-to-the-DTS-file.patch
new file mode 100644 (file)
index 0000000..ac52aa0
--- /dev/null
@@ -0,0 +1,43 @@
+From a2533caee935fff97e3e8dbfad5cc159e6bf6034 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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>;
++              };
++      };
+ };
diff --git a/target/linux/bcm53xx/patches-3.18/047-ARM-BCM5301X-Add-LEDs-for-Netgear-R6250-V1.patch b/target/linux/bcm53xx/patches-3.18/047-ARM-BCM5301X-Add-LEDs-for-Netgear-R6250-V1.patch
new file mode 100644 (file)
index 0000000..c394f3c
--- /dev/null
@@ -0,0 +1,54 @@
+From b7e4d148906685882a081e7e50692313c5a8724e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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";
++              };
++      };
+ };
diff --git a/target/linux/bcm53xx/patches-3.18/110-bcm47xx-move-the-nvram-header-file-into-common-space.patch b/target/linux/bcm53xx/patches-3.18/110-bcm47xx-move-the-nvram-header-file-into-common-space.patch
new file mode 100644 (file)
index 0000000..de0f381
--- /dev/null
@@ -0,0 +1,279 @@
+From 7063a1583166abe1a9cefed38c2f53a0e14a0005 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ 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 <nbd@openwrt.org>
+- *
+- *  This program is free software; you can redistribute  it and/or modify it
+- *  under  the terms of  the GNU General  Public License as published by the
+- *  Free Software Foundation;  either version 2 of the  License, or (at your
+- *  option) any later version.
+- */
+-
+-#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 <nbd@openwrt.org>
++ *  Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de>
++ *
++ *  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 */
diff --git a/target/linux/bcm53xx/patches-3.18/111-bcm47xx-nvram-add-new-nvram-driver-with-dt-support.patch b/target/linux/bcm53xx/patches-3.18/111-bcm47xx-nvram-add-new-nvram-driver-with-dt-support.patch
new file mode 100644 (file)
index 0000000..f22a864
--- /dev/null
@@ -0,0 +1,588 @@
+From 71a6bff8656a1713615ffdd9139a83d65ba46c6d Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ .../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 <nbd@openwrt.org>
++ * Copyright (C) 2010-2014 Hauke Mehrtens <hauke@hauke-m.de>
++ *
++ * 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 <hauke@hauke-m.de>");
++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;
+ }
diff --git a/target/linux/bcm53xx/patches-3.18/112-bcm53xx-sprom-add-sprom-driver.patch b/target/linux/bcm53xx/patches-3.18/112-bcm53xx-sprom-add-sprom-driver.patch
new file mode 100644 (file)
index 0000000..c3d5b4d
--- /dev/null
@@ -0,0 +1,762 @@
+From 4e0ab3269a6d260a41a3673157753147f5f71341 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ .../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 <jolt@tuxbox.org>
++ * Copyright (C) 2006 Michael Buesch <m@bues.ch>
++ * Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org>
++ * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
++ * Copyright (C) 2010-2014 Hauke Mehrtens <hauke@hauke-m.de>
++ *
++ * 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 <hauke@hauke-m.de>");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm53xx/patches-3.18/121-bcma-get-irqs-from-dt.patch b/target/linux/bcm53xx/patches-3.18/121-bcma-get-irqs-from-dt.patch
new file mode 100644 (file)
index 0000000..e38b5bc
--- /dev/null
@@ -0,0 +1,38 @@
+From 6611afa6c49434780096cdf2c1028f0ac277f9bc Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ 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,
diff --git a/target/linux/bcm53xx/patches-3.18/122-bcma-fill-core-details-for-every-device.patch b/target/linux/bcm53xx/patches-3.18/122-bcma-fill-core-details-for-every-device.patch
new file mode 100644 (file)
index 0000000..fadf03c
--- /dev/null
@@ -0,0 +1,65 @@
+From 487b997353e2e3afe9c452b20ff5e4320d76e9c3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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),
diff --git a/target/linux/bcm53xx/patches-3.18/123-bcma-get-sprom-from-devicetree.patch b/target/linux/bcm53xx/patches-3.18/123-bcma-get-sprom-from-devicetree.patch
new file mode 100644 (file)
index 0000000..fbc75ce
--- /dev/null
@@ -0,0 +1,88 @@
+From bd9106f5907080b467026bdaaea979fac8c7badb Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ 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;
diff --git a/target/linux/bcm53xx/patches-3.18/130-ARM-BCM5301X-register-bcma-bus.patch b/target/linux/bcm53xx/patches-3.18/130-ARM-BCM5301X-register-bcma-bus.patch
new file mode 100644 (file)
index 0000000..024123f
--- /dev/null
@@ -0,0 +1,114 @@
+From 414f0ad9b3a8e8ee6eaf09c6d79d5f448ac28630 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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>;
++              };
+       };
+ };
diff --git a/target/linux/bcm53xx/patches-3.18/131-ARM-BCM5301X-add-restart-support.patch b/target/linux/bcm53xx/patches-3.18/131-ARM-BCM5301X-add-restart-support.patch
new file mode 100644 (file)
index 0000000..a977116
--- /dev/null
@@ -0,0 +1,69 @@
+From 28b11a8b1258214b3b5d58bb6e3bbcb0fc9fd4fe Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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
diff --git a/target/linux/bcm53xx/patches-3.18/150-pci-do-not-probe-too-early.patch b/target/linux/bcm53xx/patches-3.18/150-pci-do-not-probe-too-early.patch
new file mode 100644 (file)
index 0000000..b2e56f8
--- /dev/null
@@ -0,0 +1,29 @@
+From cf72936c001056de1cfcb27dd9a232f5484ec59c Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ 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);
diff --git a/target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch b/target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch
new file mode 100644 (file)
index 0000000..9254089
--- /dev/null
@@ -0,0 +1,670 @@
+From cc2cda651fcbc498bf513a6b802dca19944bcb37 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ 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");
diff --git a/target/linux/bcm53xx/patches-3.18/300-ARM-BCM5301X-Disable-MMU-and-Dcache-for-decompression.patch b/target/linux/bcm53xx/patches-3.18/300-ARM-BCM5301X-Disable-MMU-and-Dcache-for-decompression.patch
new file mode 100644 (file)
index 0000000..56db9c1
--- /dev/null
@@ -0,0 +1,195 @@
+From 26023cdfacaf116545b1087b9d1fe50dc6fbda10 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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)
diff --git a/target/linux/bcm53xx/patches-3.18/301-ARM-BCM5301X-Add-buttons-support-for-Netgear-R6250.patch b/target/linux/bcm53xx/patches-3.18/301-ARM-BCM5301X-Add-buttons-support-for-Netgear-R6250.patch
new file mode 100644 (file)
index 0000000..9595129
--- /dev/null
@@ -0,0 +1,59 @@
+From e1b44fc2e3cf76be1213bde07fc37c47eff39158 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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"
diff --git a/target/linux/bcm53xx/patches-3.18/302-ARM-BCM5301X-Add-DT-for-Netgear-R6300-V2.patch b/target/linux/bcm53xx/patches-3.18/302-ARM-BCM5301X-Add-DT-for-Netgear-R6300-V2.patch
new file mode 100644 (file)
index 0000000..ca4c956
--- /dev/null
@@ -0,0 +1,125 @@
+From 788069f86c7fc1ce54661651e695943fb47a5188 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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 <zajec5@gmail.com>
++ *
++ * 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>;
++              };
++      };
++};
diff --git a/target/linux/bcm53xx/patches-3.18/303-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-1750DHP.patch b/target/linux/bcm53xx/patches-3.18/303-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-1750DHP.patch
new file mode 100644 (file)
index 0000000..b8b0a5f
--- /dev/null
@@ -0,0 +1,102 @@
+From b7620da56595c5505e4a10b8779cec0362b59db2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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 <zajec5@gmail.com>
++ *
++ * 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>;
++              };
++      };
++};
diff --git a/target/linux/bcm53xx/patches-3.18/304-ARM-BCM5301X-Add-DT-for-Asus-RT-N18U.patch b/target/linux/bcm53xx/patches-3.18/304-ARM-BCM5301X-Add-DT-for-Asus-RT-N18U.patch
new file mode 100644 (file)
index 0000000..d0f5a86
--- /dev/null
@@ -0,0 +1,160 @@
+From 89fe6f9b7875f74e7d63a90ae3a51d84d3cf9369 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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 <zajec5@gmail.com>
++ *
++ * 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 <zajec5@gmail.com>
++ *
++ * 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,
+ };
diff --git a/target/linux/bcm53xx/patches-3.18/305-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-600DHP2.patch b/target/linux/bcm53xx/patches-3.18/305-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-600DHP2.patch
new file mode 100644 (file)
index 0000000..6d6baa3
--- /dev/null
@@ -0,0 +1,97 @@
+From 4812cd75bc85a9f7050e2b58c1cf17e3bd4dc7f8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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 <zajec5@gmail.com>
++ *
++ * 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>;
++              };
++      };
++};
diff --git a/target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch b/target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch
new file mode 100644 (file)
index 0000000..de1e506
--- /dev/null
@@ -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);
+       /*
diff --git a/target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch b/target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch
new file mode 100644 (file)
index 0000000..c274a7e
--- /dev/null
@@ -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);
diff --git a/target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch b/target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch
new file mode 100644 (file)
index 0000000..9a9d903
--- /dev/null
@@ -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);
diff --git a/target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch b/target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch
new file mode 100644 (file)
index 0000000..04d0058
--- /dev/null
@@ -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
diff --git a/target/linux/bcm53xx/patches-3.18/410-mtd-bcm47xxpart-alloc-memory-for-more-partitions.patch b/target/linux/bcm53xx/patches-3.18/410-mtd-bcm47xxpart-alloc-memory-for-more-partitions.patch
new file mode 100644 (file)
index 0000000..3372bf5
--- /dev/null
@@ -0,0 +1,32 @@
+From 6b833541d73894b5afd40d69949f8f6099db2abf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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.
diff --git a/target/linux/bcm53xx/patches-3.18/420-mtd-bcm5301x_nand.patch b/target/linux/bcm53xx/patches-3.18/420-mtd-bcm5301x_nand.patch
new file mode 100644 (file)
index 0000000..0b549f8
--- /dev/null
@@ -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 <hauke@hauke-m.de>
++ *
++ * 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);
diff --git a/target/linux/bcm53xx/patches-3.18/500-UBI-Detect-EOF-mark-and-erase-all-remaining-blocks.patch b/target/linux/bcm53xx/patches-3.18/500-UBI-Detect-EOF-mark-and-erase-all-remaining-blocks.patch
new file mode 100644 (file)
index 0000000..b8229cd
--- /dev/null
@@ -0,0 +1,34 @@
+From f41f8b42db092e505382f7120994de21590dff48 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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;
diff --git a/target/linux/bcm53xx/patches-3.18/900-bgmac-some-fixes-to-get-bgmac-work.patch b/target/linux/bcm53xx/patches-3.18/900-bgmac-some-fixes-to-get-bgmac-work.patch
new file mode 100644 (file)
index 0000000..c3ae68d
--- /dev/null
@@ -0,0 +1,33 @@
+From 0bd576e93a188fd3aab769b622fb3d35fa9bc7a7 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ 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;
diff --git a/target/linux/bcm53xx/patches-3.18/901-bcma-register-SoC-later-as-a-module.patch b/target/linux/bcm53xx/patches-3.18/901-bcma-register-SoC-later-as-a-module.patch
new file mode 100644 (file)
index 0000000..f20b1cc
--- /dev/null
@@ -0,0 +1,42 @@
+From fee1501c494954f6e889563ca44aadfe4a83a643 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+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 <zajec5@gmail.com>
+---
+ 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 */