bcm53xx: initial support for kernel 3.18

This adds initial support for kernel 3.18.

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

SVN-Revision: 43097
This commit is contained in:
Hauke Mehrtens 2014-10-27 23:02:08 +00:00
parent d8d692342f
commit 7fc9bb1aca
30 changed files with 6385 additions and 0 deletions

View file

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

View file

@ -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

View file

@ -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

View file

@ -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>;
+ };
+ };
};

View file

@ -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";
+ };
+ };
};

View file

@ -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 */

View file

@ -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;
}

View file

@ -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");

View file

@ -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,

View file

@ -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),

View file

@ -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;

View file

@ -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>;
+ };
};
};

View file

@ -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

View file

@ -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);

View file

@ -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");

View file

@ -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)

View file

@ -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"

View file

@ -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>;
+ };
+ };
+};

View file

@ -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>;
+ };
+ };
+};

View file

@ -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,
};

View file

@ -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>;
+ };
+ };
+};

View file

@ -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);
/*

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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.

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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;

View file

@ -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 */