diff options
Diffstat (limited to 'target/linux/bcm53xx/patches-3.18')
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 */ |