5617d141b3dea1147e5f2be3de9c672e03c159ea
[oweals/openwrt.git] /
1 From 4684997d9eea29380000e062755aa6d368d789a3 Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
3 Date: Tue, 26 Feb 2019 14:11:19 +0100
4 Subject: [PATCH] brcmfmac: reset PCIe bus on a firmware crash
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
8
9 This includes bus reset & reloading a firmware. It should be sufficient
10 for a user space to (setup and) use a wireless device again.
11
12 Support for reset on USB & SDIO can be added later.
13
14 Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
15 Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com>
16 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
17 ---
18  .../broadcom/brcm80211/brcmfmac/bus.h         | 10 ++++++
19  .../broadcom/brcm80211/brcmfmac/core.c        | 12 +++++++
20  .../broadcom/brcm80211/brcmfmac/core.h        |  2 ++
21  .../broadcom/brcm80211/brcmfmac/pcie.c        | 35 +++++++++++++++++++
22  4 files changed, 59 insertions(+)
23
24 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
25 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
26 @@ -91,6 +91,7 @@ struct brcmf_bus_ops {
27         int (*get_fwname)(struct device *dev, const char *ext,
28                           unsigned char *fw_name);
29         void (*debugfs_create)(struct device *dev);
30 +       int (*reset)(struct device *dev);
31  };
32  
33  
34 @@ -245,6 +246,15 @@ void brcmf_bus_debugfs_create(struct brc
35         return bus->ops->debugfs_create(bus->dev);
36  }
37  
38 +static inline
39 +int brcmf_bus_reset(struct brcmf_bus *bus)
40 +{
41 +       if (!bus->ops->reset)
42 +               return -EOPNOTSUPP;
43 +
44 +       return bus->ops->reset(bus->dev);
45 +}
46 +
47  /*
48   * interface functions from common layer
49   */
50 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
51 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
52 @@ -1105,6 +1105,14 @@ static int brcmf_revinfo_read(struct seq
53         return 0;
54  }
55  
56 +static void brcmf_core_bus_reset(struct work_struct *work)
57 +{
58 +       struct brcmf_pub *drvr = container_of(work, struct brcmf_pub,
59 +                                             bus_reset);
60 +
61 +       brcmf_bus_reset(drvr->bus_if);
62 +}
63 +
64  static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops)
65  {
66         int ret = -1;
67 @@ -1176,6 +1184,8 @@ static int brcmf_bus_started(struct brcm
68  #endif
69  #endif /* CONFIG_INET */
70  
71 +       INIT_WORK(&drvr->bus_reset, brcmf_core_bus_reset);
72 +
73         /* populate debugfs */
74         brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
75         brcmf_feat_debugfs_create(drvr);
76 @@ -1302,6 +1312,8 @@ void brcmf_fw_crashed(struct device *dev
77         bphy_err(drvr, "Firmware has halted or crashed\n");
78  
79         brcmf_dev_coredump(dev);
80 +
81 +       schedule_work(&drvr->bus_reset);
82  }
83  
84  void brcmf_detach(struct device *dev)
85 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
86 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
87 @@ -143,6 +143,8 @@ struct brcmf_pub {
88         struct notifier_block inet6addr_notifier;
89         struct brcmf_mp_device *settings;
90  
91 +       struct work_struct bus_reset;
92 +
93         u8 clmver[BRCMF_DCMD_SMLEN];
94  };
95  
96 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
97 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
98 @@ -345,6 +345,10 @@ static const u32 brcmf_ring_itemsize[BRC
99         BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE
100  };
101  
102 +static void brcmf_pcie_setup(struct device *dev, int ret,
103 +                            struct brcmf_fw_request *fwreq);
104 +static struct brcmf_fw_request *
105 +brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo);
106  
107  static u32
108  brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset)
109 @@ -1409,6 +1413,36 @@ int brcmf_pcie_get_fwname(struct device
110         return 0;
111  }
112  
113 +static int brcmf_pcie_reset(struct device *dev)
114 +{
115 +       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
116 +       struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
117 +       struct brcmf_pciedev_info *devinfo = buspub->devinfo;
118 +       struct brcmf_fw_request *fwreq;
119 +       int err;
120 +
121 +       brcmf_detach(dev);
122 +
123 +       brcmf_pcie_release_irq(devinfo);
124 +       brcmf_pcie_release_scratchbuffers(devinfo);
125 +       brcmf_pcie_release_ringbuffers(devinfo);
126 +       brcmf_pcie_reset_device(devinfo);
127 +
128 +       fwreq = brcmf_pcie_prepare_fw_request(devinfo);
129 +       if (!fwreq) {
130 +               dev_err(dev, "Failed to prepare FW request\n");
131 +               return -ENOMEM;
132 +       }
133 +
134 +       err = brcmf_fw_get_firmwares(dev, fwreq, brcmf_pcie_setup);
135 +       if (err) {
136 +               dev_err(dev, "Failed to prepare FW request\n");
137 +               kfree(fwreq);
138 +       }
139 +
140 +       return err;
141 +}
142 +
143  static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
144         .txdata = brcmf_pcie_tx,
145         .stop = brcmf_pcie_down,
146 @@ -1418,6 +1452,7 @@ static const struct brcmf_bus_ops brcmf_
147         .get_ramsize = brcmf_pcie_get_ramsize,
148         .get_memdump = brcmf_pcie_get_memdump,
149         .get_fwname = brcmf_pcie_get_fwname,
150 +       .reset = brcmf_pcie_reset,
151  };
152  
153