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: Rafał Miłecki <rafal@milecki.pl> Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
parent
a2ec87ddbf
commit
4684997d9e
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
#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)
|
|||||||
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 {
|
|||||||
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] = {
|
|||||||
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)
|
|||||||
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 = {
|
|||||||
.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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user