Commit 4684997d authored by Rafał Miłecki's avatar Rafał Miłecki Committed by Kalle Valo

brcmfmac: reset PCIe bus on a firmware crash

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: default avatarRafał Miłecki <rafal@milecki.pl>
Reviewed-by: default avatarArend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent a2ec87dd
...@@ -91,6 +91,7 @@ struct brcmf_bus_ops { ...@@ -91,6 +91,7 @@ struct brcmf_bus_ops {
int (*get_fwname)(struct device *dev, const char *ext, int (*get_fwname)(struct device *dev, const char *ext,
unsigned char *fw_name); unsigned char *fw_name);
void (*debugfs_create)(struct device *dev); void (*debugfs_create)(struct device *dev);
int (*reset)(struct device *dev);
}; };
...@@ -245,6 +246,15 @@ void brcmf_bus_debugfs_create(struct brcmf_bus *bus) ...@@ -245,6 +246,15 @@ void brcmf_bus_debugfs_create(struct brcmf_bus *bus)
return bus->ops->debugfs_create(bus->dev); return bus->ops->debugfs_create(bus->dev);
} }
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 * interface functions from common layer
*/ */
......
...@@ -1084,6 +1084,14 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data) ...@@ -1084,6 +1084,14 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data)
return 0; 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);
}
static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops) static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops)
{ {
int ret = -1; int ret = -1;
...@@ -1155,6 +1163,8 @@ static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops) ...@@ -1155,6 +1163,8 @@ static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops)
#endif #endif
#endif /* CONFIG_INET */ #endif /* CONFIG_INET */
INIT_WORK(&drvr->bus_reset, brcmf_core_bus_reset);
/* populate debugfs */ /* populate debugfs */
brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read); brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
brcmf_feat_debugfs_create(drvr); brcmf_feat_debugfs_create(drvr);
...@@ -1281,6 +1291,8 @@ void brcmf_fw_crashed(struct device *dev) ...@@ -1281,6 +1291,8 @@ void brcmf_fw_crashed(struct device *dev)
bphy_err(drvr, "Firmware has halted or crashed\n"); bphy_err(drvr, "Firmware has halted or crashed\n");
brcmf_dev_coredump(dev); brcmf_dev_coredump(dev);
schedule_work(&drvr->bus_reset);
} }
void brcmf_detach(struct device *dev) void brcmf_detach(struct device *dev)
......
...@@ -143,6 +143,8 @@ struct brcmf_pub { ...@@ -143,6 +143,8 @@ struct brcmf_pub {
struct notifier_block inet6addr_notifier; struct notifier_block inet6addr_notifier;
struct brcmf_mp_device *settings; struct brcmf_mp_device *settings;
struct work_struct bus_reset;
u8 clmver[BRCMF_DCMD_SMLEN]; u8 clmver[BRCMF_DCMD_SMLEN];
}; };
......
...@@ -345,6 +345,10 @@ static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = { ...@@ -345,6 +345,10 @@ static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = {
BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE
}; };
static void brcmf_pcie_setup(struct device *dev, int ret,
struct brcmf_fw_request *fwreq);
static struct brcmf_fw_request *
brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo);
static u32 static u32
brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset)
...@@ -1409,6 +1413,36 @@ int brcmf_pcie_get_fwname(struct device *dev, const char *ext, u8 *fw_name) ...@@ -1409,6 +1413,36 @@ int brcmf_pcie_get_fwname(struct device *dev, const char *ext, u8 *fw_name)
return 0; return 0;
} }
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;
struct brcmf_fw_request *fwreq;
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);
fwreq = brcmf_pcie_prepare_fw_request(devinfo);
if (!fwreq) {
dev_err(dev, "Failed to prepare FW request\n");
return -ENOMEM;
}
err = brcmf_fw_get_firmwares(dev, fwreq, brcmf_pcie_setup);
if (err) {
dev_err(dev, "Failed to prepare FW request\n");
kfree(fwreq);
}
return err;
}
static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
.txdata = brcmf_pcie_tx, .txdata = brcmf_pcie_tx,
.stop = brcmf_pcie_down, .stop = brcmf_pcie_down,
...@@ -1418,6 +1452,7 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { ...@@ -1418,6 +1452,7 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
.get_ramsize = brcmf_pcie_get_ramsize, .get_ramsize = brcmf_pcie_get_ramsize,
.get_memdump = brcmf_pcie_get_memdump, .get_memdump = brcmf_pcie_get_memdump,
.get_fwname = brcmf_pcie_get_fwname, .get_fwname = brcmf_pcie_get_fwname,
.reset = brcmf_pcie_reset,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment