aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/372-0001-brcmfmac-expose-device-memory-to-devcoredump-subsyst.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/372-0001-brcmfmac-expose-device-memory-to-devcoredump-subsyst.patch')
-rw-r--r--package/kernel/mac80211/patches/372-0001-brcmfmac-expose-device-memory-to-devcoredump-subsyst.patch347
1 files changed, 347 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/372-0001-brcmfmac-expose-device-memory-to-devcoredump-subsyst.patch b/package/kernel/mac80211/patches/372-0001-brcmfmac-expose-device-memory-to-devcoredump-subsyst.patch
new file mode 100644
index 0000000000..cf3f278871
--- /dev/null
+++ b/package/kernel/mac80211/patches/372-0001-brcmfmac-expose-device-memory-to-devcoredump-subsyst.patch
@@ -0,0 +1,347 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 8 Oct 2015 20:33:11 +0200
+Subject: [PATCH] brcmfmac: expose device memory to devcoredump subsystem
+
+Upon PSM watchdog event received from firmware the driver will obtain
+a memory snapshot of the device and expose it to user-space through
+the devcoredump framework. This will trigger a uevent.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/Kconfig
++++ b/drivers/net/wireless/brcm80211/Kconfig
+@@ -85,5 +85,6 @@ config BRCM_TRACING
+ config BRCMDBG
+ bool "Broadcom driver debug functions"
+ depends on BRCMSMAC || BRCMFMAC
++ select WANT_DEV_COREDUMP
+ ---help---
+ Selecting this enables additional code for debug purposes.
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
+@@ -65,6 +65,8 @@ struct brcmf_bus_dcmd {
+ * @rxctl: receive a control response message from dongle.
+ * @gettxq: obtain a reference of bus transmit queue (optional).
+ * @wowl_config: specify if dongle is configured for wowl when going to suspend
++ * @get_ramsize: obtain size of device memory.
++ * @get_memdump: obtain device memory dump in provided buffer.
+ *
+ * This structure provides an abstract interface towards the
+ * bus specific driver. For control messages to common driver
+@@ -79,6 +81,8 @@ struct brcmf_bus_ops {
+ int (*rxctl)(struct device *dev, unsigned char *msg, uint len);
+ struct pktq * (*gettxq)(struct device *dev);
+ void (*wowl_config)(struct device *dev, bool enabled);
++ size_t (*get_ramsize)(struct device *dev);
++ int (*get_memdump)(struct device *dev, void *data, size_t len);
+ };
+
+
+@@ -185,6 +189,23 @@ void brcmf_bus_wowl_config(struct brcmf_
+ bus->ops->wowl_config(bus->dev, enabled);
+ }
+
++static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus)
++{
++ if (!bus->ops->get_ramsize)
++ return 0;
++
++ return bus->ops->get_ramsize(bus->dev);
++}
++
++static inline
++int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
++{
++ if (!bus->ops->get_memdump)
++ return -EOPNOTSUPP;
++
++ return bus->ops->get_memdump(bus->dev, data, len);
++}
++
+ /*
+ * interface functions from common layer
+ */
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -957,8 +957,8 @@ int brcmf_attach(struct device *dev)
+ drvr->bus_if = dev_get_drvdata(dev);
+ drvr->bus_if->drvr = drvr;
+
+- /* create device debugfs folder */
+- brcmf_debugfs_attach(drvr);
++ /* attach debug facilities */
++ brcmf_debug_attach(drvr);
+
+ /* Attach and link in the protocol */
+ ret = brcmf_proto_attach(drvr);
+@@ -1155,7 +1155,7 @@ void brcmf_detach(struct device *dev)
+
+ brcmf_proto_detach(drvr);
+
+- brcmf_debugfs_detach(drvr);
++ brcmf_debug_detach(drvr);
+ bus_if->drvr = NULL;
+ kfree(drvr);
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
+@@ -16,15 +16,45 @@
+ #include <linux/debugfs.h>
+ #include <linux/netdevice.h>
+ #include <linux/module.h>
++#include <linux/devcoredump.h>
+
+ #include <brcmu_wifi.h>
+ #include <brcmu_utils.h>
+ #include "core.h"
+ #include "bus.h"
++#include "fweh.h"
+ #include "debug.h"
+
+ static struct dentry *root_folder;
+
++static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
++ size_t len)
++{
++ void *dump;
++ size_t ramsize;
++
++ ramsize = brcmf_bus_get_ramsize(bus);
++ if (ramsize) {
++ dump = vzalloc(len + ramsize);
++ if (!dump)
++ return -ENOMEM;
++ memcpy(dump, data, len);
++ brcmf_bus_get_memdump(bus, dump + len, ramsize);
++ dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL);
++ }
++ return 0;
++}
++
++static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp,
++ const struct brcmf_event_msg *evtmsg,
++ void *data)
++{
++ brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
++
++ return brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
++ evtmsg->datalen);
++}
++
+ void brcmf_debugfs_init(void)
+ {
+ root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
+@@ -41,7 +71,7 @@ void brcmf_debugfs_exit(void)
+ root_folder = NULL;
+ }
+
+-int brcmf_debugfs_attach(struct brcmf_pub *drvr)
++int brcmf_debug_attach(struct brcmf_pub *drvr)
+ {
+ struct device *dev = drvr->bus_if->dev;
+
+@@ -49,12 +79,18 @@ int brcmf_debugfs_attach(struct brcmf_pu
+ return -ENODEV;
+
+ drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
++ if (IS_ERR(drvr->dbgfs_dir))
++ return PTR_ERR(drvr->dbgfs_dir);
+
+- return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
++
++ return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
++ brcmf_debug_psm_watchdog_notify);
+ }
+
+-void brcmf_debugfs_detach(struct brcmf_pub *drvr)
++void brcmf_debug_detach(struct brcmf_pub *drvr)
+ {
++ brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG);
++
+ if (!IS_ERR_OR_NULL(drvr->dbgfs_dir))
+ debugfs_remove_recursive(drvr->dbgfs_dir);
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h
+@@ -109,8 +109,8 @@ struct brcmf_pub;
+ #ifdef DEBUG
+ void brcmf_debugfs_init(void);
+ void brcmf_debugfs_exit(void);
+-int brcmf_debugfs_attach(struct brcmf_pub *drvr);
+-void brcmf_debugfs_detach(struct brcmf_pub *drvr);
++int brcmf_debug_attach(struct brcmf_pub *drvr);
++void brcmf_debug_detach(struct brcmf_pub *drvr);
+ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
+ int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data));
+@@ -121,11 +121,11 @@ static inline void brcmf_debugfs_init(vo
+ static inline void brcmf_debugfs_exit(void)
+ {
+ }
+-static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
++static inline int brcmf_debug_attach(struct brcmf_pub *drvr)
+ {
+ return 0;
+ }
+-static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
++static inline void brcmf_debug_detach(struct brcmf_pub *drvr)
+ {
+ }
+ static inline
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -448,6 +448,47 @@ brcmf_pcie_copy_mem_todev(struct brcmf_p
+ }
+
+
++static void
++brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
++ void *dstaddr, u32 len)
++{
++ void __iomem *address = devinfo->tcm + mem_offset;
++ __le32 *dst32;
++ __le16 *dst16;
++ u8 *dst8;
++
++ if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) {
++ if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) {
++ dst8 = (u8 *)dstaddr;
++ while (len) {
++ *dst8 = ioread8(address);
++ address++;
++ dst8++;
++ len--;
++ }
++ } else {
++ len = len / 2;
++ dst16 = (__le16 *)dstaddr;
++ while (len) {
++ *dst16 = cpu_to_le16(ioread16(address));
++ address += 2;
++ dst16++;
++ len--;
++ }
++ }
++ } else {
++ len = len / 4;
++ dst32 = (__le32 *)dstaddr;
++ while (len) {
++ *dst32 = cpu_to_le32(ioread32(address));
++ address += 4;
++ dst32++;
++ len--;
++ }
++ }
++}
++
++
+ #define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \
+ CHIPCREGOFFS(reg), value)
+
+@@ -1352,12 +1393,36 @@ static void brcmf_pcie_wowl_config(struc
+ }
+
+
++static size_t brcmf_pcie_get_ramsize(struct device *dev)
++{
++ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
++ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
++ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
++
++ return devinfo->ci->ramsize - devinfo->ci->srsize;
++}
++
++
++static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
++{
++ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
++ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
++ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
++
++ brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len);
++ brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len);
++ return 0;
++}
++
++
+ static struct brcmf_bus_ops brcmf_pcie_bus_ops = {
+ .txdata = brcmf_pcie_tx,
+ .stop = brcmf_pcie_down,
+ .txctl = brcmf_pcie_tx_ctlpkt,
+ .rxctl = brcmf_pcie_rx_ctlpkt,
+ .wowl_config = brcmf_pcie_wowl_config,
++ .get_ramsize = brcmf_pcie_get_ramsize,
++ .get_memdump = brcmf_pcie_get_memdump,
+ };
+
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -3539,6 +3539,51 @@ done:
+ return err;
+ }
+
++static size_t brcmf_sdio_bus_get_ramsize(struct device *dev)
++{
++ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
++ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
++ struct brcmf_sdio *bus = sdiodev->bus;
++
++ return bus->ci->ramsize - bus->ci->srsize;
++}
++
++static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data,
++ size_t mem_size)
++{
++ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
++ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
++ struct brcmf_sdio *bus = sdiodev->bus;
++ int err;
++ int address;
++ int offset;
++ int len;
++
++ brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase,
++ mem_size);
++
++ address = bus->ci->rambase;
++ offset = err = 0;
++ sdio_claim_host(sdiodev->func[1]);
++ while (offset < mem_size) {
++ len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK :
++ mem_size - offset;
++ err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len);
++ if (err) {
++ brcmf_err("error %d on reading %d membytes at 0x%08x\n",
++ err, len, address);
++ goto done;
++ }
++ data += len;
++ offset += len;
++ address += len;
++ }
++
++done:
++ sdio_release_host(sdiodev->func[1]);
++ return err;
++}
++
+ void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus)
+ {
+ if (!bus->dpc_triggered) {
+@@ -3987,7 +4032,9 @@ static struct brcmf_bus_ops brcmf_sdio_b
+ .txctl = brcmf_sdio_bus_txctl,
+ .rxctl = brcmf_sdio_bus_rxctl,
+ .gettxq = brcmf_sdio_bus_gettxq,
+- .wowl_config = brcmf_sdio_wowl_config
++ .wowl_config = brcmf_sdio_wowl_config,
++ .get_ramsize = brcmf_sdio_bus_get_ramsize,
++ .get_memdump = brcmf_sdio_bus_get_memdump,
+ };
+
+ static void brcmf_sdio_firmware_callback(struct device *dev,