aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm53xx/patches-3.18
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm53xx/patches-3.18')
-rw-r--r--target/linux/bcm53xx/patches-3.18/044-ARM-BCM5301X-fix-early-serial-console.patch33
-rw-r--r--target/linux/bcm53xx/patches-3.18/045-ARM-BCM5301X-select-GPIOLIB-as-optional.patch29
-rw-r--r--target/linux/bcm53xx/patches-3.18/046-ARM-BCM5301X-Add-Broadcom-s-bus-axi-to-the-DTS-file.patch43
-rw-r--r--target/linux/bcm53xx/patches-3.18/047-ARM-BCM5301X-Add-LEDs-for-Netgear-R6250-V1.patch54
-rw-r--r--target/linux/bcm53xx/patches-3.18/110-bcm47xx-move-the-nvram-header-file-into-common-space.patch279
-rw-r--r--target/linux/bcm53xx/patches-3.18/111-bcm47xx-nvram-add-new-nvram-driver-with-dt-support.patch588
-rw-r--r--target/linux/bcm53xx/patches-3.18/112-bcm53xx-sprom-add-sprom-driver.patch762
-rw-r--r--target/linux/bcm53xx/patches-3.18/121-bcma-get-irqs-from-dt.patch38
-rw-r--r--target/linux/bcm53xx/patches-3.18/122-bcma-fill-core-details-for-every-device.patch65
-rw-r--r--target/linux/bcm53xx/patches-3.18/123-bcma-get-sprom-from-devicetree.patch88
-rw-r--r--target/linux/bcm53xx/patches-3.18/130-ARM-BCM5301X-register-bcma-bus.patch114
-rw-r--r--target/linux/bcm53xx/patches-3.18/131-ARM-BCM5301X-add-restart-support.patch69
-rw-r--r--target/linux/bcm53xx/patches-3.18/150-pci-do-not-probe-too-early.patch29
-rw-r--r--target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch670
-rw-r--r--target/linux/bcm53xx/patches-3.18/300-ARM-BCM5301X-Disable-MMU-and-Dcache-for-decompression.patch195
-rw-r--r--target/linux/bcm53xx/patches-3.18/301-ARM-BCM5301X-Add-buttons-support-for-Netgear-R6250.patch59
-rw-r--r--target/linux/bcm53xx/patches-3.18/302-ARM-BCM5301X-Add-DT-for-Netgear-R6300-V2.patch125
-rw-r--r--target/linux/bcm53xx/patches-3.18/303-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-1750DHP.patch102
-rw-r--r--target/linux/bcm53xx/patches-3.18/304-ARM-BCM5301X-Add-DT-for-Asus-RT-N18U.patch160
-rw-r--r--target/linux/bcm53xx/patches-3.18/305-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-600DHP2.patch97
-rw-r--r--target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch46
-rw-r--r--target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch374
-rw-r--r--target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch263
-rw-r--r--target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch42
-rw-r--r--target/linux/bcm53xx/patches-3.18/410-mtd-bcm47xxpart-alloc-memory-for-more-partitions.patch32
-rw-r--r--target/linux/bcm53xx/patches-3.18/420-mtd-bcm5301x_nand.patch1616
-rw-r--r--target/linux/bcm53xx/patches-3.18/500-UBI-Detect-EOF-mark-and-erase-all-remaining-blocks.patch34
-rw-r--r--target/linux/bcm53xx/patches-3.18/900-bgmac-some-fixes-to-get-bgmac-work.patch33
-rw-r--r--target/linux/bcm53xx/patches-3.18/901-bcma-register-SoC-later-as-a-module.patch42
29 files changed, 6081 insertions, 0 deletions
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
index 0000000000..4d100dae33
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/044-ARM-BCM5301X-fix-early-serial-console.patch
@@ -0,0 +1,33 @@
+From 310a267714f7565dba8934dd51cdead6adc3b630 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..da17347d3e
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/045-ARM-BCM5301X-select-GPIOLIB-as-optional.patch
@@ -0,0 +1,29 @@
+From e7b1065712e769eb4de7b9d4aa222a4531c2b8fd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..ac52aa0162
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/046-ARM-BCM5301X-Add-Broadcom-s-bus-axi-to-the-DTS-file.patch
@@ -0,0 +1,43 @@
+From a2533caee935fff97e3e8dbfad5cc159e6bf6034 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..c394f3c481
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/047-ARM-BCM5301X-Add-LEDs-for-Netgear-R6250-V1.patch
@@ -0,0 +1,54 @@
+From b7e4d148906685882a081e7e50692313c5a8724e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..de0f38135e
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/110-bcm47xx-move-the-nvram-header-file-into-common-space.patch
@@ -0,0 +1,279 @@
+From 7063a1583166abe1a9cefed38c2f53a0e14a0005 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..f22a8646c0
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/111-bcm47xx-nvram-add-new-nvram-driver-with-dt-support.patch
@@ -0,0 +1,588 @@
+From 71a6bff8656a1713615ffdd9139a83d65ba46c6d Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..c3d5b4d7bc
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/112-bcm53xx-sprom-add-sprom-driver.patch
@@ -0,0 +1,762 @@
+From 4e0ab3269a6d260a41a3673157753147f5f71341 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..e38b5bc1c6
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/121-bcma-get-irqs-from-dt.patch
@@ -0,0 +1,38 @@
+From 6611afa6c49434780096cdf2c1028f0ac277f9bc Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..fadf03cbcd
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/122-bcma-fill-core-details-for-every-device.patch
@@ -0,0 +1,65 @@
+From 487b997353e2e3afe9c452b20ff5e4320d76e9c3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..fbc75ceff3
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/123-bcma-get-sprom-from-devicetree.patch
@@ -0,0 +1,88 @@
+From bd9106f5907080b467026bdaaea979fac8c7badb Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..024123f9d3
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/130-ARM-BCM5301X-register-bcma-bus.patch
@@ -0,0 +1,114 @@
+From 414f0ad9b3a8e8ee6eaf09c6d79d5f448ac28630 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..a977116a53
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/131-ARM-BCM5301X-add-restart-support.patch
@@ -0,0 +1,69 @@
+From 28b11a8b1258214b3b5d58bb6e3bbcb0fc9fd4fe Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..b2e56f8e40
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/150-pci-do-not-probe-too-early.patch
@@ -0,0 +1,29 @@
+From cf72936c001056de1cfcb27dd9a232f5484ec59c Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..9254089c95
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch
@@ -0,0 +1,670 @@
+From cc2cda651fcbc498bf513a6b802dca19944bcb37 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..56db9c1577
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/300-ARM-BCM5301X-Disable-MMU-and-Dcache-for-decompression.patch
@@ -0,0 +1,195 @@
+From 26023cdfacaf116545b1087b9d1fe50dc6fbda10 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..9595129e53
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/301-ARM-BCM5301X-Add-buttons-support-for-Netgear-R6250.patch
@@ -0,0 +1,59 @@
+From e1b44fc2e3cf76be1213bde07fc37c47eff39158 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..ca4c95649f
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/302-ARM-BCM5301X-Add-DT-for-Netgear-R6300-V2.patch
@@ -0,0 +1,125 @@
+From 788069f86c7fc1ce54661651e695943fb47a5188 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..b8b0a5ffd2
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/303-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-1750DHP.patch
@@ -0,0 +1,102 @@
+From b7620da56595c5505e4a10b8779cec0362b59db2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..d0f5a86a3c
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/304-ARM-BCM5301X-Add-DT-for-Asus-RT-N18U.patch
@@ -0,0 +1,160 @@
+From 89fe6f9b7875f74e7d63a90ae3a51d84d3cf9369 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..6d6baa3bc4
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/305-ARM-BCM5301X-Add-DT-for-Buffalo-WZR-600DHP2.patch
@@ -0,0 +1,97 @@
+From 4812cd75bc85a9f7050e2b58c1cf17e3bd4dc7f8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..de1e506255
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/402-mtd-spi-nor-allow-NULL-as-spi_device_id-in-spi_nor_s.patch
@@ -0,0 +1,46 @@
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -926,29 +926,23 @@ int spi_nor_scan(struct spi_nor *nor, co
+ if (ret)
+ return ret;
+
+- info = (void *)id->driver_data;
+-
+- if (info->jedec_id) {
+- const struct spi_device_id *jid;
+-
+- jid = nor->read_id(nor);
+- if (IS_ERR(jid)) {
+- return PTR_ERR(jid);
+- } else if (jid != id) {
+- /*
+- * JEDEC knows better, so overwrite platform ID. We
+- * can't trust partitions any longer, but we'll let
+- * mtd apply them anyway, since some partitions may be
+- * marked read-only, and we don't want to lose that
+- * information, even if it's not 100% accurate.
+- */
+- dev_warn(dev, "found %s, expected %s\n",
+- jid->name, id->name);
+- id = jid;
+- info = (void *)jid->driver_data;
++ if (id) {
++ info = (void *)id->driver_data;
++ if (info->jedec_id) {
++ dev_warn(dev,
++ "passed SPI device ID (%s) contains JEDEC, ignoring it, driver should be fixed!\n",
++ id->name);
++ id = NULL;
+ }
+ }
+
++ if (!id) {
++ id = nor->read_id(nor);
++ if (IS_ERR(id))
++ return PTR_ERR(id);
++ }
++ info = (void *)id->driver_data;
++
+ mutex_init(&nor->lock);
+
+ /*
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
index 0000000000..c274a7e80c
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/403-mtd-spi-nor-refactor-wait-till-ready.patch
@@ -0,0 +1,374 @@
+--- a/drivers/mtd/spi-nor/fsl-quadspi.c
++++ b/drivers/mtd/spi-nor/fsl-quadspi.c
+@@ -719,16 +719,10 @@ static int fsl_qspi_read(struct spi_nor
+ {
+ struct fsl_qspi *q = nor->priv;
+ u8 cmd = nor->read_opcode;
+- int ret;
+
+ dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n",
+ cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len);
+
+- /* Wait until the previous command is finished. */
+- ret = nor->wait_till_ready(nor);
+- if (ret)
+- return ret;
+-
+ /* Read out the data directly from the AHB buffer.*/
+ memcpy(buf, q->ahb_base + q->chip_base_addr + from, len);
+
+@@ -744,16 +738,6 @@ static int fsl_qspi_erase(struct spi_nor
+ dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
+ nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs);
+
+- /* Wait until finished previous write command. */
+- ret = nor->wait_till_ready(nor);
+- if (ret)
+- return ret;
+-
+- /* Send write enable, then erase commands. */
+- ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+- if (ret)
+- return ret;
+-
+ ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
+ if (ret)
+ return ret;
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -163,81 +163,69 @@ static inline int set_4byte(struct spi_n
+ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
+ }
+ }
+-
+-static int spi_nor_wait_till_ready(struct spi_nor *nor)
++static inline int spi_nor_sr_ready(struct spi_nor *nor)
+ {
+- unsigned long deadline;
+- int sr;
+-
+- deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+-
+- do {
+- cond_resched();
++ int sr = read_sr(nor);
++ if (sr < 0)
++ return sr;
++ else
++ return !(sr & SR_WIP);
++}
+
+- sr = read_sr(nor);
+- if (sr < 0)
+- break;
+- else if (!(sr & SR_WIP))
+- return 0;
+- } while (!time_after_eq(jiffies, deadline));
++static inline int spi_nor_fsr_ready(struct spi_nor *nor)
++{
++ int fsr = read_fsr(nor);
++ if (fsr < 0)
++ return fsr;
++ else
++ return fsr & FSR_READY;
++}
+
+- return -ETIMEDOUT;
++static int spi_nor_ready(struct spi_nor *nor)
++{
++ int sr, fsr;
++ sr = spi_nor_sr_ready(nor);
++ if (sr < 0)
++ return sr;
++ fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
++ if (fsr < 0)
++ return sr;
++ return sr && fsr;
+ }
+
+-static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
++/*
++ * Service routine to read status register until ready, or timeout occurs.
++ * Returns non-zero if error.
++ */
++static int spi_nor_wait_till_ready(struct spi_nor *nor)
+ {
+ unsigned long deadline;
+- int sr;
+- int fsr;
++ int ret;
+
+ deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+
+ do {
+ cond_resched();
+
+- sr = read_sr(nor);
+- if (sr < 0) {
+- break;
+- } else if (!(sr & SR_WIP)) {
+- fsr = read_fsr(nor);
+- if (fsr < 0)
+- break;
+- if (fsr & FSR_READY)
+- return 0;
+- }
++ ret = spi_nor_ready(nor);
++ if (ret < 0)
++ return ret;
++ if (ret)
++ return 0;
+ } while (!time_after_eq(jiffies, deadline));
+
+ return -ETIMEDOUT;
+ }
+
+ /*
+- * Service routine to read status register until ready, or timeout occurs.
+- * Returns non-zero if error.
+- */
+-static int wait_till_ready(struct spi_nor *nor)
+-{
+- return nor->wait_till_ready(nor);
+-}
+-
+-/*
+ * Erase the whole flash memory
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ */
+ static int erase_chip(struct spi_nor *nor)
+ {
+- int ret;
+-
+ dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
+
+- /* Wait until finished previous write command. */
+- ret = wait_till_ready(nor);
+- if (ret)
+- return ret;
+-
+- /* Send write enable, then erase commands. */
+- write_enable(nor);
+-
+ return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
+ }
+
+@@ -290,6 +278,8 @@ static int spi_nor_erase(struct mtd_info
+ if (ret)
+ return ret;
+
++ write_enable(nor);
++
+ /* whole-chip erase? */
+ if (len == mtd->size) {
+ if (erase_chip(nor)) {
+@@ -297,6 +287,10 @@ static int spi_nor_erase(struct mtd_info
+ goto erase_err;
+ }
+
++ ret = spi_nor_wait_till_ready(nor);
++ if (ret)
++ goto erase_err;
++
+ /* REVISIT in some cases we could speed up erasing large regions
+ * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up
+ * to use "small sector erase", but that's not always optimal.
+@@ -312,9 +306,15 @@ static int spi_nor_erase(struct mtd_info
+
+ addr += mtd->erasesize;
+ len -= mtd->erasesize;
++
++ ret = spi_nor_wait_till_ready(nor);
++ if (ret)
++ goto erase_err;
+ }
+ }
+
++ write_disable(nor);
++
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
+
+ instr->state = MTD_ERASE_DONE;
+@@ -339,11 +339,6 @@ static int spi_nor_lock(struct mtd_info
+ if (ret)
+ return ret;
+
+- /* Wait until finished previous command */
+- ret = wait_till_ready(nor);
+- if (ret)
+- goto err;
+-
+ status_old = read_sr(nor);
+
+ if (offset < mtd->size - (mtd->size / 2))
+@@ -386,11 +381,6 @@ static int spi_nor_unlock(struct mtd_inf
+ if (ret)
+ return ret;
+
+- /* Wait until finished previous command */
+- ret = wait_till_ready(nor);
+- if (ret)
+- goto err;
+-
+ status_old = read_sr(nor);
+
+ if (offset+len > mtd->size - (mtd->size / 64))
+@@ -703,11 +693,6 @@ static int sst_write(struct mtd_info *mt
+ if (ret)
+ return ret;
+
+- /* Wait until finished previous write command. */
+- ret = wait_till_ready(nor);
+- if (ret)
+- goto time_out;
+-
+ write_enable(nor);
+
+ nor->sst_write_second = false;
+@@ -719,7 +704,7 @@ static int sst_write(struct mtd_info *mt
+
+ /* write one byte. */
+ nor->write(nor, to, 1, retlen, buf);
+- ret = wait_till_ready(nor);
++ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto time_out;
+ }
+@@ -731,7 +716,7 @@ static int sst_write(struct mtd_info *mt
+
+ /* write two bytes. */
+ nor->write(nor, to, 2, retlen, buf + actual);
+- ret = wait_till_ready(nor);
++ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto time_out;
+ to += 2;
+@@ -740,7 +725,7 @@ static int sst_write(struct mtd_info *mt
+ nor->sst_write_second = false;
+
+ write_disable(nor);
+- ret = wait_till_ready(nor);
++ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto time_out;
+
+@@ -751,7 +736,7 @@ static int sst_write(struct mtd_info *mt
+ nor->program_opcode = SPINOR_OP_BP;
+ nor->write(nor, to, 1, retlen, buf + actual);
+
+- ret = wait_till_ready(nor);
++ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto time_out;
+ write_disable(nor);
+@@ -779,11 +764,6 @@ static int spi_nor_write(struct mtd_info
+ if (ret)
+ return ret;
+
+- /* Wait until finished previous write command. */
+- ret = wait_till_ready(nor);
+- if (ret)
+- goto write_err;
+-
+ write_enable(nor);
+
+ page_offset = to & (nor->page_size - 1);
+@@ -802,16 +782,20 @@ static int spi_nor_write(struct mtd_info
+ if (page_size > nor->page_size)
+ page_size = nor->page_size;
+
+- wait_till_ready(nor);
++ ret = spi_nor_wait_till_ready(nor);
++ if (ret)
++ goto write_err;
++
+ write_enable(nor);
+
+ nor->write(nor, to + i, page_size, retlen, buf + i);
+ }
+ }
+
++ ret = spi_nor_wait_till_ready(nor);
+ write_err:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
+- return 0;
++ return ret;
+ }
+
+ static int macronix_quad_enable(struct spi_nor *nor)
+@@ -824,7 +808,7 @@ static int macronix_quad_enable(struct s
+ nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
+ nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
+
+- if (wait_till_ready(nor))
++ if (spi_nor_wait_till_ready(nor))
+ return 1;
+
+ ret = read_sr(nor);
+@@ -906,8 +890,6 @@ static int spi_nor_check(struct spi_nor
+
+ if (!nor->read_id)
+ nor->read_id = spi_nor_read_id;
+- if (!nor->wait_till_ready)
+- nor->wait_till_ready = spi_nor_wait_till_ready;
+
+ return 0;
+ }
+@@ -978,9 +960,8 @@ int spi_nor_scan(struct spi_nor *nor, co
+ else
+ mtd->_write = spi_nor_write;
+
+- if ((info->flags & USE_FSR) &&
+- nor->wait_till_ready == spi_nor_wait_till_ready)
+- nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
++ if (info->flags & USE_FSR)
++ nor->flags |= SNOR_F_USE_FSR;
+
+ #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ /* prefer "small sector" erase if possible */
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -116,6 +116,10 @@ enum spi_nor_ops {
+ SPI_NOR_OPS_UNLOCK,
+ };
+
++enum spi_nor_option_flags {
++ SNOR_F_USE_FSR = BIT(0),
++};
++
+ /**
+ * struct spi_nor - Structure for defining a the SPI NOR layer
+ * @mtd: point to a mtd_info structure
+@@ -129,6 +133,7 @@ enum spi_nor_ops {
+ * @program_opcode: the program opcode
+ * @flash_read: the mode of the read
+ * @sst_write_second: used by the SST write operation
++ * @flags: flag options for the current SPI-NOR (SNOR_F_*)
+ * @cfg: used by the read_xfer/write_xfer
+ * @cmd_buf: used by the write_reg
+ * @prepare: [OPTIONAL] do some preparations for the
+@@ -141,7 +146,6 @@ enum spi_nor_ops {
+ * @write_reg: [DRIVER-SPECIFIC] write data to the register
+ * @read_id: [REPLACEABLE] read out the ID data, and find
+ * the proper spi_device_id
+- * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready
+ * @read: [DRIVER-SPECIFIC] read data from the SPI NOR
+ * @write: [DRIVER-SPECIFIC] write data to the SPI NOR
+ * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
+@@ -160,6 +164,7 @@ struct spi_nor {
+ u8 program_opcode;
+ enum read_mode flash_read;
+ bool sst_write_second;
++ u32 flags;
+ struct spi_nor_xfer_cfg cfg;
+ u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
+
+@@ -173,7 +178,6 @@ struct spi_nor {
+ int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
+ int write_enable);
+ const struct spi_device_id *(*read_id)(struct spi_nor *nor);
+- int (*wait_till_ready)(struct spi_nor *nor);
+
+ int (*read)(struct spi_nor *nor, loff_t from,
+ size_t len, size_t *retlen, u_char *read_buf);
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
index 0000000000..9a9d903f82
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch
@@ -0,0 +1,263 @@
+--- a/drivers/mtd/spi-nor/Kconfig
++++ b/drivers/mtd/spi-nor/Kconfig
+@@ -28,4 +28,10 @@ config SPI_FSL_QUADSPI
+ This enables support for the Quad SPI controller in master mode.
+ We only connect the NOR to this controller now.
+
++config MTD_SPI_BCM53XXSPIFLASH
++ tristate "SPI-NOR flashes connected to the Broadcom ARM SoC"
++ depends on MTD_SPI_NOR
++ help
++ SPI driver for flashes used on Broadcom ARM SoCs.
++
+ endif # MTD_SPI_NOR
+--- a/drivers/mtd/spi-nor/Makefile
++++ b/drivers/mtd/spi-nor/Makefile
+@@ -1,2 +1,3 @@
+ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
+ obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
++obj-$(CONFIG_MTD_SPI_BCM53XXSPIFLASH) += bcm53xxspiflash.o
+--- /dev/null
++++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c
+@@ -0,0 +1,241 @@
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/spi/spi.h>
++#include <linux/mtd/spi-nor.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/cfi.h>
++
++static const char * const probes[] = { "bcm47xxpart", NULL };
++
++struct bcm53xxsf {
++ struct spi_device *spi;
++ struct mtd_info mtd;
++ struct spi_nor nor;
++};
++
++/**************************************************
++ * spi-nor API
++ **************************************************/
++
++static int bcm53xxspiflash_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
++ int len)
++{
++ struct bcm53xxsf *b53sf = nor->priv;
++
++ return spi_write_then_read(b53sf->spi, &opcode, 1, buf, len);
++}
++
++static int bcm53xxspiflash_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
++ int len, int write_enable)
++{
++ struct bcm53xxsf *b53sf = nor->priv;
++ u8 *cmd = kzalloc(len + 1, GFP_KERNEL);
++ int err;
++
++ if (!cmd)
++ return -ENOMEM;
++
++ cmd[0] = opcode;
++ memcpy(&cmd[1], buf, len);
++ err = spi_write(b53sf->spi, cmd, len + 1);
++
++ kfree(cmd);
++
++ return err;
++}
++
++static int bcm53xxspiflash_read(struct spi_nor *nor, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ struct bcm53xxsf *b53sf = nor->priv;
++ struct spi_message m;
++ struct spi_transfer t[2] = { { 0 }, { 0 } };
++ unsigned char cmd[5];
++ int cmd_len = 0;
++ int err;
++
++ spi_message_init(&m);
++
++ cmd[cmd_len++] = SPINOR_OP_READ;
++ if (b53sf->mtd.size > 0x1000000)
++ cmd[cmd_len++] = (from & 0xFF000000) >> 24;
++ cmd[cmd_len++] = (from & 0x00FF0000) >> 16;
++ cmd[cmd_len++] = (from & 0x0000FF00) >> 8;
++ cmd[cmd_len++] = (from & 0x000000FF) >> 0;
++
++ t[0].tx_buf = cmd;
++ t[0].len = cmd_len;
++ spi_message_add_tail(&t[0], &m);
++
++ t[1].rx_buf = buf;
++ t[1].len = len;
++ spi_message_add_tail(&t[1], &m);
++
++ err = spi_sync(b53sf->spi, &m);
++ if (err)
++ return err;
++
++ if (retlen && m.actual_length > cmd_len)
++ *retlen = m.actual_length - cmd_len;
++
++ return 0;
++}
++
++static void bcm53xxspiflash_write(struct spi_nor *nor, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ struct bcm53xxsf *b53sf = nor->priv;
++ struct spi_message m;
++ struct spi_transfer t = { 0 };
++ u8 *cmd = kzalloc(len + 5, GFP_KERNEL);
++ int cmd_len = 0;
++ int err;
++
++ if (!cmd)
++ return;
++
++ spi_message_init(&m);
++
++ cmd[cmd_len++] = nor->program_opcode;
++ if (b53sf->mtd.size > 0x1000000)
++ cmd[cmd_len++] = (to & 0xFF000000) >> 24;
++ cmd[cmd_len++] = (to & 0x00FF0000) >> 16;
++ cmd[cmd_len++] = (to & 0x0000FF00) >> 8;
++ cmd[cmd_len++] = (to & 0x000000FF) >> 0;
++ memcpy(&cmd[cmd_len], buf, len);
++
++ t.tx_buf = cmd;
++ t.len = cmd_len + len;
++ spi_message_add_tail(&t, &m);
++
++ err = spi_sync(b53sf->spi, &m);
++ if (err)
++ goto out;
++
++ if (retlen && m.actual_length > cmd_len)
++ *retlen += m.actual_length - cmd_len;
++
++out:
++ kfree(cmd);
++}
++
++static int bcm53xxspiflash_erase(struct spi_nor *nor, loff_t offs)
++{
++ struct bcm53xxsf *b53sf = nor->priv;
++ unsigned char cmd[5];
++ int i;
++
++ i = 0;
++ cmd[i++] = nor->erase_opcode;
++ if (b53sf->mtd.size > 0x1000000)
++ cmd[i++] = (offs & 0xFF000000) >> 24;
++ cmd[i++] = ((offs & 0x00FF0000) >> 16);
++ cmd[i++] = ((offs & 0x0000FF00) >> 8);
++ cmd[i++] = ((offs & 0x000000FF) >> 0);
++
++ return spi_write(b53sf->spi, cmd, i);
++}
++
++static const struct spi_device_id *bcm53xxspiflash_read_id(struct spi_nor *nor)
++{
++ struct bcm53xxsf *b53sf = nor->priv;
++ struct device *dev = &b53sf->spi->dev;
++ const struct spi_device_id *id;
++ unsigned char cmd[4];
++ unsigned char resp[2];
++ char *name = NULL;
++ int err;
++
++ /* SST and Winbond/NexFlash specific command */
++ cmd[0] = 0x90; /* Read Manufacturer / Device ID */
++ cmd[1] = 0;
++ cmd[2] = 0;
++ cmd[3] = 0;
++ err = spi_write_then_read(b53sf->spi, cmd, 4, resp, 2);
++ if (err < 0) {
++ dev_err(dev, "error reading SPI flash id\n");
++ return ERR_PTR(-EBUSY);
++ }
++ switch (resp[0]) {
++ case 0xef: /* Winbond/NexFlash */
++ switch (resp[1]) {
++ case 0x17:
++ name = "w25q128";
++ break;
++ }
++ if (!name) {
++ dev_err(dev, "Unknown Winbond/NexFlash flash: %02X %02X\n",
++ resp[0], resp[1]);
++ return ERR_PTR(-ENOTSUPP);
++ }
++ goto found_name;
++ }
++
++ /* TODO: Try more ID commands */
++
++ return ERR_PTR(-ENODEV);
++
++found_name:
++ id = spi_nor_match_id(name);
++ if (!id) {
++ dev_err(dev, "No matching entry for %s flash\n", name);
++ return ERR_PTR(-ENOENT);
++ }
++
++ return id;
++}
++
++/**************************************************
++ * SPI driver
++ **************************************************/
++
++static int bcm53xxspiflash_probe(struct spi_device *spi)
++{
++ struct bcm53xxsf *b53sf;
++ int err;
++
++ b53sf = devm_kzalloc(&spi->dev, sizeof(*b53sf), GFP_KERNEL);
++ if (!b53sf)
++ return -ENOMEM;
++ spi_set_drvdata(spi, b53sf);
++
++ b53sf->spi = spi;
++
++ b53sf->mtd.priv = &b53sf->nor;
++
++ b53sf->nor.mtd = &b53sf->mtd;
++ b53sf->nor.dev = &spi->dev;
++ b53sf->nor.read_reg = bcm53xxspiflash_read_reg;
++ b53sf->nor.write_reg = bcm53xxspiflash_write_reg;
++ b53sf->nor.read = bcm53xxspiflash_read;
++ b53sf->nor.write = bcm53xxspiflash_write;
++ b53sf->nor.erase = bcm53xxspiflash_erase;
++ b53sf->nor.read_id = bcm53xxspiflash_read_id;
++ b53sf->nor.priv = b53sf;
++
++ err = spi_nor_scan(&b53sf->nor, NULL, SPI_NOR_NORMAL);
++ if (err)
++ return err;
++
++ err = mtd_device_parse_register(&b53sf->mtd, probes, NULL, NULL, 0);
++ if (err)
++ return err;
++
++ return 0;
++}
++
++static int bcm53xxspiflash_remove(struct spi_device *spi)
++{
++ return 0;
++}
++
++static struct spi_driver bcm53xxspiflash_driver = {
++ .driver = {
++ .name = "bcm53xxspiflash",
++ .owner = THIS_MODULE,
++ },
++ .probe = bcm53xxspiflash_probe,
++ .remove = bcm53xxspiflash_remove,
++};
++
++module_spi_driver(bcm53xxspiflash_driver);
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
index 0000000000..04d0058d75
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch
@@ -0,0 +1,42 @@
+--- a/drivers/mtd/spi-nor/bcm53xxspiflash.c
++++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c
+@@ -173,7 +173,8 @@ static const struct spi_device_id *bcm53
+
+ /* TODO: Try more ID commands */
+
+- return ERR_PTR(-ENODEV);
++ /* Some chips used by Broadcom may actually support JEDEC */
++ return spi_nor_read_id(nor);
+
+ found_name:
+ id = spi_nor_match_id(name);
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -630,7 +630,7 @@ const struct spi_device_id spi_nor_ids[]
+ };
+ EXPORT_SYMBOL_GPL(spi_nor_ids);
+
+-static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
++const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
+ {
+ int tmp;
+ u8 id[5];
+@@ -661,6 +661,7 @@ static const struct spi_device_id *spi_n
+ dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
+ return ERR_PTR(-ENODEV);
+ }
++EXPORT_SYMBOL_GPL(spi_nor_read_id);
+
+ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -188,6 +188,8 @@ struct spi_nor {
+ void *priv;
+ };
+
++const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor);
++
+ /**
+ * spi_nor_scan() - scan the SPI NOR
+ * @nor: the spi_nor structure
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
index 0000000000..3372bf5bbb
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/410-mtd-bcm47xxpart-alloc-memory-for-more-partitions.patch
@@ -0,0 +1,32 @@
+From 6b833541d73894b5afd40d69949f8f6099db2abf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..0b549f842c
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/420-mtd-bcm5301x_nand.patch
@@ -0,0 +1,1616 @@
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -516,4 +516,10 @@ config MTD_NAND_XWAY
+ Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
+ to the External Bus Unit (EBU).
+
++config MTD_NAND_BCM
++ tristate "Support for NAND on some Broadcom SoC"
++ help
++ This driver is currently used for the NAND flash controller on the
++ Broadcom BCM5301X (NorthStar) SoCs.
++
+ endif # MTD_NAND
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -50,5 +50,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740
+ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
+ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
++obj-$(CONFIG_MTD_NAND_BCM) += bcm_nand.o
+
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
+--- /dev/null
++++ b/drivers/mtd/nand/bcm_nand.c
+@@ -0,0 +1,1591 @@
++/*
++ * Nortstar NAND controller driver
++ * for Linux NAND library and MTD interface
++ *
++ * (c) Broadcom, Inc. 2012 All Rights Reserved.
++ * Copyright 2014 Hauke Mehrtens <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
index 0000000000..b8229cd38c
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/500-UBI-Detect-EOF-mark-and-erase-all-remaining-blocks.patch
@@ -0,0 +1,34 @@
+From f41f8b42db092e505382f7120994de21590dff48 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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
index 0000000000..c3ae68d714
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/900-bgmac-some-fixes-to-get-bgmac-work.patch
@@ -0,0 +1,33 @@
+From 0bd576e93a188fd3aab769b622fb3d35fa9bc7a7 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <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
index 0000000000..f20b1cc82d
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/901-bcma-register-SoC-later-as-a-module.patch
@@ -0,0 +1,42 @@
+From fee1501c494954f6e889563ca44aadfe4a83a643 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <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 */