1 From: Arend van Spriel <arend@broadcom.com>
2 Date: Thu, 8 Oct 2015 20:33:11 +0200
3 Subject: [PATCH] brcmfmac: expose device memory to devcoredump subsystem
5 Upon PSM watchdog event received from firmware the driver will obtain
6 a memory snapshot of the device and expose it to user-space through
7 the devcoredump framework. This will trigger a uevent.
9 Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
10 Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
11 Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
12 Signed-off-by: Arend van Spriel <arend@broadcom.com>
13 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
16 --- a/drivers/net/wireless/brcm80211/Kconfig
17 +++ b/drivers/net/wireless/brcm80211/Kconfig
18 @@ -85,5 +85,6 @@ config BRCM_TRACING
20 bool "Broadcom driver debug functions"
21 depends on BRCMSMAC || BRCMFMAC
22 + select WANT_DEV_COREDUMP
24 Selecting this enables additional code for debug purposes.
25 --- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h
26 +++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
27 @@ -65,6 +65,8 @@ struct brcmf_bus_dcmd {
28 * @rxctl: receive a control response message from dongle.
29 * @gettxq: obtain a reference of bus transmit queue (optional).
30 * @wowl_config: specify if dongle is configured for wowl when going to suspend
31 + * @get_ramsize: obtain size of device memory.
32 + * @get_memdump: obtain device memory dump in provided buffer.
34 * This structure provides an abstract interface towards the
35 * bus specific driver. For control messages to common driver
36 @@ -79,6 +81,8 @@ struct brcmf_bus_ops {
37 int (*rxctl)(struct device *dev, unsigned char *msg, uint len);
38 struct pktq * (*gettxq)(struct device *dev);
39 void (*wowl_config)(struct device *dev, bool enabled);
40 + size_t (*get_ramsize)(struct device *dev);
41 + int (*get_memdump)(struct device *dev, void *data, size_t len);
45 @@ -185,6 +189,23 @@ void brcmf_bus_wowl_config(struct brcmf_
46 bus->ops->wowl_config(bus->dev, enabled);
49 +static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus)
51 + if (!bus->ops->get_ramsize)
54 + return bus->ops->get_ramsize(bus->dev);
58 +int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
60 + if (!bus->ops->get_memdump)
63 + return bus->ops->get_memdump(bus->dev, data, len);
67 * interface functions from common layer
69 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
70 +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
71 @@ -957,8 +957,8 @@ int brcmf_attach(struct device *dev)
72 drvr->bus_if = dev_get_drvdata(dev);
73 drvr->bus_if->drvr = drvr;
75 - /* create device debugfs folder */
76 - brcmf_debugfs_attach(drvr);
77 + /* attach debug facilities */
78 + brcmf_debug_attach(drvr);
80 /* Attach and link in the protocol */
81 ret = brcmf_proto_attach(drvr);
82 @@ -1155,7 +1155,7 @@ void brcmf_detach(struct device *dev)
84 brcmf_proto_detach(drvr);
86 - brcmf_debugfs_detach(drvr);
87 + brcmf_debug_detach(drvr);
91 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c
92 +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
94 #include <linux/debugfs.h>
95 #include <linux/netdevice.h>
96 #include <linux/module.h>
97 +#include <linux/devcoredump.h>
99 #include <brcmu_wifi.h>
100 #include <brcmu_utils.h>
106 static struct dentry *root_folder;
108 +static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
114 + ramsize = brcmf_bus_get_ramsize(bus);
116 + dump = vzalloc(len + ramsize);
119 + memcpy(dump, data, len);
120 + brcmf_bus_get_memdump(bus, dump + len, ramsize);
121 + dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL);
126 +static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp,
127 + const struct brcmf_event_msg *evtmsg,
130 + brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
132 + return brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
136 void brcmf_debugfs_init(void)
138 root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
139 @@ -41,7 +71,7 @@ void brcmf_debugfs_exit(void)
143 -int brcmf_debugfs_attach(struct brcmf_pub *drvr)
144 +int brcmf_debug_attach(struct brcmf_pub *drvr)
146 struct device *dev = drvr->bus_if->dev;
148 @@ -49,12 +79,18 @@ int brcmf_debugfs_attach(struct brcmf_pu
151 drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
152 + if (IS_ERR(drvr->dbgfs_dir))
153 + return PTR_ERR(drvr->dbgfs_dir);
155 - return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
157 + return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
158 + brcmf_debug_psm_watchdog_notify);
161 -void brcmf_debugfs_detach(struct brcmf_pub *drvr)
162 +void brcmf_debug_detach(struct brcmf_pub *drvr)
164 + brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG);
166 if (!IS_ERR_OR_NULL(drvr->dbgfs_dir))
167 debugfs_remove_recursive(drvr->dbgfs_dir);
169 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h
170 +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h
171 @@ -109,8 +109,8 @@ struct brcmf_pub;
173 void brcmf_debugfs_init(void);
174 void brcmf_debugfs_exit(void);
175 -int brcmf_debugfs_attach(struct brcmf_pub *drvr);
176 -void brcmf_debugfs_detach(struct brcmf_pub *drvr);
177 +int brcmf_debug_attach(struct brcmf_pub *drvr);
178 +void brcmf_debug_detach(struct brcmf_pub *drvr);
179 struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
180 int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
181 int (*read_fn)(struct seq_file *seq, void *data));
182 @@ -121,11 +121,11 @@ static inline void brcmf_debugfs_init(vo
183 static inline void brcmf_debugfs_exit(void)
186 -static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
187 +static inline int brcmf_debug_attach(struct brcmf_pub *drvr)
191 -static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
192 +static inline void brcmf_debug_detach(struct brcmf_pub *drvr)
196 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
197 +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
198 @@ -448,6 +448,47 @@ brcmf_pcie_copy_mem_todev(struct brcmf_p
203 +brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
204 + void *dstaddr, u32 len)
206 + void __iomem *address = devinfo->tcm + mem_offset;
211 + if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) {
212 + if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) {
213 + dst8 = (u8 *)dstaddr;
215 + *dst8 = ioread8(address);
222 + dst16 = (__le16 *)dstaddr;
224 + *dst16 = cpu_to_le16(ioread16(address));
232 + dst32 = (__le32 *)dstaddr;
234 + *dst32 = cpu_to_le32(ioread32(address));
243 #define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \
244 CHIPCREGOFFS(reg), value)
246 @@ -1352,12 +1393,36 @@ static void brcmf_pcie_wowl_config(struc
250 +static size_t brcmf_pcie_get_ramsize(struct device *dev)
252 + struct brcmf_bus *bus_if = dev_get_drvdata(dev);
253 + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
254 + struct brcmf_pciedev_info *devinfo = buspub->devinfo;
256 + return devinfo->ci->ramsize - devinfo->ci->srsize;
260 +static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
262 + struct brcmf_bus *bus_if = dev_get_drvdata(dev);
263 + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
264 + struct brcmf_pciedev_info *devinfo = buspub->devinfo;
266 + brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len);
267 + brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len);
272 static struct brcmf_bus_ops brcmf_pcie_bus_ops = {
273 .txdata = brcmf_pcie_tx,
274 .stop = brcmf_pcie_down,
275 .txctl = brcmf_pcie_tx_ctlpkt,
276 .rxctl = brcmf_pcie_rx_ctlpkt,
277 .wowl_config = brcmf_pcie_wowl_config,
278 + .get_ramsize = brcmf_pcie_get_ramsize,
279 + .get_memdump = brcmf_pcie_get_memdump,
283 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
284 +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
285 @@ -3539,6 +3539,51 @@ done:
289 +static size_t brcmf_sdio_bus_get_ramsize(struct device *dev)
291 + struct brcmf_bus *bus_if = dev_get_drvdata(dev);
292 + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
293 + struct brcmf_sdio *bus = sdiodev->bus;
295 + return bus->ci->ramsize - bus->ci->srsize;
298 +static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data,
301 + struct brcmf_bus *bus_if = dev_get_drvdata(dev);
302 + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
303 + struct brcmf_sdio *bus = sdiodev->bus;
309 + brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase,
312 + address = bus->ci->rambase;
314 + sdio_claim_host(sdiodev->func[1]);
315 + while (offset < mem_size) {
316 + len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK :
318 + err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len);
320 + brcmf_err("error %d on reading %d membytes at 0x%08x\n",
321 + err, len, address);
330 + sdio_release_host(sdiodev->func[1]);
334 void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus)
336 if (!bus->dpc_triggered) {
337 @@ -3987,7 +4032,9 @@ static struct brcmf_bus_ops brcmf_sdio_b
338 .txctl = brcmf_sdio_bus_txctl,
339 .rxctl = brcmf_sdio_bus_rxctl,
340 .gettxq = brcmf_sdio_bus_gettxq,
341 - .wowl_config = brcmf_sdio_wowl_config
342 + .wowl_config = brcmf_sdio_wowl_config,
343 + .get_ramsize = brcmf_sdio_bus_get_ramsize,
344 + .get_memdump = brcmf_sdio_bus_get_memdump,
347 static void brcmf_sdio_firmware_callback(struct device *dev,