aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux
diff options
context:
space:
mode:
authorRafał Miłecki <zajec5@gmail.com>2015-11-16 06:55:38 +0000
committerRafał Miłecki <zajec5@gmail.com>2015-11-16 06:55:38 +0000
commitb2dab45aa776f8cacef8606af05cc657c8f99323 (patch)
tree9cddf12960f3ea2bf6f4ce656572fb7301cc8c5e /target/linux
parentd42669bf330fab4249c0feb33ae7894a7c935a35 (diff)
downloadupstream-b2dab45aa776f8cacef8606af05cc657c8f99323.tar.gz
upstream-b2dab45aa776f8cacef8606af05cc657c8f99323.tar.bz2
upstream-b2dab45aa776f8cacef8606af05cc657c8f99323.zip
kernel: backport ssb changes from 4.4-rc1
Signed-off-by: Rafał Miłecki <zajec5@gmail.com> SVN-Revision: 47483
Diffstat (limited to 'target/linux')
-rw-r--r--target/linux/generic/patches-4.1/020-ssb-backport.patch686
-rw-r--r--target/linux/generic/patches-4.3/020-ssb-backport.patch686
2 files changed, 1372 insertions, 0 deletions
diff --git a/target/linux/generic/patches-4.1/020-ssb-backport.patch b/target/linux/generic/patches-4.1/020-ssb-backport.patch
new file mode 100644
index 0000000000..e96480b021
--- /dev/null
+++ b/target/linux/generic/patches-4.1/020-ssb-backport.patch
@@ -0,0 +1,686 @@
+--- a/arch/mips/bcm47xx/Kconfig
++++ b/arch/mips/bcm47xx/Kconfig
+@@ -4,6 +4,7 @@ config BCM47XX_SSB
+ bool "SSB Support for Broadcom BCM47XX"
+ select SYS_HAS_CPU_BMIPS32_3300
+ select SSB
++ select SSB_HOST_SOC
+ select SSB_DRIVER_MIPS
+ select SSB_DRIVER_EXTIF
+ select SSB_EMBEDDED
+--- a/drivers/ssb/Kconfig
++++ b/drivers/ssb/Kconfig
+@@ -80,6 +80,15 @@ config SSB_SDIOHOST
+
+ If unsure, say N
+
++config SSB_HOST_SOC
++ bool "Support for SSB bus on SoC"
++ depends on SSB
++ help
++ Host interface for a SSB directly mapped into memory. This is
++ for some Broadcom SoCs from the BCM47xx and BCM53xx lines.
++
++ If unsure, say N
++
+ config SSB_SILENT
+ bool "No SSB kernel messages"
+ depends on SSB && EXPERT
+--- a/drivers/ssb/Makefile
++++ b/drivers/ssb/Makefile
+@@ -5,8 +5,9 @@ ssb-$(CONFIG_SSB_SPROM) += sprom.o
+
+ # host support
+ ssb-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o
+-ssb-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o
++ssb-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o bridge_pcmcia_80211.o
+ ssb-$(CONFIG_SSB_SDIOHOST) += sdio.o
++ssb-$(CONFIG_SSB_HOST_SOC) += host_soc.o
+
+ # built-in drivers
+ ssb-y += driver_chipcommon.o
+--- /dev/null
++++ b/drivers/ssb/bridge_pcmcia_80211.c
+@@ -0,0 +1,128 @@
++/*
++ * Broadcom 43xx PCMCIA-SSB bridge module
++ *
++ * Copyright (c) 2007 Michael Buesch <m@bues.ch>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++#include <linux/ssb/ssb.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include "ssb_private.h"
++
++static const struct pcmcia_device_id ssb_host_pcmcia_tbl[] = {
++ PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448),
++ PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476),
++ PCMCIA_DEVICE_NULL,
++};
++
++MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl);
++
++static int ssb_host_pcmcia_probe(struct pcmcia_device *dev)
++{
++ struct ssb_bus *ssb;
++ int err = -ENOMEM;
++ int res = 0;
++
++ ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
++ if (!ssb)
++ goto out_error;
++
++ err = -ENODEV;
++
++ dev->config_flags |= CONF_ENABLE_IRQ;
++
++ dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 |
++ WIN_USE_WAIT;
++ dev->resource[2]->start = 0;
++ dev->resource[2]->end = SSB_CORE_SIZE;
++ res = pcmcia_request_window(dev, dev->resource[2], 250);
++ if (res != 0)
++ goto err_kfree_ssb;
++
++ res = pcmcia_map_mem_page(dev, dev->resource[2], 0);
++ if (res != 0)
++ goto err_disable;
++
++ if (!dev->irq)
++ goto err_disable;
++
++ res = pcmcia_enable_device(dev);
++ if (res != 0)
++ goto err_disable;
++
++ err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start);
++ if (err)
++ goto err_disable;
++ dev->priv = ssb;
++
++ return 0;
++
++err_disable:
++ pcmcia_disable_device(dev);
++err_kfree_ssb:
++ kfree(ssb);
++out_error:
++ ssb_err("Initialization failed (%d, %d)\n", res, err);
++ return err;
++}
++
++static void ssb_host_pcmcia_remove(struct pcmcia_device *dev)
++{
++ struct ssb_bus *ssb = dev->priv;
++
++ ssb_bus_unregister(ssb);
++ pcmcia_disable_device(dev);
++ kfree(ssb);
++ dev->priv = NULL;
++}
++
++#ifdef CONFIG_PM
++static int ssb_host_pcmcia_suspend(struct pcmcia_device *dev)
++{
++ struct ssb_bus *ssb = dev->priv;
++
++ return ssb_bus_suspend(ssb);
++}
++
++static int ssb_host_pcmcia_resume(struct pcmcia_device *dev)
++{
++ struct ssb_bus *ssb = dev->priv;
++
++ return ssb_bus_resume(ssb);
++}
++#else /* CONFIG_PM */
++# define ssb_host_pcmcia_suspend NULL
++# define ssb_host_pcmcia_resume NULL
++#endif /* CONFIG_PM */
++
++static struct pcmcia_driver ssb_host_pcmcia_driver = {
++ .owner = THIS_MODULE,
++ .name = "ssb-pcmcia",
++ .id_table = ssb_host_pcmcia_tbl,
++ .probe = ssb_host_pcmcia_probe,
++ .remove = ssb_host_pcmcia_remove,
++ .suspend = ssb_host_pcmcia_suspend,
++ .resume = ssb_host_pcmcia_resume,
++};
++
++/*
++ * These are not module init/exit functions!
++ * The module_pcmcia_driver() helper cannot be used here.
++ */
++int ssb_host_pcmcia_init(void)
++{
++ return pcmcia_register_driver(&ssb_host_pcmcia_driver);
++}
++
++void ssb_host_pcmcia_exit(void)
++{
++ pcmcia_unregister_driver(&ssb_host_pcmcia_driver);
++}
+--- /dev/null
++++ b/drivers/ssb/host_soc.c
+@@ -0,0 +1,173 @@
++/*
++ * Sonics Silicon Backplane SoC host related functions.
++ * Subsystem core
++ *
++ * Copyright 2005, Broadcom Corporation
++ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++#include <linux/ssb/ssb.h>
++
++#include "ssb_private.h"
++
++static u8 ssb_host_soc_read8(struct ssb_device *dev, u16 offset)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ return readb(bus->mmio + offset);
++}
++
++static u16 ssb_host_soc_read16(struct ssb_device *dev, u16 offset)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ return readw(bus->mmio + offset);
++}
++
++static u32 ssb_host_soc_read32(struct ssb_device *dev, u16 offset)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ return readl(bus->mmio + offset);
++}
++
++#ifdef CONFIG_SSB_BLOCKIO
++static void ssb_host_soc_block_read(struct ssb_device *dev, void *buffer,
++ size_t count, u16 offset, u8 reg_width)
++{
++ struct ssb_bus *bus = dev->bus;
++ void __iomem *addr;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ addr = bus->mmio + offset;
++
++ switch (reg_width) {
++ case sizeof(u8): {
++ u8 *buf = buffer;
++
++ while (count) {
++ *buf = __raw_readb(addr);
++ buf++;
++ count--;
++ }
++ break;
++ }
++ case sizeof(u16): {
++ __le16 *buf = buffer;
++
++ SSB_WARN_ON(count & 1);
++ while (count) {
++ *buf = (__force __le16)__raw_readw(addr);
++ buf++;
++ count -= 2;
++ }
++ break;
++ }
++ case sizeof(u32): {
++ __le32 *buf = buffer;
++
++ SSB_WARN_ON(count & 3);
++ while (count) {
++ *buf = (__force __le32)__raw_readl(addr);
++ buf++;
++ count -= 4;
++ }
++ break;
++ }
++ default:
++ SSB_WARN_ON(1);
++ }
++}
++#endif /* CONFIG_SSB_BLOCKIO */
++
++static void ssb_host_soc_write8(struct ssb_device *dev, u16 offset, u8 value)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ writeb(value, bus->mmio + offset);
++}
++
++static void ssb_host_soc_write16(struct ssb_device *dev, u16 offset, u16 value)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ writew(value, bus->mmio + offset);
++}
++
++static void ssb_host_soc_write32(struct ssb_device *dev, u16 offset, u32 value)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ writel(value, bus->mmio + offset);
++}
++
++#ifdef CONFIG_SSB_BLOCKIO
++static void ssb_host_soc_block_write(struct ssb_device *dev, const void *buffer,
++ size_t count, u16 offset, u8 reg_width)
++{
++ struct ssb_bus *bus = dev->bus;
++ void __iomem *addr;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ addr = bus->mmio + offset;
++
++ switch (reg_width) {
++ case sizeof(u8): {
++ const u8 *buf = buffer;
++
++ while (count) {
++ __raw_writeb(*buf, addr);
++ buf++;
++ count--;
++ }
++ break;
++ }
++ case sizeof(u16): {
++ const __le16 *buf = buffer;
++
++ SSB_WARN_ON(count & 1);
++ while (count) {
++ __raw_writew((__force u16)(*buf), addr);
++ buf++;
++ count -= 2;
++ }
++ break;
++ }
++ case sizeof(u32): {
++ const __le32 *buf = buffer;
++
++ SSB_WARN_ON(count & 3);
++ while (count) {
++ __raw_writel((__force u32)(*buf), addr);
++ buf++;
++ count -= 4;
++ }
++ break;
++ }
++ default:
++ SSB_WARN_ON(1);
++ }
++}
++#endif /* CONFIG_SSB_BLOCKIO */
++
++/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
++const struct ssb_bus_ops ssb_host_soc_ops = {
++ .read8 = ssb_host_soc_read8,
++ .read16 = ssb_host_soc_read16,
++ .read32 = ssb_host_soc_read32,
++ .write8 = ssb_host_soc_write8,
++ .write16 = ssb_host_soc_write16,
++ .write32 = ssb_host_soc_write32,
++#ifdef CONFIG_SSB_BLOCKIO
++ .block_read = ssb_host_soc_block_read,
++ .block_write = ssb_host_soc_block_write,
++#endif
++};
+--- a/drivers/ssb/main.c
++++ b/drivers/ssb/main.c
+@@ -596,166 +596,6 @@ error:
+ return err;
+ }
+
+-static u8 ssb_ssb_read8(struct ssb_device *dev, u16 offset)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- return readb(bus->mmio + offset);
+-}
+-
+-static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- return readw(bus->mmio + offset);
+-}
+-
+-static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- return readl(bus->mmio + offset);
+-}
+-
+-#ifdef CONFIG_SSB_BLOCKIO
+-static void ssb_ssb_block_read(struct ssb_device *dev, void *buffer,
+- size_t count, u16 offset, u8 reg_width)
+-{
+- struct ssb_bus *bus = dev->bus;
+- void __iomem *addr;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- addr = bus->mmio + offset;
+-
+- switch (reg_width) {
+- case sizeof(u8): {
+- u8 *buf = buffer;
+-
+- while (count) {
+- *buf = __raw_readb(addr);
+- buf++;
+- count--;
+- }
+- break;
+- }
+- case sizeof(u16): {
+- __le16 *buf = buffer;
+-
+- SSB_WARN_ON(count & 1);
+- while (count) {
+- *buf = (__force __le16)__raw_readw(addr);
+- buf++;
+- count -= 2;
+- }
+- break;
+- }
+- case sizeof(u32): {
+- __le32 *buf = buffer;
+-
+- SSB_WARN_ON(count & 3);
+- while (count) {
+- *buf = (__force __le32)__raw_readl(addr);
+- buf++;
+- count -= 4;
+- }
+- break;
+- }
+- default:
+- SSB_WARN_ON(1);
+- }
+-}
+-#endif /* CONFIG_SSB_BLOCKIO */
+-
+-static void ssb_ssb_write8(struct ssb_device *dev, u16 offset, u8 value)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- writeb(value, bus->mmio + offset);
+-}
+-
+-static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- writew(value, bus->mmio + offset);
+-}
+-
+-static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- writel(value, bus->mmio + offset);
+-}
+-
+-#ifdef CONFIG_SSB_BLOCKIO
+-static void ssb_ssb_block_write(struct ssb_device *dev, const void *buffer,
+- size_t count, u16 offset, u8 reg_width)
+-{
+- struct ssb_bus *bus = dev->bus;
+- void __iomem *addr;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- addr = bus->mmio + offset;
+-
+- switch (reg_width) {
+- case sizeof(u8): {
+- const u8 *buf = buffer;
+-
+- while (count) {
+- __raw_writeb(*buf, addr);
+- buf++;
+- count--;
+- }
+- break;
+- }
+- case sizeof(u16): {
+- const __le16 *buf = buffer;
+-
+- SSB_WARN_ON(count & 1);
+- while (count) {
+- __raw_writew((__force u16)(*buf), addr);
+- buf++;
+- count -= 2;
+- }
+- break;
+- }
+- case sizeof(u32): {
+- const __le32 *buf = buffer;
+-
+- SSB_WARN_ON(count & 3);
+- while (count) {
+- __raw_writel((__force u32)(*buf), addr);
+- buf++;
+- count -= 4;
+- }
+- break;
+- }
+- default:
+- SSB_WARN_ON(1);
+- }
+-}
+-#endif /* CONFIG_SSB_BLOCKIO */
+-
+-/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
+-static const struct ssb_bus_ops ssb_ssb_ops = {
+- .read8 = ssb_ssb_read8,
+- .read16 = ssb_ssb_read16,
+- .read32 = ssb_ssb_read32,
+- .write8 = ssb_ssb_write8,
+- .write16 = ssb_ssb_write16,
+- .write32 = ssb_ssb_write32,
+-#ifdef CONFIG_SSB_BLOCKIO
+- .block_read = ssb_ssb_block_read,
+- .block_write = ssb_ssb_block_write,
+-#endif
+-};
+-
+ static int ssb_fetch_invariants(struct ssb_bus *bus,
+ ssb_invariants_func_t get_invariants)
+ {
+@@ -876,7 +716,6 @@ int ssb_bus_pcibus_register(struct ssb_b
+
+ return err;
+ }
+-EXPORT_SYMBOL(ssb_bus_pcibus_register);
+ #endif /* CONFIG_SSB_PCIHOST */
+
+ #ifdef CONFIG_SSB_PCMCIAHOST
+@@ -898,7 +737,6 @@ int ssb_bus_pcmciabus_register(struct ss
+
+ return err;
+ }
+-EXPORT_SYMBOL(ssb_bus_pcmciabus_register);
+ #endif /* CONFIG_SSB_PCMCIAHOST */
+
+ #ifdef CONFIG_SSB_SDIOHOST
+@@ -923,13 +761,14 @@ int ssb_bus_sdiobus_register(struct ssb_
+ EXPORT_SYMBOL(ssb_bus_sdiobus_register);
+ #endif /* CONFIG_SSB_PCMCIAHOST */
+
++#ifdef CONFIG_SSB_HOST_SOC
+ int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr,
+ ssb_invariants_func_t get_invariants)
+ {
+ int err;
+
+ bus->bustype = SSB_BUSTYPE_SSB;
+- bus->ops = &ssb_ssb_ops;
++ bus->ops = &ssb_host_soc_ops;
+
+ err = ssb_bus_register(bus, get_invariants, baseaddr);
+ if (!err) {
+@@ -939,6 +778,7 @@ int ssb_bus_ssbbus_register(struct ssb_b
+
+ return err;
+ }
++#endif
+
+ int __ssb_driver_register(struct ssb_driver *drv, struct module *owner)
+ {
+@@ -1465,6 +1305,12 @@ static int __init ssb_modinit(void)
+ /* don't fail SSB init because of this */
+ err = 0;
+ }
++ err = ssb_host_pcmcia_init();
++ if (err) {
++ ssb_err("PCMCIA host initialization failed\n");
++ /* don't fail SSB init because of this */
++ err = 0;
++ }
+ err = ssb_gige_init();
+ if (err) {
+ ssb_err("SSB Broadcom Gigabit Ethernet driver initialization failed\n");
+@@ -1482,6 +1328,7 @@ fs_initcall(ssb_modinit);
+ static void __exit ssb_modexit(void)
+ {
+ ssb_gige_exit();
++ ssb_host_pcmcia_exit();
+ b43_pci_ssb_bridge_exit();
+ bus_unregister(&ssb_bustype);
+ }
+--- a/drivers/ssb/pcmcia.c
++++ b/drivers/ssb/pcmcia.c
+@@ -147,8 +147,7 @@ error:
+ return err;
+ }
+
+-int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+- struct ssb_device *dev)
++static int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
+ {
+ int err;
+
+--- a/drivers/ssb/sdio.c
++++ b/drivers/ssb/sdio.c
+@@ -200,7 +200,7 @@ out:
+ }
+
+ /* host must be already claimed */
+-int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
++static int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
+ {
+ u8 coreidx = dev->core_index;
+ u32 sbaddr;
+--- a/drivers/ssb/ssb_private.h
++++ b/drivers/ssb/ssb_private.h
+@@ -85,8 +85,6 @@ static inline int ssb_pci_init(struct ss
+
+ /* pcmcia.c */
+ #ifdef CONFIG_SSB_PCMCIAHOST
+-extern int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+- struct ssb_device *dev);
+ extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+ u8 coreidx);
+ extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
+@@ -96,13 +94,10 @@ extern int ssb_pcmcia_get_invariants(str
+ extern int ssb_pcmcia_hardware_setup(struct ssb_bus *bus);
+ extern void ssb_pcmcia_exit(struct ssb_bus *bus);
+ extern int ssb_pcmcia_init(struct ssb_bus *bus);
++extern int ssb_host_pcmcia_init(void);
++extern void ssb_host_pcmcia_exit(void);
+ extern const struct ssb_bus_ops ssb_pcmcia_ops;
+ #else /* CONFIG_SSB_PCMCIAHOST */
+-static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+- struct ssb_device *dev)
+-{
+- return 0;
+-}
+ static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+ u8 coreidx)
+ {
+@@ -124,6 +119,13 @@ static inline int ssb_pcmcia_init(struct
+ {
+ return 0;
+ }
++static inline int ssb_host_pcmcia_init(void)
++{
++ return 0;
++}
++static inline void ssb_host_pcmcia_exit(void)
++{
++}
+ #endif /* CONFIG_SSB_PCMCIAHOST */
+
+ /* sdio.c */
+@@ -132,9 +134,7 @@ extern int ssb_sdio_get_invariants(struc
+ struct ssb_init_invariants *iv);
+
+ extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset);
+-extern int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev);
+ extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx);
+-extern int ssb_sdio_hardware_setup(struct ssb_bus *bus);
+ extern void ssb_sdio_exit(struct ssb_bus *bus);
+ extern int ssb_sdio_init(struct ssb_bus *bus);
+
+@@ -144,19 +144,10 @@ static inline u32 ssb_sdio_scan_read32(s
+ {
+ return 0;
+ }
+-static inline int ssb_sdio_switch_core(struct ssb_bus *bus,
+- struct ssb_device *dev)
+-{
+- return 0;
+-}
+ static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
+ {
+ return 0;
+ }
+-static inline int ssb_sdio_hardware_setup(struct ssb_bus *bus)
+-{
+- return 0;
+-}
+ static inline void ssb_sdio_exit(struct ssb_bus *bus)
+ {
+ }
+@@ -166,6 +157,13 @@ static inline int ssb_sdio_init(struct s
+ }
+ #endif /* CONFIG_SSB_SDIOHOST */
+
++/**************************************************
++ * host_soc.c
++ **************************************************/
++
++#ifdef CONFIG_SSB_HOST_SOC
++extern const struct ssb_bus_ops ssb_host_soc_ops;
++#endif
+
+ /* scan.c */
+ extern const char *ssb_core_name(u16 coreid);
diff --git a/target/linux/generic/patches-4.3/020-ssb-backport.patch b/target/linux/generic/patches-4.3/020-ssb-backport.patch
new file mode 100644
index 0000000000..e96480b021
--- /dev/null
+++ b/target/linux/generic/patches-4.3/020-ssb-backport.patch
@@ -0,0 +1,686 @@
+--- a/arch/mips/bcm47xx/Kconfig
++++ b/arch/mips/bcm47xx/Kconfig
+@@ -4,6 +4,7 @@ config BCM47XX_SSB
+ bool "SSB Support for Broadcom BCM47XX"
+ select SYS_HAS_CPU_BMIPS32_3300
+ select SSB
++ select SSB_HOST_SOC
+ select SSB_DRIVER_MIPS
+ select SSB_DRIVER_EXTIF
+ select SSB_EMBEDDED
+--- a/drivers/ssb/Kconfig
++++ b/drivers/ssb/Kconfig
+@@ -80,6 +80,15 @@ config SSB_SDIOHOST
+
+ If unsure, say N
+
++config SSB_HOST_SOC
++ bool "Support for SSB bus on SoC"
++ depends on SSB
++ help
++ Host interface for a SSB directly mapped into memory. This is
++ for some Broadcom SoCs from the BCM47xx and BCM53xx lines.
++
++ If unsure, say N
++
+ config SSB_SILENT
+ bool "No SSB kernel messages"
+ depends on SSB && EXPERT
+--- a/drivers/ssb/Makefile
++++ b/drivers/ssb/Makefile
+@@ -5,8 +5,9 @@ ssb-$(CONFIG_SSB_SPROM) += sprom.o
+
+ # host support
+ ssb-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o
+-ssb-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o
++ssb-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o bridge_pcmcia_80211.o
+ ssb-$(CONFIG_SSB_SDIOHOST) += sdio.o
++ssb-$(CONFIG_SSB_HOST_SOC) += host_soc.o
+
+ # built-in drivers
+ ssb-y += driver_chipcommon.o
+--- /dev/null
++++ b/drivers/ssb/bridge_pcmcia_80211.c
+@@ -0,0 +1,128 @@
++/*
++ * Broadcom 43xx PCMCIA-SSB bridge module
++ *
++ * Copyright (c) 2007 Michael Buesch <m@bues.ch>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++#include <linux/ssb/ssb.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include "ssb_private.h"
++
++static const struct pcmcia_device_id ssb_host_pcmcia_tbl[] = {
++ PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448),
++ PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476),
++ PCMCIA_DEVICE_NULL,
++};
++
++MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl);
++
++static int ssb_host_pcmcia_probe(struct pcmcia_device *dev)
++{
++ struct ssb_bus *ssb;
++ int err = -ENOMEM;
++ int res = 0;
++
++ ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
++ if (!ssb)
++ goto out_error;
++
++ err = -ENODEV;
++
++ dev->config_flags |= CONF_ENABLE_IRQ;
++
++ dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 |
++ WIN_USE_WAIT;
++ dev->resource[2]->start = 0;
++ dev->resource[2]->end = SSB_CORE_SIZE;
++ res = pcmcia_request_window(dev, dev->resource[2], 250);
++ if (res != 0)
++ goto err_kfree_ssb;
++
++ res = pcmcia_map_mem_page(dev, dev->resource[2], 0);
++ if (res != 0)
++ goto err_disable;
++
++ if (!dev->irq)
++ goto err_disable;
++
++ res = pcmcia_enable_device(dev);
++ if (res != 0)
++ goto err_disable;
++
++ err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start);
++ if (err)
++ goto err_disable;
++ dev->priv = ssb;
++
++ return 0;
++
++err_disable:
++ pcmcia_disable_device(dev);
++err_kfree_ssb:
++ kfree(ssb);
++out_error:
++ ssb_err("Initialization failed (%d, %d)\n", res, err);
++ return err;
++}
++
++static void ssb_host_pcmcia_remove(struct pcmcia_device *dev)
++{
++ struct ssb_bus *ssb = dev->priv;
++
++ ssb_bus_unregister(ssb);
++ pcmcia_disable_device(dev);
++ kfree(ssb);
++ dev->priv = NULL;
++}
++
++#ifdef CONFIG_PM
++static int ssb_host_pcmcia_suspend(struct pcmcia_device *dev)
++{
++ struct ssb_bus *ssb = dev->priv;
++
++ return ssb_bus_suspend(ssb);
++}
++
++static int ssb_host_pcmcia_resume(struct pcmcia_device *dev)
++{
++ struct ssb_bus *ssb = dev->priv;
++
++ return ssb_bus_resume(ssb);
++}
++#else /* CONFIG_PM */
++# define ssb_host_pcmcia_suspend NULL
++# define ssb_host_pcmcia_resume NULL
++#endif /* CONFIG_PM */
++
++static struct pcmcia_driver ssb_host_pcmcia_driver = {
++ .owner = THIS_MODULE,
++ .name = "ssb-pcmcia",
++ .id_table = ssb_host_pcmcia_tbl,
++ .probe = ssb_host_pcmcia_probe,
++ .remove = ssb_host_pcmcia_remove,
++ .suspend = ssb_host_pcmcia_suspend,
++ .resume = ssb_host_pcmcia_resume,
++};
++
++/*
++ * These are not module init/exit functions!
++ * The module_pcmcia_driver() helper cannot be used here.
++ */
++int ssb_host_pcmcia_init(void)
++{
++ return pcmcia_register_driver(&ssb_host_pcmcia_driver);
++}
++
++void ssb_host_pcmcia_exit(void)
++{
++ pcmcia_unregister_driver(&ssb_host_pcmcia_driver);
++}
+--- /dev/null
++++ b/drivers/ssb/host_soc.c
+@@ -0,0 +1,173 @@
++/*
++ * Sonics Silicon Backplane SoC host related functions.
++ * Subsystem core
++ *
++ * Copyright 2005, Broadcom Corporation
++ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
++ *
++ * Licensed under the GNU/GPL. See COPYING for details.
++ */
++
++#include <linux/ssb/ssb.h>
++
++#include "ssb_private.h"
++
++static u8 ssb_host_soc_read8(struct ssb_device *dev, u16 offset)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ return readb(bus->mmio + offset);
++}
++
++static u16 ssb_host_soc_read16(struct ssb_device *dev, u16 offset)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ return readw(bus->mmio + offset);
++}
++
++static u32 ssb_host_soc_read32(struct ssb_device *dev, u16 offset)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ return readl(bus->mmio + offset);
++}
++
++#ifdef CONFIG_SSB_BLOCKIO
++static void ssb_host_soc_block_read(struct ssb_device *dev, void *buffer,
++ size_t count, u16 offset, u8 reg_width)
++{
++ struct ssb_bus *bus = dev->bus;
++ void __iomem *addr;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ addr = bus->mmio + offset;
++
++ switch (reg_width) {
++ case sizeof(u8): {
++ u8 *buf = buffer;
++
++ while (count) {
++ *buf = __raw_readb(addr);
++ buf++;
++ count--;
++ }
++ break;
++ }
++ case sizeof(u16): {
++ __le16 *buf = buffer;
++
++ SSB_WARN_ON(count & 1);
++ while (count) {
++ *buf = (__force __le16)__raw_readw(addr);
++ buf++;
++ count -= 2;
++ }
++ break;
++ }
++ case sizeof(u32): {
++ __le32 *buf = buffer;
++
++ SSB_WARN_ON(count & 3);
++ while (count) {
++ *buf = (__force __le32)__raw_readl(addr);
++ buf++;
++ count -= 4;
++ }
++ break;
++ }
++ default:
++ SSB_WARN_ON(1);
++ }
++}
++#endif /* CONFIG_SSB_BLOCKIO */
++
++static void ssb_host_soc_write8(struct ssb_device *dev, u16 offset, u8 value)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ writeb(value, bus->mmio + offset);
++}
++
++static void ssb_host_soc_write16(struct ssb_device *dev, u16 offset, u16 value)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ writew(value, bus->mmio + offset);
++}
++
++static void ssb_host_soc_write32(struct ssb_device *dev, u16 offset, u32 value)
++{
++ struct ssb_bus *bus = dev->bus;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ writel(value, bus->mmio + offset);
++}
++
++#ifdef CONFIG_SSB_BLOCKIO
++static void ssb_host_soc_block_write(struct ssb_device *dev, const void *buffer,
++ size_t count, u16 offset, u8 reg_width)
++{
++ struct ssb_bus *bus = dev->bus;
++ void __iomem *addr;
++
++ offset += dev->core_index * SSB_CORE_SIZE;
++ addr = bus->mmio + offset;
++
++ switch (reg_width) {
++ case sizeof(u8): {
++ const u8 *buf = buffer;
++
++ while (count) {
++ __raw_writeb(*buf, addr);
++ buf++;
++ count--;
++ }
++ break;
++ }
++ case sizeof(u16): {
++ const __le16 *buf = buffer;
++
++ SSB_WARN_ON(count & 1);
++ while (count) {
++ __raw_writew((__force u16)(*buf), addr);
++ buf++;
++ count -= 2;
++ }
++ break;
++ }
++ case sizeof(u32): {
++ const __le32 *buf = buffer;
++
++ SSB_WARN_ON(count & 3);
++ while (count) {
++ __raw_writel((__force u32)(*buf), addr);
++ buf++;
++ count -= 4;
++ }
++ break;
++ }
++ default:
++ SSB_WARN_ON(1);
++ }
++}
++#endif /* CONFIG_SSB_BLOCKIO */
++
++/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
++const struct ssb_bus_ops ssb_host_soc_ops = {
++ .read8 = ssb_host_soc_read8,
++ .read16 = ssb_host_soc_read16,
++ .read32 = ssb_host_soc_read32,
++ .write8 = ssb_host_soc_write8,
++ .write16 = ssb_host_soc_write16,
++ .write32 = ssb_host_soc_write32,
++#ifdef CONFIG_SSB_BLOCKIO
++ .block_read = ssb_host_soc_block_read,
++ .block_write = ssb_host_soc_block_write,
++#endif
++};
+--- a/drivers/ssb/main.c
++++ b/drivers/ssb/main.c
+@@ -596,166 +596,6 @@ error:
+ return err;
+ }
+
+-static u8 ssb_ssb_read8(struct ssb_device *dev, u16 offset)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- return readb(bus->mmio + offset);
+-}
+-
+-static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- return readw(bus->mmio + offset);
+-}
+-
+-static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- return readl(bus->mmio + offset);
+-}
+-
+-#ifdef CONFIG_SSB_BLOCKIO
+-static void ssb_ssb_block_read(struct ssb_device *dev, void *buffer,
+- size_t count, u16 offset, u8 reg_width)
+-{
+- struct ssb_bus *bus = dev->bus;
+- void __iomem *addr;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- addr = bus->mmio + offset;
+-
+- switch (reg_width) {
+- case sizeof(u8): {
+- u8 *buf = buffer;
+-
+- while (count) {
+- *buf = __raw_readb(addr);
+- buf++;
+- count--;
+- }
+- break;
+- }
+- case sizeof(u16): {
+- __le16 *buf = buffer;
+-
+- SSB_WARN_ON(count & 1);
+- while (count) {
+- *buf = (__force __le16)__raw_readw(addr);
+- buf++;
+- count -= 2;
+- }
+- break;
+- }
+- case sizeof(u32): {
+- __le32 *buf = buffer;
+-
+- SSB_WARN_ON(count & 3);
+- while (count) {
+- *buf = (__force __le32)__raw_readl(addr);
+- buf++;
+- count -= 4;
+- }
+- break;
+- }
+- default:
+- SSB_WARN_ON(1);
+- }
+-}
+-#endif /* CONFIG_SSB_BLOCKIO */
+-
+-static void ssb_ssb_write8(struct ssb_device *dev, u16 offset, u8 value)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- writeb(value, bus->mmio + offset);
+-}
+-
+-static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- writew(value, bus->mmio + offset);
+-}
+-
+-static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
+-{
+- struct ssb_bus *bus = dev->bus;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- writel(value, bus->mmio + offset);
+-}
+-
+-#ifdef CONFIG_SSB_BLOCKIO
+-static void ssb_ssb_block_write(struct ssb_device *dev, const void *buffer,
+- size_t count, u16 offset, u8 reg_width)
+-{
+- struct ssb_bus *bus = dev->bus;
+- void __iomem *addr;
+-
+- offset += dev->core_index * SSB_CORE_SIZE;
+- addr = bus->mmio + offset;
+-
+- switch (reg_width) {
+- case sizeof(u8): {
+- const u8 *buf = buffer;
+-
+- while (count) {
+- __raw_writeb(*buf, addr);
+- buf++;
+- count--;
+- }
+- break;
+- }
+- case sizeof(u16): {
+- const __le16 *buf = buffer;
+-
+- SSB_WARN_ON(count & 1);
+- while (count) {
+- __raw_writew((__force u16)(*buf), addr);
+- buf++;
+- count -= 2;
+- }
+- break;
+- }
+- case sizeof(u32): {
+- const __le32 *buf = buffer;
+-
+- SSB_WARN_ON(count & 3);
+- while (count) {
+- __raw_writel((__force u32)(*buf), addr);
+- buf++;
+- count -= 4;
+- }
+- break;
+- }
+- default:
+- SSB_WARN_ON(1);
+- }
+-}
+-#endif /* CONFIG_SSB_BLOCKIO */
+-
+-/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
+-static const struct ssb_bus_ops ssb_ssb_ops = {
+- .read8 = ssb_ssb_read8,
+- .read16 = ssb_ssb_read16,
+- .read32 = ssb_ssb_read32,
+- .write8 = ssb_ssb_write8,
+- .write16 = ssb_ssb_write16,
+- .write32 = ssb_ssb_write32,
+-#ifdef CONFIG_SSB_BLOCKIO
+- .block_read = ssb_ssb_block_read,
+- .block_write = ssb_ssb_block_write,
+-#endif
+-};
+-
+ static int ssb_fetch_invariants(struct ssb_bus *bus,
+ ssb_invariants_func_t get_invariants)
+ {
+@@ -876,7 +716,6 @@ int ssb_bus_pcibus_register(struct ssb_b
+
+ return err;
+ }
+-EXPORT_SYMBOL(ssb_bus_pcibus_register);
+ #endif /* CONFIG_SSB_PCIHOST */
+
+ #ifdef CONFIG_SSB_PCMCIAHOST
+@@ -898,7 +737,6 @@ int ssb_bus_pcmciabus_register(struct ss
+
+ return err;
+ }
+-EXPORT_SYMBOL(ssb_bus_pcmciabus_register);
+ #endif /* CONFIG_SSB_PCMCIAHOST */
+
+ #ifdef CONFIG_SSB_SDIOHOST
+@@ -923,13 +761,14 @@ int ssb_bus_sdiobus_register(struct ssb_
+ EXPORT_SYMBOL(ssb_bus_sdiobus_register);
+ #endif /* CONFIG_SSB_PCMCIAHOST */
+
++#ifdef CONFIG_SSB_HOST_SOC
+ int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr,
+ ssb_invariants_func_t get_invariants)
+ {
+ int err;
+
+ bus->bustype = SSB_BUSTYPE_SSB;
+- bus->ops = &ssb_ssb_ops;
++ bus->ops = &ssb_host_soc_ops;
+
+ err = ssb_bus_register(bus, get_invariants, baseaddr);
+ if (!err) {
+@@ -939,6 +778,7 @@ int ssb_bus_ssbbus_register(struct ssb_b
+
+ return err;
+ }
++#endif
+
+ int __ssb_driver_register(struct ssb_driver *drv, struct module *owner)
+ {
+@@ -1465,6 +1305,12 @@ static int __init ssb_modinit(void)
+ /* don't fail SSB init because of this */
+ err = 0;
+ }
++ err = ssb_host_pcmcia_init();
++ if (err) {
++ ssb_err("PCMCIA host initialization failed\n");
++ /* don't fail SSB init because of this */
++ err = 0;
++ }
+ err = ssb_gige_init();
+ if (err) {
+ ssb_err("SSB Broadcom Gigabit Ethernet driver initialization failed\n");
+@@ -1482,6 +1328,7 @@ fs_initcall(ssb_modinit);
+ static void __exit ssb_modexit(void)
+ {
+ ssb_gige_exit();
++ ssb_host_pcmcia_exit();
+ b43_pci_ssb_bridge_exit();
+ bus_unregister(&ssb_bustype);
+ }
+--- a/drivers/ssb/pcmcia.c
++++ b/drivers/ssb/pcmcia.c
+@@ -147,8 +147,7 @@ error:
+ return err;
+ }
+
+-int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+- struct ssb_device *dev)
++static int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
+ {
+ int err;
+
+--- a/drivers/ssb/sdio.c
++++ b/drivers/ssb/sdio.c
+@@ -200,7 +200,7 @@ out:
+ }
+
+ /* host must be already claimed */
+-int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
++static int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
+ {
+ u8 coreidx = dev->core_index;
+ u32 sbaddr;
+--- a/drivers/ssb/ssb_private.h
++++ b/drivers/ssb/ssb_private.h
+@@ -85,8 +85,6 @@ static inline int ssb_pci_init(struct ss
+
+ /* pcmcia.c */
+ #ifdef CONFIG_SSB_PCMCIAHOST
+-extern int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+- struct ssb_device *dev);
+ extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+ u8 coreidx);
+ extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
+@@ -96,13 +94,10 @@ extern int ssb_pcmcia_get_invariants(str
+ extern int ssb_pcmcia_hardware_setup(struct ssb_bus *bus);
+ extern void ssb_pcmcia_exit(struct ssb_bus *bus);
+ extern int ssb_pcmcia_init(struct ssb_bus *bus);
++extern int ssb_host_pcmcia_init(void);
++extern void ssb_host_pcmcia_exit(void);
+ extern const struct ssb_bus_ops ssb_pcmcia_ops;
+ #else /* CONFIG_SSB_PCMCIAHOST */
+-static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+- struct ssb_device *dev)
+-{
+- return 0;
+-}
+ static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+ u8 coreidx)
+ {
+@@ -124,6 +119,13 @@ static inline int ssb_pcmcia_init(struct
+ {
+ return 0;
+ }
++static inline int ssb_host_pcmcia_init(void)
++{
++ return 0;
++}
++static inline void ssb_host_pcmcia_exit(void)
++{
++}
+ #endif /* CONFIG_SSB_PCMCIAHOST */
+
+ /* sdio.c */
+@@ -132,9 +134,7 @@ extern int ssb_sdio_get_invariants(struc
+ struct ssb_init_invariants *iv);
+
+ extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset);
+-extern int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev);
+ extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx);
+-extern int ssb_sdio_hardware_setup(struct ssb_bus *bus);
+ extern void ssb_sdio_exit(struct ssb_bus *bus);
+ extern int ssb_sdio_init(struct ssb_bus *bus);
+
+@@ -144,19 +144,10 @@ static inline u32 ssb_sdio_scan_read32(s
+ {
+ return 0;
+ }
+-static inline int ssb_sdio_switch_core(struct ssb_bus *bus,
+- struct ssb_device *dev)
+-{
+- return 0;
+-}
+ static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
+ {
+ return 0;
+ }
+-static inline int ssb_sdio_hardware_setup(struct ssb_bus *bus)
+-{
+- return 0;
+-}
+ static inline void ssb_sdio_exit(struct ssb_bus *bus)
+ {
+ }
+@@ -166,6 +157,13 @@ static inline int ssb_sdio_init(struct s
+ }
+ #endif /* CONFIG_SSB_SDIOHOST */
+
++/**************************************************
++ * host_soc.c
++ **************************************************/
++
++#ifdef CONFIG_SSB_HOST_SOC
++extern const struct ssb_bus_ops ssb_host_soc_ops;
++#endif
+
+ /* scan.c */
+ extern const char *ssb_core_name(u16 coreid);