From 4684997d9eea29380000e062755aa6d368d789a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 26 Feb 2019 14:11:19 +0100 Subject: [PATCH] brcmfmac: reset PCIe bus on a firmware crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This includes bus reset & reloading a firmware. It should be sufficient for a user space to (setup and) use a wireless device again. Support for reset on USB & SDIO can be added later. Signed-off-by: Rafał Miłecki Reviewed-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/bus.h | 10 ++++++ .../broadcom/brcm80211/brcmfmac/core.c | 12 +++++++ .../broadcom/brcm80211/brcmfmac/core.h | 2 ++ .../broadcom/brcm80211/brcmfmac/pcie.c | 35 +++++++++++++++++++ 4 files changed, 59 insertions(+) --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -87,6 +87,7 @@ struct brcmf_bus_ops { 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); + int (*reset)(struct device *dev); }; @@ -214,6 +215,15 @@ int brcmf_bus_get_memdump(struct brcmf_b return bus->ops->get_memdump(bus->dev, data, len); } +static inline +int brcmf_bus_reset(struct brcmf_bus *bus) +{ + if (!bus->ops->reset) + return -EOPNOTSUPP; + + return bus->ops->reset(bus->dev); +} + /* * interface functions from common layer */ --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1080,6 +1080,14 @@ static int brcmf_revinfo_read(struct seq return 0; } +static void brcmf_core_bus_reset(struct work_struct *work) +{ + struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, + bus_reset); + + brcmf_bus_reset(drvr->bus_if); +} + int brcmf_bus_started(struct device *dev) { int ret = -1; @@ -1161,6 +1169,8 @@ int brcmf_bus_started(struct device *dev #endif #endif /* CONFIG_INET */ + INIT_WORK(&drvr->bus_reset, brcmf_core_bus_reset); + return 0; fail: @@ -1282,6 +1292,8 @@ void brcmf_fw_crashed(struct device *dev bphy_err(drvr, "Firmware has halted or crashed\n"); brcmf_dev_coredump(dev); + + schedule_work(&drvr->bus_reset); } void brcmf_detach(struct device *dev) --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -146,6 +146,8 @@ struct brcmf_pub { struct notifier_block inet6addr_notifier; struct brcmf_mp_device *settings; + struct work_struct bus_reset; + /* Pointer needed by OpenWrt due to backporting some fixes */ void *cfg80211_ops; }; --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -343,6 +343,8 @@ static const u32 brcmf_ring_itemsize[BRC BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE }; +static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, + void *nvram, u32 nvram_len); static u32 brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) @@ -1382,6 +1384,45 @@ static int brcmf_pcie_get_memdump(struct } +static int brcmf_pcie_reset(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; + u16 domain_nr; + u16 bus_nr; + int err; + + brcmf_detach(dev); + + brcmf_pcie_release_irq(devinfo); + brcmf_pcie_release_scratchbuffers(devinfo); + brcmf_pcie_release_ringbuffers(devinfo); + brcmf_pcie_reset_device(devinfo); + + err = brcmf_fw_map_chip_to_name(devinfo->ci->chip, devinfo->ci->chiprev, + brcmf_pcie_fwnames, + ARRAY_SIZE(brcmf_pcie_fwnames), + devinfo->fw_name, devinfo->nvram_name); + if (err) { + dev_err(dev, "Failed to prepare FW request\n"); + return err; + } + + domain_nr = pci_domain_nr(devinfo->pdev->bus) + 1; + bus_nr = devinfo->pdev->bus->number; + err = brcmf_fw_get_firmwares_pcie(bus_if->dev, BRCMF_FW_REQUEST_NVRAM | + BRCMF_FW_REQ_NV_OPTIONAL, + devinfo->fw_name, devinfo->nvram_name, + brcmf_pcie_setup, domain_nr, bus_nr); + if (err) { + dev_err(dev, "Failed to prepare FW request\n"); + return err; + } + + return err; +} + static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { .txdata = brcmf_pcie_tx, .stop = brcmf_pcie_down, @@ -1390,6 +1431,7 @@ static const struct brcmf_bus_ops brcmf_ .wowl_config = brcmf_pcie_wowl_config, .get_ramsize = brcmf_pcie_get_ramsize, .get_memdump = brcmf_pcie_get_memdump, + .reset = brcmf_pcie_reset, };