This adds device tree support to bcma. In addition it also provides the
sprom needed by bcma through a device tree driver.
This is needed for the Broadcom BCM47XX/BCM53XX ARM SoCs (NorthStar).
bcma drives the system AIX bus. This bus is scanable, so we
automatically detect the cores attached to this bus. We only have to
provide the address of the first core, this can now be done through
device tree.
It is not possible to find out what IRQ is associated to a specific
core, we have to provide this through device tree.
Broadcom uses a nvram which is a key value store on the flash chip.
This nvram contains the mac addresses to be used by the wifi and
Ethernet devices, the configuration data for the switch, the
calibration data for the wifi cores and some more stuff. Currently bcma
assumes to get some sprom when it gets initialized on a SoC. This sprom
is a structure with all the data which are originally stored in the
nvram. When bcma is used on a PCIe wifi card this data is fetched from
some eeporm on the card. The nvram could contains more than one sprom,
in case there is a main Baordocm SoC and a Broadcom wifi card connected
via PCIe this PCIe cards mostly use the nvram to store the sprom with
the calibration data.
This patch series adds two drivers, the nvram driver which reads the
nvram and provides an interface to fetch values by giving a key and a
driver which creates a sprom based on a given nvram. These two things
are no real drivers, they are just converting stuff. I made them
drivers so they can be replaced with some other drivers which are
fetching the sprom from other storages. I know that the BCM63XX SoCs
are also using an sprom, but there it is stored in a file on the file
system.
Some code was copied from the mips code at arch/mips/bcm47xx/ in the
end I want to make this mips code also use these new drivers.
There is no device I know of which uses more than one nvram and I do
not think there will ever be such a device. With this patch you have to
provide a reference to the nvram you want to fetch data from which is a
little bit annoying, should I remove this parameter and assume there is
just one nvram driver in the system?
I am interested on comments if this architecture is correct or if there
are some improvements I could do. I haven't fond similar code in the
kernel.
Through which tree should I merge the final patches?
This is based on linus 3.17-rc1.
Log output:
bcma: bus0: Found chip with id 0xCF12, rev 0x00 and package 0x02
bcma: bus0: Core 0 found: ChipCommon (manuf 0x4BF, id 0x800, rev 0x2A, class 0x0)
bcma: bus0: Core 1 found: Chipcommon B (manuf 0x4BF, id 0x50B, rev 0x01, class 0x0)
bcma: bus0: Core 2 found: DMA (manuf 0x4BF, id 0x502, rev 0x01, class 0x0)
bcma: bus0: Core 3 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0)
bcma: bus0: Core 4 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0)
bcma: bus0: Core 5 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0)
bcma: bus0: Core 6 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0)
bcma: bus0: Core 7 found: PCIe Gen 2 (manuf 0x4BF, id 0x501, rev 0x01, class 0x0)
bcma: bus0: Core 8 found: PCIe Gen 2 (manuf 0x4BF, id 0x501, rev 0x01, class 0x0)
bcma: bus0: Core 9 found: ARM Cortex A9 core (ihost) (manuf 0x4BF, id 0x510, rev 0x01, class 0x0)
bcma: bus0: Core 10 found: USB 2.0 (manuf 0x4BF, id 0x504, rev 0x01, class 0x0)
bcma: bus0: Core 11 found: USB 3.0 (manuf 0x4BF, id 0x505, rev 0x01, class 0x0)
bcma: bus0: Core 12 found: SDIO3 (manuf 0x4BF, id 0x503, rev 0x01, class 0x0)
bcma: bus0: Core 13 found: ARM Cortex A9 JTAG (manuf 0x4BF, id 0x506, rev 0x01, class 0x0)
bcma: bus0: Core 14 found: Denali DDR2/DDR3 memory controller (manuf 0x4BF, id 0x507, rev 0x01, class 0x0)
bcma: bus0: Core 15 found: ROM (manuf 0x4BF, id 0x508, rev 0x01, class 0x0)
bcma: bus0: Core 16 found: NAND flash controller (manuf 0x4BF, id 0x509, rev 0x01, class 0x0)
bcma: bus0: Core 17 found: SPI flash controller (manuf 0x4BF, id 0x50A, rev 0x01, class 0x0)
bcma: bus0: Found sprom from device tree provider
bgmac bcma0:1: Found PHY addr: 30 (NOREGS)
bgmac bcma0:1: Support for Roboswitch not implemented
libphy: bgmac mii bus: probed
b53_common: found switch: BCM53011, rev 2
bgmac bcma0:2: Found PHY addr: 0
bgmac bcma0:2: Support for Roboswitch not implemented
libphy: bgmac mii bus: probed
Hauke Mehrtens (7):
MIPS: BCM47XX: move the nvram header file into common space
bcm47xx-nvram: add new broadcom nvram driver with dt support
bcm47xx-sprom: add Broadcom sprom parser driver
bcma: register bcma as device tree driver
bcma: get IRQ numbers from dt
bcma: get sprom from devicetree
ARM: BCM5301X: register bcma bus
Documentation/devicetree/bindings/bus/bcma.txt | 46 ++
.../devicetree/bindings/misc/bcm47xx-nvram.txt | 19 +
.../devicetree/bindings/misc/bcm47xx-sprom.txt | 16 +
arch/arm/boot/dts/bcm4708.dtsi | 58 ++
arch/mips/bcm47xx/board.c | 42 +-
arch/mips/bcm47xx/nvram.c | 9 +-
arch/mips/bcm47xx/setup.c | 6 +-
arch/mips/bcm47xx/sprom.c | 6 +-
arch/mips/bcm47xx/time.c | 4 +-
arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h | 53 --
drivers/bcma/host_soc.c | 70 +++
drivers/bcma/main.c | 42 +-
drivers/bcma/sprom.c | 51 +-
drivers/misc/Kconfig | 27 +
drivers/misc/Makefile | 2 +
drivers/misc/bcm47xx-nvram.c | 215 +++++++
drivers/misc/bcm47xx-sprom.c | 690 +++++++++++++++++++++
drivers/net/ethernet/broadcom/b44.c | 10 +-
drivers/net/ethernet/broadcom/bgmac.c | 7 +-
drivers/ssb/driver_chipcommon_pmu.c | 9 +-
include/linux/bcm47xx_nvram.h | 71 +++
include/linux/bcma/bcma.h | 2 +
22 files changed, 1350 insertions(+), 105 deletions(-)
create mode 100644 Documentation/devicetree/bindings/bus/bcma.txt
create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-nvram.txt
create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
delete mode 100644 arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h
create mode 100644 drivers/misc/bcm47xx-nvram.c
create mode 100644 drivers/misc/bcm47xx-sprom.c
create mode 100644 include/linux/bcm47xx_nvram.h
--
1.9.1
On 08/26/2014 10:54 PM, Rafał Miłecki wrote:
> On 24 August 2014 23:24, Hauke Mehrtens <[email protected]> wrote:
>> +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>;
>> +};
>
> Could we avoid that? Type of flash can easily be checked in the code.
> All we need to do is to read BCMA_IOST register of BCMA_CORE_NS_ROM
> core.
So there is a boot status register you can read to tell what type of
flash you booted from, but does that also give you the resource ranges
for these type of flashes? Presumably they will be mapped into different
addresses (at least bcm63xx is like that), that information needs to be
listed somewhere.
--
Florian
On Sunday 24 August 2014 23:24:44 Hauke Mehrtens wrote:
> +#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;
You can remove the #ifdef above if you change this into
if (!IS_ENABLED(CONFIG_OF) || !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;
> +}
missing of_node_put().
Arnd
On 24 August 2014 23:24, Hauke Mehrtens <[email protected]> wrote:
> +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>;
> +};
Could we avoid that? Type of flash can easily be checked in the code.
All we need to do is to read BCMA_IOST register of BCMA_CORE_NS_ROM
core.
Moving mach-bcm47xx/bcm47xx_nvram.h to include/linux/bcm47xx_nvram.h
makes it possible to reuse this header on the ARM based bcm47xx/bcm53xx
SoCs (e.g. BCM5301X devices). Broadcom uses ARM CPUs in their new SoC
form the bcm47xx and bcm53xx line, but many other things like nvram
stayed the same.
This is a preparation for adding a new nvram driver, which can be used
by the ARM SoC and the MIPS SoC code. The device drivers accessing
nvram do not have to care about ARM or MIPS SoC version.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
arch/mips/bcm47xx/board.c | 2 +-
arch/mips/bcm47xx/nvram.c | 2 +-
arch/mips/bcm47xx/setup.c | 2 +-
arch/mips/bcm47xx/sprom.c | 2 +-
arch/mips/bcm47xx/time.c | 2 +-
arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h | 53 -----------------
drivers/net/ethernet/broadcom/b44.c | 8 +--
drivers/net/ethernet/broadcom/bgmac.c | 2 +-
drivers/ssb/driver_chipcommon_pmu.c | 6 +-
include/linux/bcm47xx_nvram.h | 66 ++++++++++++++++++++++
10 files changed, 74 insertions(+), 71 deletions(-)
delete mode 100644 arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h
create mode 100644 include/linux/bcm47xx_nvram.h
diff --git a/arch/mips/bcm47xx/board.c b/arch/mips/bcm47xx/board.c
index b3ae068..ec46fd7 100644
--- 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;
diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c
index 2bed73a..c47a4a8 100644
--- 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];
diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c
index 2b63e7e..deadb71 100644
--- 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;
diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c
index 41226b6..29ced12 100644
--- 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>
diff --git a/arch/mips/bcm47xx/time.c b/arch/mips/bcm47xx/time.c
index 2c85d92..c57a515 100644
--- 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)
diff --git a/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h b/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h
deleted file mode 100644
index 36a3fc1..0000000
--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2005, Broadcom Corporation
- * Copyright (C) 2006, Felix Fietkau <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef __BCM47XX_NVRAM_H
-#define __BCM47XX_NVRAM_H
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-struct nvram_header {
- u32 magic;
- u32 len;
- u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */
- u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */
- u32 config_ncdl; /* ncdl values for memc */
-};
-
-#define NVRAM_HEADER 0x48534C46 /* 'FLSH' */
-#define NVRAM_VERSION 1
-#define NVRAM_HEADER_SIZE 20
-#define NVRAM_SPACE 0x8000
-
-#define FLASH_MIN 0x00020000 /* Minimum flash size */
-
-#define NVRAM_MAX_VALUE_LEN 255
-#define NVRAM_MAX_PARAM_LEN 64
-
-extern int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len);
-
-static inline void bcm47xx_nvram_parse_macaddr(char *buf, u8 macaddr[6])
-{
- if (strchr(buf, ':'))
- sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0],
- &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
- &macaddr[5]);
- else if (strchr(buf, '-'))
- sscanf(buf, "%hhx-%hhx-%hhx-%hhx-%hhx-%hhx", &macaddr[0],
- &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
- &macaddr[5]);
- else
- printk(KERN_WARNING "Can not parse mac address: %s\n", buf);
-}
-
-int bcm47xx_nvram_gpio_pin(const char *name);
-
-#endif /* __BCM47XX_NVRAM_H */
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index 4a7028d..d2714a2 100644
--- 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 *bp, u32 local, u32 remote)
__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(struct b44 *bp)
error:
pr_warning("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)
{
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index 05c6af6..bdda57b 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -16,7 +16,7 @@
#include <linux/phy.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.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),
diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c
index 1173a09..8d07d4d 100644
--- 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_chipcommon *cc)
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) {
diff --git a/include/linux/bcm47xx_nvram.h b/include/linux/bcm47xx_nvram.h
new file mode 100644
index 0000000..333d32c
--- /dev/null
+++ b/include/linux/bcm47xx_nvram.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2005, Broadcom Corporation
+ * Copyright (C) 2006, Felix Fietkau <[email protected]>
+ * Copyright (C) 2014 Hauke Mehrtens <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __BCM47XX_NVRAM_H
+#define __BCM47XX_NVRAM_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+struct nvram_header {
+ u32 magic;
+ u32 len;
+ u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */
+ u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */
+ u32 config_ncdl; /* ncdl values for memc */
+};
+
+#define NVRAM_HEADER 0x48534C46 /* 'FLSH' */
+#define NVRAM_VERSION 1
+#define NVRAM_HEADER_SIZE 20
+#define NVRAM_SPACE 0x8000
+
+#define FLASH_MIN 0x00020000 /* Minimum flash size */
+
+#define NVRAM_MAX_VALUE_LEN 255
+#define NVRAM_MAX_PARAM_LEN 64
+
+#ifdef CONFIG_BCM47XX
+int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len);
+
+int bcm47xx_nvram_gpio_pin(const char *name);
+#else
+static inline int bcm47xx_nvram_getenv(const char *name, char *val,
+ size_t val_len)
+{
+ return -ENXIO;
+}
+
+static inline int bcm47xx_nvram_gpio_pin(const char *name)
+{
+ return -ENXIO;
+}
+#endif
+
+static inline void bcm47xx_nvram_parse_macaddr(char *buf, u8 macaddr[6])
+{
+ if (strchr(buf, ':'))
+ sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0],
+ &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
+ &macaddr[5]);
+ else if (strchr(buf, '-'))
+ sscanf(buf, "%hhx-%hhx-%hhx-%hhx-%hhx-%hhx", &macaddr[0],
+ &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
+ &macaddr[5]);
+ else
+ pr_warn("Can not parse mac address: %s\n", buf);
+}
+#endif /* __BCM47XX_NVRAM_H */
--
1.9.1
On 24 August 2014 21:24, Hauke Mehrtens <[email protected]> wrote:
> 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.
I don't like this.
First, you change API (_getenv call), without explanation why/if we
really need this. We decided we're very unlikely to ever find devices
with multiple (separated) NVRAMs. So what's the point of having nvram
per device?
Secondly, don't duplicate the code. If you're going to switch bcm47xx
to this new code, handle it smarter. Modify bcm47xx mode, move it to
the "misc" and re-use in bcm53xx.
On Sunday 24 August 2014 23:24:41 Hauke Mehrtens wrote:
> This driver needs an nvram driver and fetches the sprom values from the
> nvram and provides it to any other driver. The calibration data for the
> wifi chip the mac address and some more board description data is
> stores in the sprom.
>
> This is based on a copy of arch/mips/bcm47xx/sprom.c and my plan is to
> make the bcm47xx MIPS SoCs also use this driver some time later.
> Signed-off-by: Hauke Mehrtens <[email protected]>
> ---
> .../devicetree/bindings/misc/bcm47xx-sprom.txt | 16 +
I'd prefer not to list the binding in a 'misc' category. Maybe we can
have a new category and move the misc/sram.txt into the same?
> drivers/misc/Kconfig | 14 +
> drivers/misc/Makefile | 1 +
> drivers/misc/bcm47xx-sprom.c | 690 +++++++++++++++++++++
On a similar note, putting the driver into drivers/misc seems
suboptimal: misc drivers should by definition be something that
is for some odd hardware with no external dependencies on it,
whereas your driver seems to be used by multiple other drivers.
Would it make sense to put it into drivers/bcma when that is the
only bus it is used on?
> 4 files changed, 721 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
> create mode 100644 drivers/misc/bcm47xx-sprom.c
>
> diff --git a/Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt b/Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
> new file mode 100644
> index 0000000..eed2a4a
> --- /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
Please use a specific SoC here as the compatible string, not something
with 'x' in it as a wildcard.
> +#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
Hmm, multiline macros are ugly. How about using one function to read
into an s64 internally using kstrtoll, and simple inline wrappers around
that for the smaller types, doing the appropriate range check?
> +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);
> +}
Rather than calling the same set of functions multiple times, can you
do this using a table driven approach for smaller code size and
improved readability?
> +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));
You can avoid the explicit be32_to_cpup here if you use
of_property_read_u32 above.
Arnd
This adds device tree support to bcma. In addition it also provides the
sprom needed by bcma through a device tree driver.
This is needed for the Broadcom BCM47XX/BCM53XX ARM SoCs (NorthStar).
bcma drives the system AIX bus. This bus is scanable, so we
automatically detect the cores attached to this bus. We only have to
provide the address of the first core, this can now be done through
device tree.
It is not possible to find out what IRQ is associated to a specific
core, we have to provide this through device tree.
Broadcom uses a nvram which is a key value store on the flash chip.
This nvram contains the mac addresses to be used by the wifi and
Ethernet devices, the configuration data for the switch, the
calibration data for the wifi cores and some more stuff. Currently bcma
assumes to get some sprom when it gets initialized on a SoC. This sprom
is a structure with all the data which are originally stored in the
nvram. When bcma is used on a PCIe wifi card this data is fetched from
some eeporm on the card. The nvram could contains more than one sprom,
in case there is a main Baordocm SoC and a Broadcom wifi card connected
via PCIe this PCIe cards mostly use the nvram to store the sprom with
the calibration data.
This patch series adds two drivers, the nvram driver which reads the
nvram and provides an interface to fetch values by giving a key and a
driver which creates a sprom based on a given nvram. These two things
are no real drivers, they are just converting stuff. I made them
drivers so they can be replaced with some other drivers which are
fetching the sprom from other storages. I know that the BCM63XX SoCs
are also using an sprom, but there it is stored in a file on the file
system.
Some code was copied from the mips code at arch/mips/bcm47xx/ in the
end I want to make this mips code also use these new drivers.
There is no device I know of which uses more than one nvram and I do
not think there will ever be such a device. With this patch you have to
provide a reference to the nvram you want to fetch data from which is a
little bit annoying, should I remove this parameter and assume there is
just one nvram driver in the system?
I am interested on comments if this architecture is correct or if there
are some improvements I could do. I haven't fond similar code in the
kernel.
Through which tree should I merge the final patches?
This is based on linus 3.17-rc1.
Log output:
bcma: bus0: Found chip with id 0xCF12, rev 0x00 and package 0x02
bcma: bus0: Core 0 found: ChipCommon (manuf 0x4BF, id 0x800, rev 0x2A, class 0x0)
bcma: bus0: Core 1 found: Chipcommon B (manuf 0x4BF, id 0x50B, rev 0x01, class 0x0)
bcma: bus0: Core 2 found: DMA (manuf 0x4BF, id 0x502, rev 0x01, class 0x0)
bcma: bus0: Core 3 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0)
bcma: bus0: Core 4 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0)
bcma: bus0: Core 5 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0)
bcma: bus0: Core 6 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0)
bcma: bus0: Core 7 found: PCIe Gen 2 (manuf 0x4BF, id 0x501, rev 0x01, class 0x0)
bcma: bus0: Core 8 found: PCIe Gen 2 (manuf 0x4BF, id 0x501, rev 0x01, class 0x0)
bcma: bus0: Core 9 found: ARM Cortex A9 core (ihost) (manuf 0x4BF, id 0x510, rev 0x01, class 0x0)
bcma: bus0: Core 10 found: USB 2.0 (manuf 0x4BF, id 0x504, rev 0x01, class 0x0)
bcma: bus0: Core 11 found: USB 3.0 (manuf 0x4BF, id 0x505, rev 0x01, class 0x0)
bcma: bus0: Core 12 found: SDIO3 (manuf 0x4BF, id 0x503, rev 0x01, class 0x0)
bcma: bus0: Core 13 found: ARM Cortex A9 JTAG (manuf 0x4BF, id 0x506, rev 0x01, class 0x0)
bcma: bus0: Core 14 found: Denali DDR2/DDR3 memory controller (manuf 0x4BF, id 0x507, rev 0x01, class 0x0)
bcma: bus0: Core 15 found: ROM (manuf 0x4BF, id 0x508, rev 0x01, class 0x0)
bcma: bus0: Core 16 found: NAND flash controller (manuf 0x4BF, id 0x509, rev 0x01, class 0x0)
bcma: bus0: Core 17 found: SPI flash controller (manuf 0x4BF, id 0x50A, rev 0x01, class 0x0)
bcma: bus0: Found sprom from device tree provider
bgmac bcma0:1: Found PHY addr: 30 (NOREGS)
bgmac bcma0:1: Support for Roboswitch not implemented
libphy: bgmac mii bus: probed
b53_common: found switch: BCM53011, rev 2
bgmac bcma0:2: Found PHY addr: 0
bgmac bcma0:2: Support for Roboswitch not implemented
libphy: bgmac mii bus: probed
Hauke Mehrtens (7):
MIPS: BCM47XX: move the nvram header file into common space
bcm47xx-nvram: add new broadcom nvram driver with dt support
bcm47xx-sprom: add Broadcom sprom parser driver
bcma: register bcma as device tree driver
bcma: get IRQ numbers from dt
bcma: get sprom from devicetree
ARM: BCM5301X: register bcma bus
Documentation/devicetree/bindings/bus/bcma.txt | 46 ++
.../devicetree/bindings/misc/bcm47xx-nvram.txt | 19 +
.../devicetree/bindings/misc/bcm47xx-sprom.txt | 16 +
arch/arm/boot/dts/bcm4708.dtsi | 58 ++
arch/mips/bcm47xx/board.c | 42 +-
arch/mips/bcm47xx/nvram.c | 9 +-
arch/mips/bcm47xx/setup.c | 6 +-
arch/mips/bcm47xx/sprom.c | 6 +-
arch/mips/bcm47xx/time.c | 4 +-
arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h | 53 --
drivers/bcma/host_soc.c | 70 +++
drivers/bcma/main.c | 42 +-
drivers/bcma/sprom.c | 51 +-
drivers/misc/Kconfig | 27 +
drivers/misc/Makefile | 2 +
drivers/misc/bcm47xx-nvram.c | 215 +++++++
drivers/misc/bcm47xx-sprom.c | 690 +++++++++++++++++++++
drivers/net/ethernet/broadcom/b44.c | 10 +-
drivers/net/ethernet/broadcom/bgmac.c | 7 +-
drivers/ssb/driver_chipcommon_pmu.c | 9 +-
include/linux/bcm47xx_nvram.h | 71 +++
include/linux/bcma/bcma.h | 2 +
22 files changed, 1350 insertions(+), 105 deletions(-)
create mode 100644 Documentation/devicetree/bindings/bus/bcma.txt
create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-nvram.txt
create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
delete mode 100644 arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h
create mode 100644 drivers/misc/bcm47xx-nvram.c
create mode 100644 drivers/misc/bcm47xx-sprom.c
create mode 100644 include/linux/bcm47xx_nvram.h
--
1.9.1
It is not possible to auto detect the irq numbers used by the cores on
an arm SoC. If bcma was registered with device tree it will search for
some device tree nodes with the irq number and add it to the core
configuration.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/main.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 0ff8d58..3f75776 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -10,6 +10,8 @@
#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");
MODULE_LICENSE("GPL");
@@ -120,6 +122,38 @@ static void bcma_release_core_dev(struct device *dev)
kfree(core);
}
+static struct device_node *bcma_of_find_child_device(struct platform_device *parent,
+ struct bcma_device *core)
+{
+ struct device_node *node;
+ u64 size;
+ const __be32 *reg;
+
+ if (!parent || !parent->dev.of_node)
+ return NULL;
+
+ for_each_child_of_node(parent->dev.of_node, node) {
+ reg = of_get_address(node, 0, &size, NULL);
+ if (!reg)
+ continue;
+ if (be32_to_cpup(reg) == core->addr)
+ return node;
+ }
+ return NULL;
+}
+
+static void bcma_of_fill_device(struct platform_device *parent,
+ struct bcma_device *core)
+{
+ struct device_node *node;
+
+ node = bcma_of_find_child_device(parent, core);
+ if (!node)
+ return;
+ core->dev.of_node = node;
+ core->irq = irq_of_parse_and_map(node, 0);
+}
+
static int bcma_register_cores(struct bcma_bus *bus)
{
struct bcma_device *core;
@@ -155,7 +189,13 @@ static int bcma_register_cores(struct bcma_bus *bus)
break;
case BCMA_HOSTTYPE_SOC:
core->dev.dma_mask = &core->dev.coherent_dma_mask;
- core->dma_dev = &core->dev;
+ if (bus->host_pdev) {
+ core->dma_dev = &bus->host_pdev->dev;
+ core->dev.parent = &bus->host_pdev->dev;
+ bcma_of_fill_device(bus->host_pdev, core);
+ } else {
+ core->dma_dev = &core->dev;
+ }
break;
case BCMA_HOSTTYPE_SDIO:
break;
--
1.9.1
On Sunday 24 August 2014 23:24:42 Hauke Mehrtens wrote:
> This driver is used by the bcm53xx ARM SoC code. Now it is possible to
> give the address of the chipcommon core in device tree and bcma will
> search for all the other cores.
>
> Signed-off-by: Hauke Mehrtens <[email protected]>
Looks good to me overall. Two small comments:
> Documentation/devicetree/bindings/bus/bcma.txt | 46 +++++++++++++++++
> drivers/bcma/host_soc.c | 70 ++++++++++++++++++++++++++
> include/linux/bcma/bcma.h | 2 +
> 3 files changed, 118 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/bus/bcma.txt
>
> diff --git a/Documentation/devicetree/bindings/bus/bcma.txt b/Documentation/devicetree/bindings/bus/bcma.txt
> new file mode 100644
> index 0000000..52fb929
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/bus/bcma.txt
> @@ -0,0 +1,46 @@
> +Broadcom AIX bcma bus driver
> +
> +
> +Required properties:
> +
> +- compatible : brcm,bus-aix
> +
> +- reg : iomem address range of chipcommon core
> +
> +Optional properties:
> +
> +- sprom: reference to a sprom driver. This is needed for sprom less devices.
> + Use bcm47xx_sprom for example.
> +
> +
> +The cores on the AIX bus are auto detected by bcma. Detection of the
> +IRQ number is not supported on BCM47xx/BCM53xx ARM SoCs, so it is
> +possible to provide the IRQ number over device tree. The IRQ number and
> +the device tree child entry will be added to the core with the matching
> +reg address.
What is the problem with the interrupt numbers? Is that information
missing completely from the data available to the brcm bus, or is it
in an inconvenient format?
> +Example:
> +
> + aix@18000000 {
> + compatible = "brcm,bus-aix";
> + reg = <0x18000000 0x1000>;
> + ranges = <0x00000000 0x18000000 0x00100000>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> + sprom = <&sprom0>;
> +
> + gmac@0 {
> + reg = <0x18024000 0x1000>;
> + interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
> + };
The @0 part seems wrong here: the address should generally match
the first entry in the reg property, which would be gmac@18024000.
Also, you probably mean ethernet@ not gmac@.
> + gmac@1 {
> + reg = <0x18025000 0x1000>;
> + interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + pcie@0 {
> + reg = <0x18012000 0x1000>;
> + interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
> + };
> + };
We may require additional properties for the pcie node, depending on whether
we want to use the DT probing interfaces for it, or whether it should just
hardcode the settings used on brcm based on the ID.
Arnd
---
arch/arm/boot/dts/bcm4708.dtsi | 58 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/arch/arm/boot/dts/bcm4708.dtsi b/arch/arm/boot/dts/bcm4708.dtsi
index 31141e8..7c240ab 100644
--- a/arch/arm/boot/dts/bcm4708.dtsi
+++ b/arch/arm/boot/dts/bcm4708.dtsi
@@ -31,4 +31,62 @@
};
};
+ nvram0: nvram@0 {
+ compatible = "brcm,bcm47xx-nvram";
+ reg = <0x1c000000 0x01000000>;
+ };
+
+ sprom0: sprom@0 {
+ compatible = "brcm,bcm47xx-sprom";
+ nvram = <&nvram0>;
+ };
+
+ aix@18000000 {
+ compatible = "brcm,bus-aix";
+ reg = <0x18000000 0x1000>;
+ ranges = <0x00000000 0x18000000 0x00100000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ sprom = <&sprom0>;
+
+ usb2@0 {
+ reg = <0x18021000 0x1000>;
+ interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ usb3@0 {
+ reg = <0x18023000 0x1000>;
+ interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gmac@0 {
+ reg = <0x18024000 0x1000>;
+ interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gmac@1 {
+ reg = <0x18025000 0x1000>;
+ interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gmac@2 {
+ reg = <0x18026000 0x1000>;
+ interrupts = <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gmac@3 {
+ reg = <0x18027000 0x1000>;
+ interrupts = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pcie@0 {
+ reg = <0x18012000 0x1000>;
+ interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pcie@1 {
+ reg = <0x18013000 0x1000>;
+ interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
};
--
1.9.1
On 08/25/2014 09:57 AM, Arnd Bergmann wrote:
> On Sunday 24 August 2014 23:24:42 Hauke Mehrtens wrote:
>> This driver is used by the bcm53xx ARM SoC code. Now it is possible to
>> give the address of the chipcommon core in device tree and bcma will
>> search for all the other cores.
>>
>> Signed-off-by: Hauke Mehrtens <[email protected]>
>
> Looks good to me overall. Two small comments:
>
>> Documentation/devicetree/bindings/bus/bcma.txt | 46 +++++++++++++++++
>> drivers/bcma/host_soc.c | 70 ++++++++++++++++++++++++++
>> include/linux/bcma/bcma.h | 2 +
>> 3 files changed, 118 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/bus/bcma.txt
>>
>> diff --git a/Documentation/devicetree/bindings/bus/bcma.txt b/Documentation/devicetree/bindings/bus/bcma.txt
>> new file mode 100644
>> index 0000000..52fb929
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/bus/bcma.txt
>> @@ -0,0 +1,46 @@
>> +Broadcom AIX bcma bus driver
>> +
>> +
>> +Required properties:
>> +
>> +- compatible : brcm,bus-aix
>> +
>> +- reg : iomem address range of chipcommon core
>> +
>> +Optional properties:
>> +
>> +- sprom: reference to a sprom driver. This is needed for sprom less devices.
>> + Use bcm47xx_sprom for example.
>> +
>> +
>> +The cores on the AIX bus are auto detected by bcma. Detection of the
>> +IRQ number is not supported on BCM47xx/BCM53xx ARM SoCs, so it is
>> +possible to provide the IRQ number over device tree. The IRQ number and
>> +the device tree child entry will be added to the core with the matching
>> +reg address.
>
> What is the problem with the interrupt numbers? Is that information
> missing completely from the data available to the brcm bus, or is it
> in an inconvenient format?
I do not have access to the datasheet, only to the vendor source code.
The irq numbers are hard coded in the vendor code, see:
https://github.com/RMerl/asuswrt-merlin/blob/master/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/arm/plat-brcm/bcm5301x_pcie.c#L286
On the mips SoCs it was possible to read them from some register in the
mips core on the aix bus.
>
>> +Example:
>> +
>> + aix@18000000 {
>> + compatible = "brcm,bus-aix";
>> + reg = <0x18000000 0x1000>;
>> + ranges = <0x00000000 0x18000000 0x00100000>;
>> + #address-cells = <1>;
>> + #size-cells = <1>;
>> + sprom = <&sprom0>;
>> +
>> + gmac@0 {
>> + reg = <0x18024000 0x1000>;
>> + interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
>> + };
>
> The @0 part seems wrong here: the address should generally match
> the first entry in the reg property, which would be gmac@18024000.
>
> Also, you probably mean ethernet@ not gmac@.
Will change that.
>> + gmac@1 {
>> + reg = <0x18025000 0x1000>;
>> + interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
>> + };
>> +
>> + pcie@0 {
>> + reg = <0x18012000 0x1000>;
>> + interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
>> + };
>> + };
>
> We may require additional properties for the pcie node, depending on whether
> we want to use the DT probing interfaces for it, or whether it should just
> hardcode the settings used on brcm based on the ID.
I wrote a driver for the PCIe host controller and it also automatically
detects all needed memory addresses, it just had to provide the IRQ
number through device tree.
>
> Arnd
>
On 08/25/2014 09:52 AM, Arnd Bergmann wrote:
> On Sunday 24 August 2014 23:24:41 Hauke Mehrtens wrote:
>> This driver needs an nvram driver and fetches the sprom values from the
>> nvram and provides it to any other driver. The calibration data for the
>> wifi chip the mac address and some more board description data is
>> stores in the sprom.
>>
>> This is based on a copy of arch/mips/bcm47xx/sprom.c and my plan is to
>> make the bcm47xx MIPS SoCs also use this driver some time later.
>
>> Signed-off-by: Hauke Mehrtens <[email protected]>
>> ---
>> .../devicetree/bindings/misc/bcm47xx-sprom.txt | 16 +
>
> I'd prefer not to list the binding in a 'misc' category. Maybe we can
> have a new category and move the misc/sram.txt into the same?
>
>> drivers/misc/Kconfig | 14 +
>> drivers/misc/Makefile | 1 +
>> drivers/misc/bcm47xx-sprom.c | 690 +++++++++++++++++++++
>
> On a similar note, putting the driver into drivers/misc seems
> suboptimal: misc drivers should by definition be something that
> is for some odd hardware with no external dependencies on it,
> whereas your driver seems to be used by multiple other drivers.
>
> Would it make sense to put it into drivers/bcma when that is the
> only bus it is used on?
As Jonas already said this code should be used for the bcm53xx ARM code
and the bcm47xx MIPS code and it is needed for drivers/bcma/ and
drivers/ssb/ (ssb only for old mips devices). Do you have any better
idea than putting this to drivers/misc/ ? For the mips SoC we need the
code very early and will not use the driver interface but probably
directly call the function name.
>
>> 4 files changed, 721 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
>> create mode 100644 drivers/misc/bcm47xx-sprom.c
>>
>> diff --git a/Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt b/Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
>> new file mode 100644
>> index 0000000..eed2a4a
>> --- /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
>
> Please use a specific SoC here as the compatible string, not something
> with 'x' in it as a wildcard.
I will change that.
>
>> +#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
>
> Hmm, multiline macros are ugly. How about using one function to read
> into an s64 internally using kstrtoll, and simple inline wrappers around
> that for the smaller types, doing the appropriate range check?
Yes that should work I will try it.
>> +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);
>> +}
>
> Rather than calling the same set of functions multiple times, can you
> do this using a table driven approach for smaller code size and
> improved readability?
I will try to change that.
>> +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));
>
> You can avoid the explicit be32_to_cpup here if you use
> of_property_read_u32 above.
Thanks for the hint.
Hauke
On Mon, Aug 25, 2014 at 9:52 AM, Arnd Bergmann <[email protected]> wrote:
> On Sunday 24 August 2014 23:24:41 Hauke Mehrtens wrote:
>> This driver needs an nvram driver and fetches the sprom values from the
>> nvram and provides it to any other driver. The calibration data for the
>> wifi chip the mac address and some more board description data is
>> stores in the sprom.
>>
>> This is based on a copy of arch/mips/bcm47xx/sprom.c and my plan is to
>> make the bcm47xx MIPS SoCs also use this driver some time later.
>
>> Signed-off-by: Hauke Mehrtens <[email protected]>
>> ---
>> .../devicetree/bindings/misc/bcm47xx-sprom.txt | 16 +
>
> I'd prefer not to list the binding in a 'misc' category. Maybe we can
> have a new category and move the misc/sram.txt into the same?
>
>> drivers/misc/Kconfig | 14 +
>> drivers/misc/Makefile | 1 +
>> drivers/misc/bcm47xx-sprom.c | 690 +++++++++++++++++++++
>
> On a similar note, putting the driver into drivers/misc seems
> suboptimal: misc drivers should by definition be something that
> is for some odd hardware with no external dependencies on it,
> whereas your driver seems to be used by multiple other drivers.
>
> Would it make sense to put it into drivers/bcma when that is the
> only bus it is used on?
If the driver will be used for bcm47xx/mips, it will be used for two
busses, bcma and ssb, so it will need to be at a common
location.
Jonas
This driver needs an nvram driver and fetches the sprom values from the
nvram and provides it to any other driver. The calibration data for the
wifi chip the mac address and some more board description data is
stores in the sprom.
This is based on a copy of arch/mips/bcm47xx/sprom.c and my plan is to
make the bcm47xx MIPS SoCs also use this driver some time later.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
.../devicetree/bindings/misc/bcm47xx-sprom.txt | 16 +
drivers/misc/Kconfig | 14 +
drivers/misc/Makefile | 1 +
drivers/misc/bcm47xx-sprom.c | 690 +++++++++++++++++++++
4 files changed, 721 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
create mode 100644 drivers/misc/bcm47xx-sprom.c
diff --git a/Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt b/Documentation/devicetree/bindings/misc/bcm47xx-sprom.txt
new file mode 100644
index 0000000..eed2a4a
--- /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>;
+};
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b306c02..fe9e2f2 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -528,6 +528,20 @@ config BCM47XX_NVRAM
If unsure, say N.
+config BCM47XX_SPROM
+ tristate "BCM47XX sprom driver"
+ depends on OF
+ 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.
+
+ If unsure, say N.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 2331208..e958e35 100644
--- 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_BCM47XX_NVRAM) += bcm47xx-nvram.o
+obj-$(CONFIG_BCM47XX_SPROM) += bcm47xx-sprom.o
diff --git a/drivers/misc/bcm47xx-sprom.c b/drivers/misc/bcm47xx-sprom.c
new file mode 100644
index 0000000..3221b12
--- /dev/null
+++ b/drivers/misc/bcm47xx-sprom.c
@@ -0,0 +1,690 @@
+/*
+ * BCM47xx/BCM53xx nvram variable access
+ *
+ * Copyright (C) 2005 Broadcom Corporation
+ * Copyright (C) 2004 Florian Schirmer <[email protected]>
+ * Copyright (C) 2006 Michael Buesch <[email protected]>
+ * Copyright (C) 2010 Waldemar Brodkorb <[email protected]>
+ * Copyright (C) 2006 Felix Fietkau <[email protected]>
+ * Copyright (C) 2010-2014 Hauke Mehrtens <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/of_address.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/ssb/ssb.h>
+#include <linux/bcm47xx_nvram.h>
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+
+struct bcm47xx_sprom_fill {
+ const char *prefix;
+ bool fallback;
+ int (*getenv)(const struct bcm47xx_sprom_fill *fill, const char *name,
+ char *val, size_t val_len);
+ const void *priv;
+};
+
+static void create_key(const char *prefix, const char *postfix,
+ const char *name, char *buf, int len)
+{
+ if (prefix && postfix)
+ snprintf(buf, len, "%s%s%s", prefix, name, postfix);
+ else if (prefix)
+ snprintf(buf, len, "%s%s", prefix, name);
+ else if (postfix)
+ snprintf(buf, len, "%s%s", name, postfix);
+ else
+ snprintf(buf, len, "%s", name);
+}
+
+static int get_nvram_var(const struct bcm47xx_sprom_fill *fill,
+ const char *postfix, const char *name, char *buf,
+ int len)
+{
+ char key[40];
+ int err;
+
+ create_key(fill->prefix, postfix, name, key, sizeof(key));
+
+ err = fill->getenv(fill, key, buf, len);
+ if (fill->fallback && err == -ENOENT && fill->prefix) {
+ create_key(NULL, postfix, name, key, sizeof(key));
+ err = fill->getenv(fill, key, buf, len);
+ }
+ return err;
+}
+
+#define NVRAM_READ_VAL(type) \
+static void nvram_read_ ## type (const struct bcm47xx_sprom_fill *fill, \
+ const char *postfix, const char *name, \
+ type *val, type allset) \
+{ \
+ char buf[100]; \
+ int err; \
+ type var; \
+ \
+ err = get_nvram_var(fill, postfix, name, buf, sizeof(buf)); \
+ if (err < 0) \
+ return; \
+ err = kstrto ## type(strim(buf), 0, &var); \
+ if (err) { \
+ pr_warn("can not parse nvram name %s%s%s with value %s got %i\n", \
+ fill->prefix, name, postfix, buf, err); \
+ return; \
+ } \
+ if (allset && var == allset) \
+ return; \
+ *val = var; \
+}
+
+NVRAM_READ_VAL(u8)
+NVRAM_READ_VAL(s8)
+NVRAM_READ_VAL(u16)
+NVRAM_READ_VAL(u32)
+
+#undef NVRAM_READ_VAL
+
+static void nvram_read_u32_2(const struct bcm47xx_sprom_fill *fill,
+ const char *name, u16 *val_lo, u16 *val_hi)
+{
+ char buf[100];
+ int err;
+ u32 val;
+
+ err = get_nvram_var(fill, NULL, name, buf, sizeof(buf));
+ if (err < 0)
+ return;
+ err = kstrtou32(strim(buf), 0, &val);
+ if (err) {
+ pr_warn("can not parse nvram name %s%s with value %s got %i\n",
+ fill->prefix, name, buf, err);
+ return;
+ }
+ *val_lo = (val & 0x0000FFFFU);
+ *val_hi = (val & 0xFFFF0000U) >> 16;
+}
+
+static void nvram_read_leddc(const struct bcm47xx_sprom_fill *fill,
+ const char *name, u8 *leddc_on_time,
+ u8 *leddc_off_time)
+{
+ char buf[100];
+ int err;
+ u32 val;
+
+ err = get_nvram_var(fill, NULL, name, buf, sizeof(buf));
+ if (err < 0)
+ return;
+ err = kstrtou32(strim(buf), 0, &val);
+ if (err) {
+ pr_warn("can not parse nvram name %s%s with value %s got %i\n",
+ fill->prefix, name, buf, err);
+ return;
+ }
+
+ if (val == 0xffff || val == 0xffffffff)
+ return;
+
+ *leddc_on_time = val & 0xff;
+ *leddc_off_time = (val >> 16) & 0xff;
+}
+
+static void nvram_read_macaddr(const struct bcm47xx_sprom_fill *fill,
+ const char *name, u8 val[6])
+{
+ char buf[100];
+ int err;
+
+ err = get_nvram_var(fill, NULL, name, buf, sizeof(buf));
+ if (err < 0)
+ return;
+
+ bcm47xx_nvram_parse_macaddr(buf, val);
+}
+
+static void nvram_read_alpha2(const struct bcm47xx_sprom_fill *fill,
+ const char *name, char val[2])
+{
+ char buf[10];
+ int err;
+
+ err = get_nvram_var(fill, NULL, name, buf, sizeof(buf));
+ if (err < 0)
+ return;
+ if (buf[0] == '0')
+ return;
+ if (strlen(buf) > 2) {
+ pr_warn("alpha2 is too long %s\n", buf);
+ return;
+ }
+ memcpy(val, buf, 2);
+}
+
+static void bcm47xx_sprom_fill_r1234589(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u16(fill, NULL, "devid", &sprom->dev_id, 0);
+ nvram_read_u8(fill, NULL, "ledbh0", &sprom->gpio0, 0xff);
+ nvram_read_u8(fill, NULL, "ledbh1", &sprom->gpio1, 0xff);
+ nvram_read_u8(fill, NULL, "ledbh2", &sprom->gpio2, 0xff);
+ nvram_read_u8(fill, NULL, "ledbh3", &sprom->gpio3, 0xff);
+ nvram_read_u8(fill, NULL, "aa2g", &sprom->ant_available_bg, 0);
+ nvram_read_u8(fill, NULL, "aa5g", &sprom->ant_available_a, 0);
+ nvram_read_s8(fill, NULL, "ag0", &sprom->antenna_gain.a0, 0);
+ nvram_read_s8(fill, NULL, "ag1", &sprom->antenna_gain.a1, 0);
+ nvram_read_alpha2(fill, "ccode", sprom->alpha2);
+}
+
+static void bcm47xx_sprom_fill_r12389(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u16(fill, NULL, "pa0b0", &sprom->pa0b0, 0);
+ nvram_read_u16(fill, NULL, "pa0b1", &sprom->pa0b1, 0);
+ nvram_read_u16(fill, NULL, "pa0b2", &sprom->pa0b2, 0);
+ nvram_read_u8(fill, NULL, "pa0itssit", &sprom->itssi_bg, 0);
+ nvram_read_u8(fill, NULL, "pa0maxpwr", &sprom->maxpwr_bg, 0);
+ nvram_read_u16(fill, NULL, "pa1b0", &sprom->pa1b0, 0);
+ nvram_read_u16(fill, NULL, "pa1b1", &sprom->pa1b1, 0);
+ nvram_read_u16(fill, NULL, "pa1b2", &sprom->pa1b2, 0);
+ nvram_read_u8(fill, NULL, "pa1itssit", &sprom->itssi_a, 0);
+ nvram_read_u8(fill, NULL, "pa1maxpwr", &sprom->maxpwr_a, 0);
+}
+
+static void bcm47xx_sprom_fill_r1(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u16(fill, NULL, "boardflags", &sprom->boardflags_lo, 0);
+ nvram_read_u8(fill, NULL, "cc", &sprom->country_code, 0);
+}
+
+static void bcm47xx_sprom_fill_r2389(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u8(fill, NULL, "opo", &sprom->opo, 0);
+ nvram_read_u16(fill, NULL, "pa1lob0", &sprom->pa1lob0, 0);
+ nvram_read_u16(fill, NULL, "pa1lob1", &sprom->pa1lob1, 0);
+ nvram_read_u16(fill, NULL, "pa1lob2", &sprom->pa1lob2, 0);
+ nvram_read_u16(fill, NULL, "pa1hib0", &sprom->pa1hib0, 0);
+ nvram_read_u16(fill, NULL, "pa1hib1", &sprom->pa1hib1, 0);
+ nvram_read_u16(fill, NULL, "pa1hib2", &sprom->pa1hib2, 0);
+ nvram_read_u8(fill, NULL, "pa1lomaxpwr", &sprom->maxpwr_al, 0);
+ nvram_read_u8(fill, NULL, "pa1himaxpwr", &sprom->maxpwr_ah, 0);
+}
+
+static void bcm47xx_sprom_fill_r389(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u8(fill, NULL, "bxa2g", &sprom->bxa2g, 0);
+ nvram_read_u8(fill, NULL, "rssisav2g", &sprom->rssisav2g, 0);
+ nvram_read_u8(fill, NULL, "rssismc2g", &sprom->rssismc2g, 0);
+ nvram_read_u8(fill, NULL, "rssismf2g", &sprom->rssismf2g, 0);
+ nvram_read_u8(fill, NULL, "bxa5g", &sprom->bxa5g, 0);
+ nvram_read_u8(fill, NULL, "rssisav5g", &sprom->rssisav5g, 0);
+ nvram_read_u8(fill, NULL, "rssismc5g", &sprom->rssismc5g, 0);
+ nvram_read_u8(fill, NULL, "rssismf5g", &sprom->rssismf5g, 0);
+ nvram_read_u8(fill, NULL, "tri2g", &sprom->tri2g, 0);
+ nvram_read_u8(fill, NULL, "tri5g", &sprom->tri5g, 0);
+ nvram_read_u8(fill, NULL, "tri5gl", &sprom->tri5gl, 0);
+ nvram_read_u8(fill, NULL, "tri5gh", &sprom->tri5gh, 0);
+ nvram_read_s8(fill, NULL, "rxpo2g", &sprom->rxpo2g, 0);
+ nvram_read_s8(fill, NULL, "rxpo5g", &sprom->rxpo5g, 0);
+}
+
+static void bcm47xx_sprom_fill_r3(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u8(fill, NULL, "regrev", &sprom->regrev, 0);
+ nvram_read_leddc(fill, "leddc", &sprom->leddc_on_time,
+ &sprom->leddc_off_time);
+}
+
+static void bcm47xx_sprom_fill_r4589(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u8(fill, NULL, "regrev", &sprom->regrev, 0);
+ nvram_read_s8(fill, NULL, "ag2", &sprom->antenna_gain.a2, 0);
+ nvram_read_s8(fill, NULL, "ag3", &sprom->antenna_gain.a3, 0);
+ nvram_read_u8(fill, NULL, "txchain", &sprom->txchain, 0xf);
+ nvram_read_u8(fill, NULL, "rxchain", &sprom->rxchain, 0xf);
+ nvram_read_u8(fill, NULL, "antswitch", &sprom->antswitch, 0xff);
+ nvram_read_leddc(fill, "leddc", &sprom->leddc_on_time,
+ &sprom->leddc_off_time);
+}
+
+static void bcm47xx_sprom_fill_r458(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u16(fill, NULL, "cck2gpo", &sprom->cck2gpo, 0);
+ nvram_read_u32(fill, NULL, "ofdm2gpo", &sprom->ofdm2gpo, 0);
+ nvram_read_u32(fill, NULL, "ofdm5gpo", &sprom->ofdm5gpo, 0);
+ nvram_read_u32(fill, NULL, "ofdm5glpo", &sprom->ofdm5glpo, 0);
+ nvram_read_u32(fill, NULL, "ofdm5ghpo", &sprom->ofdm5ghpo, 0);
+ nvram_read_u16(fill, NULL, "cddpo", &sprom->cddpo, 0);
+ nvram_read_u16(fill, NULL, "stbcpo", &sprom->stbcpo, 0);
+ nvram_read_u16(fill, NULL, "bw40po", &sprom->bw40po, 0);
+ nvram_read_u16(fill, NULL, "bwduppo", &sprom->bwduppo, 0);
+ nvram_read_u16(fill, NULL, "mcs2gpo0", &sprom->mcs2gpo[0], 0);
+ nvram_read_u16(fill, NULL, "mcs2gpo1", &sprom->mcs2gpo[1], 0);
+ nvram_read_u16(fill, NULL, "mcs2gpo2", &sprom->mcs2gpo[2], 0);
+ nvram_read_u16(fill, NULL, "mcs2gpo3", &sprom->mcs2gpo[3], 0);
+ nvram_read_u16(fill, NULL, "mcs2gpo4", &sprom->mcs2gpo[4], 0);
+ nvram_read_u16(fill, NULL, "mcs2gpo5", &sprom->mcs2gpo[5], 0);
+ nvram_read_u16(fill, NULL, "mcs2gpo6", &sprom->mcs2gpo[6], 0);
+ nvram_read_u16(fill, NULL, "mcs2gpo7", &sprom->mcs2gpo[7], 0);
+ nvram_read_u16(fill, NULL, "mcs5gpo0", &sprom->mcs5gpo[0], 0);
+ nvram_read_u16(fill, NULL, "mcs5gpo1", &sprom->mcs5gpo[1], 0);
+ nvram_read_u16(fill, NULL, "mcs5gpo2", &sprom->mcs5gpo[2], 0);
+ nvram_read_u16(fill, NULL, "mcs5gpo3", &sprom->mcs5gpo[3], 0);
+ nvram_read_u16(fill, NULL, "mcs5gpo4", &sprom->mcs5gpo[4], 0);
+ nvram_read_u16(fill, NULL, "mcs5gpo5", &sprom->mcs5gpo[5], 0);
+ nvram_read_u16(fill, NULL, "mcs5gpo6", &sprom->mcs5gpo[6], 0);
+ nvram_read_u16(fill, NULL, "mcs5gpo7", &sprom->mcs5gpo[7], 0);
+ nvram_read_u16(fill, NULL, "mcs5glpo0", &sprom->mcs5glpo[0], 0);
+ nvram_read_u16(fill, NULL, "mcs5glpo1", &sprom->mcs5glpo[1], 0);
+ nvram_read_u16(fill, NULL, "mcs5glpo2", &sprom->mcs5glpo[2], 0);
+ nvram_read_u16(fill, NULL, "mcs5glpo3", &sprom->mcs5glpo[3], 0);
+ nvram_read_u16(fill, NULL, "mcs5glpo4", &sprom->mcs5glpo[4], 0);
+ nvram_read_u16(fill, NULL, "mcs5glpo5", &sprom->mcs5glpo[5], 0);
+ nvram_read_u16(fill, NULL, "mcs5glpo6", &sprom->mcs5glpo[6], 0);
+ nvram_read_u16(fill, NULL, "mcs5glpo7", &sprom->mcs5glpo[7], 0);
+ nvram_read_u16(fill, NULL, "mcs5ghpo0", &sprom->mcs5ghpo[0], 0);
+ nvram_read_u16(fill, NULL, "mcs5ghpo1", &sprom->mcs5ghpo[1], 0);
+ nvram_read_u16(fill, NULL, "mcs5ghpo2", &sprom->mcs5ghpo[2], 0);
+ nvram_read_u16(fill, NULL, "mcs5ghpo3", &sprom->mcs5ghpo[3], 0);
+ nvram_read_u16(fill, NULL, "mcs5ghpo4", &sprom->mcs5ghpo[4], 0);
+ nvram_read_u16(fill, NULL, "mcs5ghpo5", &sprom->mcs5ghpo[5], 0);
+ nvram_read_u16(fill, NULL, "mcs5ghpo6", &sprom->mcs5ghpo[6], 0);
+ nvram_read_u16(fill, NULL, "mcs5ghpo7", &sprom->mcs5ghpo[7], 0);
+}
+
+static void bcm47xx_sprom_fill_r45(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u8(fill, NULL, "txpid2ga0", &sprom->txpid2g[0], 0);
+ nvram_read_u8(fill, NULL, "txpid2ga1", &sprom->txpid2g[1], 0);
+ nvram_read_u8(fill, NULL, "txpid2ga2", &sprom->txpid2g[2], 0);
+ nvram_read_u8(fill, NULL, "txpid2ga3", &sprom->txpid2g[3], 0);
+ nvram_read_u8(fill, NULL, "txpid5ga0", &sprom->txpid5g[0], 0);
+ nvram_read_u8(fill, NULL, "txpid5ga1", &sprom->txpid5g[1], 0);
+ nvram_read_u8(fill, NULL, "txpid5ga2", &sprom->txpid5g[2], 0);
+ nvram_read_u8(fill, NULL, "txpid5ga3", &sprom->txpid5g[3], 0);
+ nvram_read_u8(fill, NULL, "txpid5gla0", &sprom->txpid5gl[0], 0);
+ nvram_read_u8(fill, NULL, "txpid5gla1", &sprom->txpid5gl[1], 0);
+ nvram_read_u8(fill, NULL, "txpid5gla2", &sprom->txpid5gl[2], 0);
+ nvram_read_u8(fill, NULL, "txpid5gla3", &sprom->txpid5gl[3], 0);
+ nvram_read_u8(fill, NULL, "txpid5gha0", &sprom->txpid5gh[0], 0);
+ nvram_read_u8(fill, NULL, "txpid5gha1", &sprom->txpid5gh[1], 0);
+ nvram_read_u8(fill, NULL, "txpid5gha2", &sprom->txpid5gh[2], 0);
+ nvram_read_u8(fill, NULL, "txpid5gha3", &sprom->txpid5gh[3], 0);
+}
+
+static void bcm47xx_sprom_fill_r89(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u8(fill, NULL, "tssipos2g", &sprom->fem.ghz2.tssipos, 0);
+ nvram_read_u8(fill, NULL, "extpagain2g", &sprom->fem.ghz2.extpa_gain, 0);
+ nvram_read_u8(fill, NULL, "pdetrange2g", &sprom->fem.ghz2.pdet_range, 0);
+ nvram_read_u8(fill, NULL, "triso2g", &sprom->fem.ghz2.tr_iso, 0);
+ nvram_read_u8(fill, NULL, "antswctl2g", &sprom->fem.ghz2.antswlut, 0);
+ nvram_read_u8(fill, NULL, "tssipos5g", &sprom->fem.ghz5.tssipos, 0);
+ nvram_read_u8(fill, NULL, "extpagain5g", &sprom->fem.ghz5.extpa_gain, 0);
+ nvram_read_u8(fill, NULL, "pdetrange5g", &sprom->fem.ghz5.pdet_range, 0);
+ nvram_read_u8(fill, NULL, "triso5g", &sprom->fem.ghz5.tr_iso, 0);
+ nvram_read_u8(fill, NULL, "antswctl5g", &sprom->fem.ghz5.antswlut, 0);
+ nvram_read_u8(fill, NULL, "tempthresh", &sprom->tempthresh, 0);
+ nvram_read_u8(fill, NULL, "tempoffset", &sprom->tempoffset, 0);
+ nvram_read_u16(fill, NULL, "rawtempsense", &sprom->rawtempsense, 0);
+ nvram_read_u8(fill, NULL, "measpower", &sprom->measpower, 0);
+ nvram_read_u8(fill, NULL, "tempsense_slope", &sprom->tempsense_slope, 0);
+ nvram_read_u8(fill, NULL, "tempcorrx", &sprom->tempcorrx, 0);
+ nvram_read_u8(fill, NULL, "tempsense_option", &sprom->tempsense_option, 0);
+ nvram_read_u8(fill, NULL, "freqoffset_corr", &sprom->freqoffset_corr, 0);
+ nvram_read_u8(fill, NULL, "iqcal_swp_dis", &sprom->iqcal_swp_dis, 0);
+ nvram_read_u8(fill, NULL, "hw_iqcal_en", &sprom->hw_iqcal_en, 0);
+ nvram_read_u8(fill, NULL, "elna2g", &sprom->elna2g, 0);
+ nvram_read_u8(fill, NULL, "elna5g", &sprom->elna5g, 0);
+ nvram_read_u8(fill, NULL, "phycal_tempdelta", &sprom->phycal_tempdelta, 0);
+ nvram_read_u8(fill, NULL, "temps_period", &sprom->temps_period, 0);
+ nvram_read_u8(fill, NULL, "temps_hysteresis", &sprom->temps_hysteresis, 0);
+ nvram_read_u8(fill, NULL, "measpower1", &sprom->measpower1, 0);
+ nvram_read_u8(fill, NULL, "measpower2", &sprom->measpower2, 0);
+ nvram_read_u8(fill, NULL, "rxgainerr2ga0", &sprom->rxgainerr2ga[0], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr2ga1", &sprom->rxgainerr2ga[1], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr2ga2", &sprom->rxgainerr2ga[2], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gla0", &sprom->rxgainerr5gla[0], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gla1", &sprom->rxgainerr5gla[1], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gla2", &sprom->rxgainerr5gla[2], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gma0", &sprom->rxgainerr5gma[0], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gma1", &sprom->rxgainerr5gma[1], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gma2", &sprom->rxgainerr5gma[2], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gha0", &sprom->rxgainerr5gha[0], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gha1", &sprom->rxgainerr5gha[1], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gha2", &sprom->rxgainerr5gha[2], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gua0", &sprom->rxgainerr5gua[0], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gua1", &sprom->rxgainerr5gua[1], 0);
+ nvram_read_u8(fill, NULL, "rxgainerr5gua2", &sprom->rxgainerr5gua[2], 0);
+ nvram_read_u8(fill, NULL, "noiselvl2ga0", &sprom->noiselvl2ga[0], 0);
+ nvram_read_u8(fill, NULL, "noiselvl2ga1", &sprom->noiselvl2ga[1], 0);
+ nvram_read_u8(fill, NULL, "noiselvl2ga2", &sprom->noiselvl2ga[2], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gla0", &sprom->noiselvl5gla[0], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gla1", &sprom->noiselvl5gla[1], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gla2", &sprom->noiselvl5gla[2], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gma0", &sprom->noiselvl5gma[0], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gma1", &sprom->noiselvl5gma[1], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gma2", &sprom->noiselvl5gma[2], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gha0", &sprom->noiselvl5gha[0], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gha1", &sprom->noiselvl5gha[1], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gha2", &sprom->noiselvl5gha[2], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gua0", &sprom->noiselvl5gua[0], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gua1", &sprom->noiselvl5gua[1], 0);
+ nvram_read_u8(fill, NULL, "noiselvl5gua2", &sprom->noiselvl5gua[2], 0);
+ nvram_read_u8(fill, NULL, "pcieingress_war", &sprom->pcieingress_war, 0);
+}
+
+static void bcm47xx_sprom_fill_r9(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u16(fill, NULL, "cckbw202gpo", &sprom->cckbw202gpo, 0);
+ nvram_read_u16(fill, NULL, "cckbw20ul2gpo", &sprom->cckbw20ul2gpo, 0);
+ nvram_read_u32(fill, NULL, "legofdmbw202gpo", &sprom->legofdmbw202gpo, 0);
+ nvram_read_u32(fill, NULL, "legofdmbw20ul2gpo", &sprom->legofdmbw20ul2gpo, 0);
+ nvram_read_u32(fill, NULL, "legofdmbw205glpo", &sprom->legofdmbw205glpo, 0);
+ nvram_read_u32(fill, NULL, "legofdmbw20ul5glpo", &sprom->legofdmbw20ul5glpo, 0);
+ nvram_read_u32(fill, NULL, "legofdmbw205gmpo", &sprom->legofdmbw205gmpo, 0);
+ nvram_read_u32(fill, NULL, "legofdmbw20ul5gmpo", &sprom->legofdmbw20ul5gmpo, 0);
+ nvram_read_u32(fill, NULL, "legofdmbw205ghpo", &sprom->legofdmbw205ghpo, 0);
+ nvram_read_u32(fill, NULL, "legofdmbw20ul5ghpo", &sprom->legofdmbw20ul5ghpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw202gpo", &sprom->mcsbw202gpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw20ul2gpo", &sprom->mcsbw20ul2gpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw402gpo", &sprom->mcsbw402gpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw205glpo", &sprom->mcsbw205glpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw20ul5glpo", &sprom->mcsbw20ul5glpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw405glpo", &sprom->mcsbw405glpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw205gmpo", &sprom->mcsbw205gmpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw20ul5gmpo", &sprom->mcsbw20ul5gmpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw405gmpo", &sprom->mcsbw405gmpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw205ghpo", &sprom->mcsbw205ghpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw20ul5ghpo", &sprom->mcsbw20ul5ghpo, 0);
+ nvram_read_u32(fill, NULL, "mcsbw405ghpo", &sprom->mcsbw405ghpo, 0);
+ nvram_read_u16(fill, NULL, "mcs32po", &sprom->mcs32po, 0);
+ nvram_read_u16(fill, NULL, "legofdm40duppo", &sprom->legofdm40duppo, 0);
+ nvram_read_u8(fill, NULL, "sar2g", &sprom->sar2g, 0);
+ nvram_read_u8(fill, NULL, "sar5g", &sprom->sar5g, 0);
+}
+
+static void bcm47xx_sprom_fill_path_r4589(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ char postfix[2];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) {
+ struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i];
+
+ snprintf(postfix, sizeof(postfix), "%i", i);
+ nvram_read_u8(fill, postfix, "maxp2ga", &pwr_info->maxpwr_2g, 0);
+ nvram_read_u8(fill, postfix, "itt2ga", &pwr_info->itssi_2g, 0);
+ nvram_read_u8(fill, postfix, "itt5ga", &pwr_info->itssi_5g, 0);
+ nvram_read_u16(fill, postfix, "pa2gw0a", &pwr_info->pa_2g[0], 0);
+ nvram_read_u16(fill, postfix, "pa2gw1a", &pwr_info->pa_2g[1], 0);
+ nvram_read_u16(fill, postfix, "pa2gw2a", &pwr_info->pa_2g[2], 0);
+ nvram_read_u8(fill, postfix, "maxp5ga", &pwr_info->maxpwr_5g, 0);
+ nvram_read_u8(fill, postfix, "maxp5gha", &pwr_info->maxpwr_5gh, 0);
+ nvram_read_u8(fill, postfix, "maxp5gla", &pwr_info->maxpwr_5gl, 0);
+ nvram_read_u16(fill, postfix, "pa5gw0a", &pwr_info->pa_5g[0], 0);
+ nvram_read_u16(fill, postfix, "pa5gw1a", &pwr_info->pa_5g[1], 0);
+ nvram_read_u16(fill, postfix, "pa5gw2a", &pwr_info->pa_5g[2], 0);
+ nvram_read_u16(fill, postfix, "pa5glw0a", &pwr_info->pa_5gl[0], 0);
+ nvram_read_u16(fill, postfix, "pa5glw1a", &pwr_info->pa_5gl[1], 0);
+ nvram_read_u16(fill, postfix, "pa5glw2a", &pwr_info->pa_5gl[2], 0);
+ nvram_read_u16(fill, postfix, "pa5ghw0a", &pwr_info->pa_5gh[0], 0);
+ nvram_read_u16(fill, postfix, "pa5ghw1a", &pwr_info->pa_5gh[1], 0);
+ nvram_read_u16(fill, postfix, "pa5ghw2a", &pwr_info->pa_5gh[2], 0);
+ }
+}
+
+static void bcm47xx_sprom_fill_path_r45(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ char postfix[2];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) {
+ struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i];
+
+ snprintf(postfix, sizeof(postfix), "%i", i);
+ nvram_read_u16(fill, postfix, "pa2gw3a", &pwr_info->pa_2g[3], 0);
+ nvram_read_u16(fill, postfix, "pa5gw3a", &pwr_info->pa_5g[3], 0);
+ nvram_read_u16(fill, postfix, "pa5glw3a", &pwr_info->pa_5gl[3], 0);
+ nvram_read_u16(fill, postfix, "pa5ghw3a", &pwr_info->pa_5gh[3], 0);
+ }
+}
+
+static bool bcm47xx_is_valid_mac(u8 *mac)
+{
+ return mac && !(mac[0] == 0x00 && mac[1] == 0x90 && mac[2] == 0x4c);
+}
+
+static int bcm47xx_increase_mac_addr(u8 *mac, u8 num)
+{
+ u8 *oui = mac + ETH_ALEN/2 - 1;
+ u8 *p = mac + ETH_ALEN - 1;
+
+ do {
+ (*p) += num;
+ if (*p > num)
+ break;
+ p--;
+ num = 1;
+ } while (p != oui);
+
+ if (p == oui) {
+ pr_err("unable to fetch mac address\n");
+ return -ENOENT;
+ }
+ return 0;
+}
+
+/*
+ * This is a global counter because different instances of sprom will
+ * access the same nvram.
+ */
+static int mac_addr_used = 2;
+
+static void bcm47xx_sprom_fill_ethernet(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_macaddr(fill, "et0macaddr", sprom->et0mac);
+ nvram_read_u8(fill, NULL, "et0mdcport", &sprom->et0mdcport, 0);
+ nvram_read_u8(fill, NULL, "et0phyaddr", &sprom->et0phyaddr, 0);
+
+ nvram_read_macaddr(fill, "et1macaddr", sprom->et1mac);
+ nvram_read_u8(fill, NULL, "et1mdcport", &sprom->et1mdcport, 0);
+ nvram_read_u8(fill, NULL, "et1phyaddr", &sprom->et1phyaddr, 0);
+
+ nvram_read_macaddr(fill, "macaddr", sprom->il0mac);
+ nvram_read_macaddr(fill, "il0macaddr", sprom->il0mac);
+
+ /*
+ * The address prefix 00:90:4C is used by Broadcom in their initial
+ * configuration. When a mac address with the prefix 00:90:4C is used
+ * all devices from the same series are sharing the same mac address.
+ * To prevent mac address collisions we replace them with a mac address
+ * based on the base address.
+ */
+ if (!bcm47xx_is_valid_mac(sprom->il0mac)) {
+ u8 mac[6];
+ struct bcm47xx_sprom_fill fill_no_prefix;
+
+ memcpy(&fill_no_prefix, fill, sizeof(fill_no_prefix));
+ fill_no_prefix.prefix = NULL;
+
+ nvram_read_macaddr(&fill_no_prefix, "et0macaddr", mac);
+ if (bcm47xx_is_valid_mac(mac)) {
+ int err = bcm47xx_increase_mac_addr(mac, mac_addr_used);
+
+ if (!err) {
+ ether_addr_copy(sprom->il0mac, mac);
+ mac_addr_used++;
+ }
+ }
+ }
+}
+
+static void bcm47xx_sprom_fill_board_data(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ nvram_read_u16(fill, NULL, "boardrev", &sprom->board_rev, 0);
+ nvram_read_u16(fill, NULL, "boardnum", &sprom->board_num, 0);
+ nvram_read_u16(fill, NULL, "boardtype", &sprom->board_type, 0);
+ nvram_read_u32_2(fill, "boardflags", &sprom->boardflags_lo,
+ &sprom->boardflags_hi);
+ nvram_read_u32_2(fill, "boardflags2", &sprom->boardflags2_lo,
+ &sprom->boardflags2_hi);
+}
+
+static void bcm47xx_sprom_fill(struct ssb_sprom *sprom,
+ const struct bcm47xx_sprom_fill *fill)
+{
+ bcm47xx_sprom_fill_ethernet(sprom, fill);
+ bcm47xx_sprom_fill_board_data(sprom, fill);
+
+ nvram_read_u8(fill, NULL, "sromrev", &sprom->revision, 0);
+
+ switch (sprom->revision) {
+ case 1:
+ bcm47xx_sprom_fill_r1234589(sprom, fill);
+ bcm47xx_sprom_fill_r12389(sprom, fill);
+ bcm47xx_sprom_fill_r1(sprom, fill);
+ break;
+ case 2:
+ bcm47xx_sprom_fill_r1234589(sprom, fill);
+ bcm47xx_sprom_fill_r12389(sprom, fill);
+ bcm47xx_sprom_fill_r2389(sprom, fill);
+ break;
+ case 3:
+ bcm47xx_sprom_fill_r1234589(sprom, fill);
+ bcm47xx_sprom_fill_r12389(sprom, fill);
+ bcm47xx_sprom_fill_r2389(sprom, fill);
+ bcm47xx_sprom_fill_r389(sprom, fill);
+ bcm47xx_sprom_fill_r3(sprom, fill);
+ break;
+ case 4:
+ case 5:
+ bcm47xx_sprom_fill_r1234589(sprom, fill);
+ bcm47xx_sprom_fill_r4589(sprom, fill);
+ bcm47xx_sprom_fill_r458(sprom, fill);
+ bcm47xx_sprom_fill_r45(sprom, fill);
+ bcm47xx_sprom_fill_path_r4589(sprom, fill);
+ bcm47xx_sprom_fill_path_r45(sprom, fill);
+ break;
+ case 8:
+ bcm47xx_sprom_fill_r1234589(sprom, fill);
+ bcm47xx_sprom_fill_r12389(sprom, fill);
+ bcm47xx_sprom_fill_r2389(sprom, fill);
+ bcm47xx_sprom_fill_r389(sprom, fill);
+ bcm47xx_sprom_fill_r4589(sprom, fill);
+ bcm47xx_sprom_fill_r458(sprom, fill);
+ bcm47xx_sprom_fill_r89(sprom, fill);
+ bcm47xx_sprom_fill_path_r4589(sprom, fill);
+ break;
+ case 9:
+ bcm47xx_sprom_fill_r1234589(sprom, fill);
+ bcm47xx_sprom_fill_r12389(sprom, fill);
+ bcm47xx_sprom_fill_r2389(sprom, fill);
+ bcm47xx_sprom_fill_r389(sprom, fill);
+ bcm47xx_sprom_fill_r4589(sprom, fill);
+ bcm47xx_sprom_fill_r89(sprom, fill);
+ bcm47xx_sprom_fill_r9(sprom, fill);
+ bcm47xx_sprom_fill_path_r4589(sprom, fill);
+ break;
+ default:
+ pr_warn("Unsupported SPROM revision %d detected. Will extract v1\n",
+ sprom->revision);
+ sprom->revision = 1;
+ bcm47xx_sprom_fill_r1234589(sprom, fill);
+ bcm47xx_sprom_fill_r12389(sprom, fill);
+ bcm47xx_sprom_fill_r1(sprom, fill);
+ }
+}
+
+static int bcm47xx_sprom_getenv(const struct bcm47xx_sprom_fill *fill,
+ const char *name, char *val, size_t val_len)
+{
+ const struct platform_device *nvram_dev = fill->priv;
+
+ return bcm47xx_nvram_getenv(&nvram_dev->dev, name, val, val_len);
+};
+
+static int bcm47xx_sprom_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct ssb_sprom *sprom;
+ const __be32 *handle;
+ struct device_node *nvram_node;
+ struct platform_device *nvram_dev;
+ struct bcm47xx_sprom_fill fill;
+
+ /* Alloc */
+ sprom = devm_kzalloc(dev, sizeof(*sprom), GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+
+ handle = of_get_property(np, "nvram", NULL);
+ if (!handle)
+ return -ENOMEM;
+
+ nvram_node = of_find_node_by_phandle(be32_to_cpup(handle));
+ if (!nvram_node)
+ return -ENOMEM;
+
+ nvram_dev = of_find_device_by_node(nvram_node);
+ if (!nvram_dev)
+ return -ENOMEM;
+
+ fill.prefix = of_get_property(np, "prefix", NULL);
+
+ fill.fallback = false;
+ fill.getenv = bcm47xx_sprom_getenv;
+ fill.priv = nvram_dev;
+
+ bcm47xx_sprom_fill(sprom, &fill);
+
+ platform_set_drvdata(pdev, sprom);
+
+ return 0;
+}
+
+static const struct of_device_id bcm47xx_sprom_of_match_table[] = {
+ { .compatible = "brcm,bcm47xx-sprom", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table);
+
+static struct platform_driver bcm47xx_sprom_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bcm47xx-sprom",
+ .of_match_table = bcm47xx_sprom_of_match_table,
+ /* driver unloading/unbinding currently not supported */
+ .suppress_bind_attrs = true,
+ },
+ .probe = bcm47xx_sprom_probe,
+};
+module_platform_driver(bcm47xx_sprom_driver);
+
+MODULE_AUTHOR("Hauke Mehrtens <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
1.9.1
On Sunday 24 August 2014 23:24:43 Hauke Mehrtens wrote:
> It is not possible to auto detect the irq numbers used by the cores on
> an arm SoC. If bcma was registered with device tree it will search for
> some device tree nodes with the irq number and add it to the core
> configuration.
>
> Signed-off-by: Hauke Mehrtens <[email protected]>
>
Acked-by: Arnd Bergmann <[email protected]>
On 27 August 2014 20:20, Florian Fainelli <[email protected]> wrote:
> On 08/26/2014 10:54 PM, Rafał Miłecki wrote:
>> Could we avoid that? Type of flash can easily be checked in the code.
>> All we need to do is to read BCMA_IOST register of BCMA_CORE_NS_ROM
>> core.
>
> So there is a boot status register you can read to tell what type of
> flash you booted from, but does that also give you the resource ranges
> for these type of flashes? Presumably they will be mapped into different
> addresses (at least bcm63xx is like that), that information needs to be
> listed somewhere.
Take a look at find_nvram in nvram_rw.c. It scans the whole region
which is up to 0x02000000 (SI_FLASH2_SZ) size.
In case of NAND limit is slightly different (nfl_boot_size function):
1) On ARM it's 0x800000 or 0x2600000
2) On MIPS it's 0x200000
--
Rafał
This adds a new driver which searches at a given memory range for a
nvram like it is used on the bcm47xx and bcm53xx SoCs with ARM and MIPS
CPUs. This driver provides acces to this nvram to other device in the
device tree. You have to specify the memory ranges where the content of
the flash chip is memory mapped and this driver will search there for
some nvram and parse it. Other drivers can use this driver to access the
device nvram. The nvram is used to store board configurations like the
mac addresses, the switch configuration and the calibration data for
the wifi devices.
This was copied from arch/mips/bcm47xx/nvram.c and modified to interact
with device tree. My plan is to make the MIPS bcm47xx also use this new
driver some time later.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
.../devicetree/bindings/misc/bcm47xx-nvram.txt | 19 ++
arch/mips/bcm47xx/board.c | 40 ++--
arch/mips/bcm47xx/nvram.c | 7 +-
arch/mips/bcm47xx/setup.c | 4 +-
arch/mips/bcm47xx/sprom.c | 4 +-
arch/mips/bcm47xx/time.c | 2 +-
drivers/misc/Kconfig | 13 ++
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, 294 insertions(+), 38 deletions(-)
create mode 100644 Documentation/devicetree/bindings/misc/bcm47xx-nvram.txt
create mode 100644 drivers/misc/bcm47xx-nvram.c
diff --git a/Documentation/devicetree/bindings/misc/bcm47xx-nvram.txt b/Documentation/devicetree/bindings/misc/bcm47xx-nvram.txt
new file mode 100644
index 0000000..ed979a8
--- /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>;
+};
diff --git a/arch/mips/bcm47xx/board.c b/arch/mips/bcm47xx/board.c
index ec46fd7..2c98f74 100644
--- a/arch/mips/bcm47xx/board.c
+++ b/arch/mips/bcm47xx/board.c
@@ -218,36 +218,36 @@ static __init const struct bcm47xx_board_type *bcm47xx_board_get_nvram(void)
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_type *bcm47xx_board_get_nvram(void)
}
}
- 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_type *bcm47xx_board_get_nvram(void)
}
}
- 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_type *bcm47xx_board_get_nvram(void)
}
}
- 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_type *bcm47xx_board_get_nvram(void)
}
}
- 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) {
diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c
index c47a4a8..b2112c7 100644
--- 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, char *val, size_t val_len)
}
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 *name)
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))
diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c
index deadb71..d87709d 100644
--- a/arch/mips/bcm47xx/setup.c
+++ b/arch/mips/bcm47xx/setup.c
@@ -123,7 +123,7 @@ static int bcm47xx_get_invariants(struct ssb_bus *bus,
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;
@@ -146,7 +146,7 @@ static void __init bcm47xx_register_ssb(void)
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;
diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c
index 29ced12..f88f49b 100644
--- a/arch/mips/bcm47xx/sprom.c
+++ b/arch/mips/bcm47xx/sprom.c
@@ -52,10 +52,10 @@ static int get_nvram_var(const char *prefix, const char *postfix,
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;
}
diff --git a/arch/mips/bcm47xx/time.c b/arch/mips/bcm47xx/time.c
index c57a515..b2c3c7d 100644
--- 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;
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b841180..b306c02 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -515,6 +515,19 @@ 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"
+ depends on OF
+ help
+ This driver parses the nvram from a given memory range and
+ provides its values to other drivers.
+
+ The nvram is a key value store used on bcm47xx/bcm53xx SoC
+ with ARM and MIPS CPUs. It contains the board specific
+ configuration.
+
+ If unsure, say N.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 5497d02..2331208 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,3 +55,4 @@ obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
+obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx-nvram.o
diff --git a/drivers/misc/bcm47xx-nvram.c b/drivers/misc/bcm47xx-nvram.c
new file mode 100644
index 0000000..6ea715c
--- /dev/null
+++ b/drivers/misc/bcm47xx-nvram.c
@@ -0,0 +1,215 @@
+/*
+ * BCM947xx nvram variable access
+ *
+ * Copyright (C) 2005 Broadcom Corporation
+ * Copyright (C) 2006 Felix Fietkau <[email protected]>
+ * Copyright (C) 2010-2014 Hauke Mehrtens <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/of_address.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/bcm47xx_nvram.h>
+
+struct bcm47xx_nvram {
+ size_t nvram_len;
+ char *nvram_buf;
+};
+
+static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000};
+
+static u32 find_nvram_size(void __iomem *end)
+{
+ struct nvram_header __iomem *header;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
+ header = (struct nvram_header __iomem *)(end - nvram_sizes[i]);
+ if (__raw_readl(&header->magic) == NVRAM_HEADER)
+ return nvram_sizes[i];
+ }
+
+ return 0;
+}
+
+/* Probe for NVRAM header */
+static int nvram_find_and_copy(struct device *dev, void __iomem *base,
+ size_t len, char **nvram_buf,
+ size_t *nvram_len)
+{
+ struct nvram_header __iomem *header;
+ int i;
+ u32 off;
+ u32 *dst;
+ __le32 __iomem *src;
+ u32 size;
+
+ /* TODO: when nvram is on nand flash check for bad blocks first. */
+ off = FLASH_MIN;
+ while (off <= len) {
+ /* Windowed flash access */
+ size = find_nvram_size(base + off);
+ if (size) {
+ header = (struct nvram_header __iomem *)
+ (base + off - size);
+ goto found;
+ }
+ off += 0x10000;
+ }
+
+ /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
+ header = (struct nvram_header __iomem *)(base + 4096);
+ if (__raw_readl(&header->magic) == NVRAM_HEADER) {
+ size = NVRAM_SPACE;
+ goto found;
+ }
+
+ header = (struct nvram_header __iomem *)(base + 1024);
+ if (__raw_readl(&header->magic) == NVRAM_HEADER) {
+ size = NVRAM_SPACE;
+ goto found;
+ }
+
+ *nvram_buf = NULL;
+ *nvram_len = 0;
+ pr_err("no nvram found\n");
+ return -ENXIO;
+
+found:
+ if (readl(&header->len) > size)
+ pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n");
+ *nvram_len = min_t(u32, readl(&header->len), size);
+
+ *nvram_buf = devm_kzalloc(dev, *nvram_len, GFP_KERNEL);
+ if (!*nvram_buf)
+ return -ENOMEM;
+
+ src = (__le32 __iomem *) header;
+ dst = (u32 *) *nvram_buf;
+ for (i = 0; i < sizeof(struct nvram_header); i += 4)
+ *dst++ = __raw_readl(src++);
+ for (; i < *nvram_len; i += 4)
+ *dst++ = readl(src++);
+
+ return 0;
+}
+
+int bcm47xx_nvram_getenv(const struct device *dev, const char *name, char *val,
+ size_t val_len)
+{
+ char *var, *value, *end, *eq;
+ struct bcm47xx_nvram *nvram;
+
+ if (!dev)
+ return -ENODEV;
+
+ nvram = dev_get_drvdata(dev);
+
+ if (!name || !nvram || !nvram->nvram_len)
+ return -EINVAL;
+
+ /* Look for name=value and return value */
+ var = nvram->nvram_buf + sizeof(struct nvram_header);
+ end = nvram->nvram_buf + nvram->nvram_len - 2;
+ end[0] = end[1] = '\0';
+ for (; *var; var = value + strlen(value) + 1) {
+ eq = strchr(var, '=');
+ if (!eq)
+ break;
+ value = eq + 1;
+ if ((eq - var) == strlen(name) &&
+ strncmp(var, name, (eq - var)) == 0) {
+ return snprintf(val, val_len, "%s", value);
+ }
+ }
+ return -ENOENT;
+}
+EXPORT_SYMBOL(bcm47xx_nvram_getenv);
+
+int bcm47xx_nvram_gpio_pin(const struct device *dev, const char *name)
+{
+ int i, err;
+ char nvram_var[10];
+ char buf[30];
+
+ if (!dev)
+ return -ENODEV;
+
+ for (i = 0; i < 32; i++) {
+ err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i);
+ if (err <= 0)
+ continue;
+ err = bcm47xx_nvram_getenv(dev, nvram_var, buf, sizeof(buf));
+ if (err <= 0)
+ continue;
+ if (!strcmp(name, buf))
+ return i;
+ }
+ return -ENOENT;
+}
+EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin);
+
+static int bcm47xx_nvram_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct bcm47xx_nvram *nvram;
+ int err;
+ struct resource flash_mem;
+ void __iomem *mmio;
+
+ /* Alloc */
+ nvram = devm_kzalloc(dev, sizeof(*nvram), GFP_KERNEL);
+ if (!nvram)
+ return -ENOMEM;
+
+ err = of_address_to_resource(np, 0, &flash_mem);
+ if (err)
+ return err;
+
+ mmio = ioremap_nocache(flash_mem.start, resource_size(&flash_mem));
+ if (!mmio)
+ return -ENOMEM;
+
+ err = nvram_find_and_copy(dev, mmio, resource_size(&flash_mem),
+ &nvram->nvram_buf, &nvram->nvram_len);
+ if (err)
+ goto err_unmap_mmio;
+
+ platform_set_drvdata(pdev, nvram);
+
+err_unmap_mmio:
+ iounmap(mmio);
+ return err;
+}
+
+static const struct of_device_id bcm47xx_nvram_of_match_table[] = {
+ { .compatible = "brcm,bcm47xx-nvram", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table);
+
+static struct platform_driver bcm47xx_nvram_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bcm47xx-nvram",
+ .of_match_table = bcm47xx_nvram_of_match_table,
+ /* driver unloading/unbinding currently not supported */
+ .suppress_bind_attrs = true,
+ },
+ .probe = bcm47xx_nvram_probe,
+};
+module_platform_driver(bcm47xx_nvram_driver);
+
+MODULE_AUTHOR("Hauke Mehrtens <[email protected]>");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index d2714a2..8340498 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -411,7 +411,7 @@ static void b44_wap54g10_workaround(struct b44 *bp)
* 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);
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index bdda57b..1df7fb0 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -952,7 +952,8 @@ static void bgmac_chip_reset(struct bgmac *bgmac)
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);
@@ -1501,7 +1502,7 @@ static int bgmac_probe(struct bcma_device *core)
}
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 */
diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c
index 8d07d4d..b611ea1 100644
--- 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_chipcommon *cc)
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);
}
diff --git a/include/linux/bcm47xx_nvram.h b/include/linux/bcm47xx_nvram.h
index 333d32c..515fc06 100644
--- 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;
}
--
1.9.1
This driver is used by the bcm53xx ARM SoC code. Now it is possible to
give the address of the chipcommon core in device tree and bcma will
search for all the other cores.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
Documentation/devicetree/bindings/bus/bcma.txt | 46 +++++++++++++++++
drivers/bcma/host_soc.c | 70 ++++++++++++++++++++++++++
include/linux/bcma/bcma.h | 2 +
3 files changed, 118 insertions(+)
create mode 100644 Documentation/devicetree/bindings/bus/bcma.txt
diff --git a/Documentation/devicetree/bindings/bus/bcma.txt b/Documentation/devicetree/bindings/bus/bcma.txt
new file mode 100644
index 0000000..52fb929
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/bcma.txt
@@ -0,0 +1,46 @@
+Broadcom AIX bcma bus driver
+
+
+Required properties:
+
+- compatible : brcm,bus-aix
+
+- reg : iomem address range of chipcommon core
+
+Optional properties:
+
+- sprom: reference to a sprom driver. This is needed for sprom less devices.
+ Use bcm47xx_sprom for example.
+
+
+The cores on the AIX bus are auto detected by bcma. Detection of the
+IRQ number is not supported on BCM47xx/BCM53xx ARM SoCs, so it is
+possible to provide the IRQ number over device tree. The IRQ number and
+the device tree child entry will be added to the core with the matching
+reg address.
+
+Example:
+
+ aix@18000000 {
+ compatible = "brcm,bus-aix";
+ reg = <0x18000000 0x1000>;
+ ranges = <0x00000000 0x18000000 0x00100000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ sprom = <&sprom0>;
+
+ gmac@0 {
+ reg = <0x18024000 0x1000>;
+ interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gmac@1 {
+ reg = <0x18025000 0x1000>;
+ interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pcie@0 {
+ reg = <0x18012000 0x1000>;
+ interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c
index 3475e60..d009bc9 100644
--- a/drivers/bcma/host_soc.c
+++ b/drivers/bcma/host_soc.c
@@ -7,6 +7,9 @@
#include "bcma_private.h"
#include "scan.h"
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
#include <linux/bcma/bcma.h>
#include <linux/bcma/bcma_soc.h>
@@ -173,6 +176,7 @@ int __init bcma_host_soc_register(struct bcma_soc *soc)
/* Host specific */
bus->hosttype = BCMA_HOSTTYPE_SOC;
bus->ops = &bcma_host_soc_ops;
+ bus->host_pdev = NULL;
/* Register */
err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
@@ -181,3 +185,69 @@ int __init bcma_host_soc_register(struct bcma_soc *soc)
return err;
}
+
+#ifdef CONFIG_OF
+static int bcma_host_soc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct bcma_bus *bus;
+ int err;
+
+ /* Alloc */
+ bus = devm_kzalloc(dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
+ /* Map MMIO */
+ bus->mmio = of_iomap(np, 0);
+ if (!bus->mmio)
+ return -ENOMEM;
+
+ /* Host specific */
+ bus->hosttype = BCMA_HOSTTYPE_SOC;
+ bus->ops = &bcma_host_soc_ops;
+ bus->host_pdev = pdev;
+
+ /* Register */
+ err = bcma_bus_register(bus);
+ if (err)
+ goto err_unmap_mmio;
+
+ platform_set_drvdata(pdev, bus);
+
+ return err;
+
+err_unmap_mmio:
+ iounmap(bus->mmio);
+ return err;
+}
+
+static int bcma_host_soc_remove(struct platform_device *pdev)
+{
+ struct bcma_bus *bus = platform_get_drvdata(pdev);
+
+ bcma_bus_unregister(bus);
+ iounmap(bus->mmio);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id bcma_host_soc_of_match[] = {
+ { .compatible = "brcm,bus-aix", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcma_host_soc_of_match);
+
+static struct platform_driver bcma_host_soc_driver = {
+ .driver = {
+ .name = "bcma-host-soc",
+ .owner = THIS_MODULE,
+ .of_match_table = bcma_host_soc_of_match,
+ },
+ .probe = bcma_host_soc_probe,
+ .remove = bcma_host_soc_remove,
+};
+module_platform_driver(bcma_host_soc_driver);
+#endif /* CONFIG_OF */
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
index 0272e49..7a10d6d 100644
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -323,6 +323,8 @@ struct bcma_bus {
struct pci_dev *host_pci;
/* Pointer to the SDIO device (only for BCMA_HOSTTYPE_SDIO) */
struct sdio_func *host_sdio;
+ /* Pointer to platform device (only for BCMA_HOSTTYPE_SOC) */
+ struct platform_device *host_pdev;
};
struct bcma_chipinfo chipinfo;
--
1.9.1
This patch make it possible to device an sprom provider in device tree
and get the sprom from this driver. Every time there is such a provider
it gets asked for a sprom.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/sprom.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index efb037f..ebe495d 100644
--- 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(int (*sprom_callback)(struct bcma_bus *bus
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;
--
1.9.1
On 08/24/2014 02:24 PM, Hauke Mehrtens wrote:
> ---
> arch/arm/boot/dts/bcm4708.dtsi | 58 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 58 insertions(+)
We probably want a compatible string for each of these aix subnodes you
are adding, so we can match them.
>
> diff --git a/arch/arm/boot/dts/bcm4708.dtsi b/arch/arm/boot/dts/bcm4708.dtsi
> index 31141e8..7c240ab 100644
> --- a/arch/arm/boot/dts/bcm4708.dtsi
> +++ b/arch/arm/boot/dts/bcm4708.dtsi
> @@ -31,4 +31,62 @@
> };
> };
>
> + nvram0: nvram@0 {
> + compatible = "brcm,bcm47xx-nvram";
> + reg = <0x1c000000 0x01000000>;
> + };
> +
> + sprom0: sprom@0 {
> + compatible = "brcm,bcm47xx-sprom";
> + nvram = <&nvram0>;
> + };
> +
> + aix@18000000 {
> + compatible = "brcm,bus-aix";
> + reg = <0x18000000 0x1000>;
> + ranges = <0x00000000 0x18000000 0x00100000>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> + sprom = <&sprom0>;
> +
> + usb2@0 {
> + reg = <0x18021000 0x1000>;
> + interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + usb3@0 {
> + reg = <0x18023000 0x1000>;
> + interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + gmac@0 {
> + reg = <0x18024000 0x1000>;
> + interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + gmac@1 {
> + reg = <0x18025000 0x1000>;
> + interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + gmac@2 {
> + reg = <0x18026000 0x1000>;
> + interrupts = <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + gmac@3 {
> + reg = <0x18027000 0x1000>;
> + interrupts = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + pcie@0 {
> + reg = <0x18012000 0x1000>;
> + interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + pcie@1 {
> + reg = <0x18013000 0x1000>;
> + interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
> + };
> + };
> };
>
On 26 August 2014 23:32, Hauke Mehrtens <[email protected]> wrote:
> On 08/25/2014 09:52 AM, Arnd Bergmann wrote:
>> On Sunday 24 August 2014 23:24:41 Hauke Mehrtens wrote:
>>> drivers/misc/Kconfig | 14 +
>>> drivers/misc/Makefile | 1 +
>>> drivers/misc/bcm47xx-sprom.c | 690 +++++++++++++++++++++
>>
>> On a similar note, putting the driver into drivers/misc seems
>> suboptimal: misc drivers should by definition be something that
>> is for some odd hardware with no external dependencies on it,
>> whereas your driver seems to be used by multiple other drivers.
>>
>> Would it make sense to put it into drivers/bcma when that is the
>> only bus it is used on?
>
> As Jonas already said this code should be used for the bcm53xx ARM code
> and the bcm47xx MIPS code and it is needed for drivers/bcma/ and
> drivers/ssb/ (ssb only for old mips devices). Do you have any better
> idea than putting this to drivers/misc/ ? For the mips SoC we need the
> code very early and will not use the driver interface but probably
> directly call the function name.
Ping? Does anyone have any better idea?
Both: nvram and sprom drivers will be used by bcm47xx (mips) and
bcm53xx (arm). They can't be put in drivers/bcma, as they are used by
drivers/ssb as well.