1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/module.h>
18#include <linux/pci.h>
19#include <linux/moduleparam.h>
20#include <linux/interrupt.h>
21#include <linux/suspend.h>
22#include "wil6210.h"
23#include <linux/rtnetlink.h>
24
25static bool use_msi = true;
26module_param(use_msi, bool, 0444);
27MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
28
29#ifdef CONFIG_PM
30#ifdef CONFIG_PM_SLEEP
31static int wil6210_pm_notify(struct notifier_block *notify_block,
32 unsigned long mode, void *unused);
33#endif
34#endif
35
36static
37void wil_set_capabilities(struct wil6210_priv *wil)
38{
39 u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
40 u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
41 RGF_USER_REVISION_ID_MASK);
42
43 bitmap_zero(wil->hw_capabilities, hw_capability_last);
44 bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
45 wil->wil_fw_name = WIL_FW_NAME_DEFAULT;
46 wil->chip_revision = chip_revision;
47
48 switch (jtag_id) {
49 case JTAG_DEV_ID_SPARROW:
50 switch (chip_revision) {
51 case REVISION_ID_SPARROW_D0:
52 wil->hw_name = "Sparrow D0";
53 wil->hw_version = HW_VER_SPARROW_D0;
54 if (wil_fw_verify_file_exists(wil,
55 WIL_FW_NAME_SPARROW_PLUS))
56 wil->wil_fw_name = WIL_FW_NAME_SPARROW_PLUS;
57 break;
58 case REVISION_ID_SPARROW_B0:
59 wil->hw_name = "Sparrow B0";
60 wil->hw_version = HW_VER_SPARROW_B0;
61 break;
62 default:
63 wil->hw_name = "Unknown";
64 wil->hw_version = HW_VER_UNKNOWN;
65 break;
66 }
67 break;
68 default:
69 wil_err(wil, "Unknown board hardware, chip_id 0x%08x, chip_revision 0x%08x\n",
70 jtag_id, chip_revision);
71 wil->hw_name = "Unknown";
72 wil->hw_version = HW_VER_UNKNOWN;
73 }
74
75 wil_info(wil, "Board hardware is %s\n", wil->hw_name);
76
77
78 wil_request_firmware(wil, wil->wil_fw_name, false);
79}
80
81void wil_disable_irq(struct wil6210_priv *wil)
82{
83 disable_irq(wil->pdev->irq);
84}
85
86void wil_enable_irq(struct wil6210_priv *wil)
87{
88 enable_irq(wil->pdev->irq);
89}
90
91
92static int wil_if_pcie_enable(struct wil6210_priv *wil)
93{
94 struct pci_dev *pdev = wil->pdev;
95 int rc;
96
97
98
99
100 int msi_only = pdev->msi_enabled;
101 bool _use_msi = use_msi;
102 bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
103 wil->fw_capabilities);
104
105 wil_dbg_misc(wil, "if_pcie_enable, wmi_only %d\n", wmi_only);
106
107 pdev->msi_enabled = 0;
108
109 pci_set_master(pdev);
110
111 wil_dbg_misc(wil, "Setup %s interrupt\n", use_msi ? "MSI" : "INTx");
112
113 if (use_msi && pci_enable_msi(pdev)) {
114 wil_err(wil, "pci_enable_msi failed, use INTx\n");
115 _use_msi = false;
116 }
117
118 if (!_use_msi && msi_only) {
119 wil_err(wil, "Interrupt pin not routed, unable to use INTx\n");
120 rc = -ENODEV;
121 goto stop_master;
122 }
123
124 rc = wil6210_init_irq(wil, pdev->irq, _use_msi);
125 if (rc)
126 goto stop_master;
127
128
129
130
131 mutex_lock(&wil->mutex);
132 rc = wil_reset(wil, wmi_only);
133 mutex_unlock(&wil->mutex);
134 if (rc)
135 goto release_irq;
136
137 return 0;
138
139 release_irq:
140 wil6210_fini_irq(wil, pdev->irq);
141
142 pci_disable_msi(pdev);
143 stop_master:
144 pci_clear_master(pdev);
145 return rc;
146}
147
148static int wil_if_pcie_disable(struct wil6210_priv *wil)
149{
150 struct pci_dev *pdev = wil->pdev;
151
152 wil_dbg_misc(wil, "if_pcie_disable\n");
153
154 pci_clear_master(pdev);
155
156 wil6210_fini_irq(wil, pdev->irq);
157
158 pci_disable_msi(pdev);
159
160
161 return 0;
162}
163
164static int wil_platform_rop_ramdump(void *wil_handle, void *buf, uint32_t size)
165{
166 struct wil6210_priv *wil = wil_handle;
167
168 if (!wil)
169 return -EINVAL;
170
171 return wil_fw_copy_crash_dump(wil, buf, size);
172}
173
174static int wil_platform_rop_fw_recovery(void *wil_handle)
175{
176 struct wil6210_priv *wil = wil_handle;
177
178 if (!wil)
179 return -EINVAL;
180
181 wil_fw_error_recovery(wil);
182
183 return 0;
184}
185
186static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
187{
188 struct wil6210_priv *wil;
189 struct device *dev = &pdev->dev;
190 int rc;
191 const struct wil_platform_rops rops = {
192 .ramdump = wil_platform_rop_ramdump,
193 .fw_recovery = wil_platform_rop_fw_recovery,
194 };
195
196
197 dev_info(&pdev->dev, WIL_NAME
198 " device found [%04x:%04x] (rev %x)\n",
199 (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
200
201 if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
202 dev_err(&pdev->dev, "Not " WIL_NAME "? "
203 "BAR0 size is %lu while expecting %lu\n",
204 (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE);
205 return -ENODEV;
206 }
207
208 wil = wil_if_alloc(dev);
209 if (IS_ERR(wil)) {
210 rc = (int)PTR_ERR(wil);
211 dev_err(dev, "wil_if_alloc failed: %d\n", rc);
212 return rc;
213 }
214 wil->pdev = pdev;
215 pci_set_drvdata(pdev, wil);
216
217
218 wil->platform_handle =
219 wil_platform_init(&pdev->dev, &wil->platform_ops, &rops, wil);
220 if (!wil->platform_handle) {
221 rc = -ENODEV;
222 wil_err(wil, "wil_platform_init failed\n");
223 goto if_free;
224 }
225
226
227 rc = pci_enable_device(pdev);
228 if (rc) {
229 wil_err(wil,
230 "pci_enable_device failed, retry with MSI only\n");
231
232
233
234 pdev->msi_enabled = 1;
235 rc = pci_enable_device(pdev);
236 }
237 if (rc) {
238 wil_err(wil,
239 "pci_enable_device failed, even with MSI only\n");
240 goto err_plat;
241 }
242
243
244 rc = pci_request_region(pdev, 0, WIL_NAME);
245 if (rc) {
246 wil_err(wil, "pci_request_region failed\n");
247 goto err_disable_pdev;
248 }
249
250
251 wil->csr = pci_ioremap_bar(pdev, 0);
252 if (!wil->csr) {
253 wil_err(wil, "pci_ioremap_bar failed\n");
254 rc = -ENODEV;
255 goto err_release_reg;
256 }
257
258 wil_info(wil, "CSR at %pR -> 0x%p\n", &pdev->resource[0], wil->csr);
259
260 wil_set_capabilities(wil);
261 wil6210_clear_irq(wil);
262
263
264 rc = wil_if_pcie_enable(wil);
265 if (rc) {
266 wil_err(wil, "Enable device failed\n");
267 goto err_iounmap;
268 }
269
270
271 rc = wil_if_add(wil);
272 if (rc) {
273 wil_err(wil, "wil_if_add failed: %d\n", rc);
274 goto bus_disable;
275 }
276
277#ifdef CONFIG_PM
278#ifdef CONFIG_PM_SLEEP
279 wil->pm_notify.notifier_call = wil6210_pm_notify;
280 rc = register_pm_notifier(&wil->pm_notify);
281 if (rc)
282
283
284
285 wil_err(wil, "register_pm_notifier failed: %d\n", rc);
286#endif
287#endif
288
289 wil6210_debugfs_init(wil);
290
291
292 return 0;
293
294bus_disable:
295 wil_if_pcie_disable(wil);
296err_iounmap:
297 pci_iounmap(pdev, wil->csr);
298err_release_reg:
299 pci_release_region(pdev, 0);
300err_disable_pdev:
301 pci_disable_device(pdev);
302err_plat:
303 if (wil->platform_ops.uninit)
304 wil->platform_ops.uninit(wil->platform_handle);
305if_free:
306 wil_if_free(wil);
307
308 return rc;
309}
310
311static void wil_pcie_remove(struct pci_dev *pdev)
312{
313 struct wil6210_priv *wil = pci_get_drvdata(pdev);
314 void __iomem *csr = wil->csr;
315
316 wil_dbg_misc(wil, "pcie_remove\n");
317
318#ifdef CONFIG_PM
319#ifdef CONFIG_PM_SLEEP
320 unregister_pm_notifier(&wil->pm_notify);
321#endif
322#endif
323
324 wil6210_debugfs_remove(wil);
325 rtnl_lock();
326 wil_p2p_wdev_free(wil);
327 rtnl_unlock();
328 wil_if_remove(wil);
329 wil_if_pcie_disable(wil);
330 pci_iounmap(pdev, csr);
331 pci_release_region(pdev, 0);
332 pci_disable_device(pdev);
333 if (wil->platform_ops.uninit)
334 wil->platform_ops.uninit(wil->platform_handle);
335 wil_if_free(wil);
336}
337
338static const struct pci_device_id wil6210_pcie_ids[] = {
339 { PCI_DEVICE(0x1ae9, 0x0310) },
340 { PCI_DEVICE(0x1ae9, 0x0302) },
341 { },
342};
343MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
344
345#ifdef CONFIG_PM
346#ifdef CONFIG_PM_SLEEP
347
348static int wil6210_suspend(struct device *dev, bool is_runtime)
349{
350 int rc = 0;
351 struct pci_dev *pdev = to_pci_dev(dev);
352 struct wil6210_priv *wil = pci_get_drvdata(pdev);
353
354 wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
355
356 rc = wil_can_suspend(wil, is_runtime);
357 if (rc)
358 goto out;
359
360 rc = wil_suspend(wil, is_runtime);
361 if (rc)
362 goto out;
363
364
365
366
367 pci_clear_master(pdev);
368
369
370out:
371 return rc;
372}
373
374static int wil6210_resume(struct device *dev, bool is_runtime)
375{
376 int rc = 0;
377 struct pci_dev *pdev = to_pci_dev(dev);
378 struct wil6210_priv *wil = pci_get_drvdata(pdev);
379
380 wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
381
382
383 pci_set_master(pdev);
384
385 rc = wil_resume(wil, is_runtime);
386 if (rc)
387 pci_clear_master(pdev);
388
389 return rc;
390}
391
392static int wil6210_pm_notify(struct notifier_block *notify_block,
393 unsigned long mode, void *unused)
394{
395 struct wil6210_priv *wil = container_of(
396 notify_block, struct wil6210_priv, pm_notify);
397 int rc = 0;
398 enum wil_platform_event evt;
399
400 wil_dbg_pm(wil, "pm_notify: mode (%ld)\n", mode);
401
402 switch (mode) {
403 case PM_HIBERNATION_PREPARE:
404 case PM_SUSPEND_PREPARE:
405 case PM_RESTORE_PREPARE:
406 rc = wil_can_suspend(wil, false);
407 if (rc)
408 break;
409 evt = WIL_PLATFORM_EVT_PRE_SUSPEND;
410 if (wil->platform_ops.notify)
411 rc = wil->platform_ops.notify(wil->platform_handle,
412 evt);
413 break;
414 case PM_POST_SUSPEND:
415 case PM_POST_HIBERNATION:
416 case PM_POST_RESTORE:
417 evt = WIL_PLATFORM_EVT_POST_SUSPEND;
418 if (wil->platform_ops.notify)
419 rc = wil->platform_ops.notify(wil->platform_handle,
420 evt);
421 break;
422 default:
423 wil_dbg_pm(wil, "unhandled notify mode %ld\n", mode);
424 break;
425 }
426
427 wil_dbg_pm(wil, "notification mode %ld: rc (%d)\n", mode, rc);
428 return rc;
429}
430
431static int wil6210_pm_suspend(struct device *dev)
432{
433 return wil6210_suspend(dev, false);
434}
435
436static int wil6210_pm_resume(struct device *dev)
437{
438 return wil6210_resume(dev, false);
439}
440#endif
441
442#endif
443
444static const struct dev_pm_ops wil6210_pm_ops = {
445 SET_SYSTEM_SLEEP_PM_OPS(wil6210_pm_suspend, wil6210_pm_resume)
446};
447
448static struct pci_driver wil6210_driver = {
449 .probe = wil_pcie_probe,
450 .remove = wil_pcie_remove,
451 .id_table = wil6210_pcie_ids,
452 .name = WIL_NAME,
453 .driver = {
454 .pm = &wil6210_pm_ops,
455 },
456};
457
458static int __init wil6210_driver_init(void)
459{
460 int rc;
461
462 rc = wil_platform_modinit();
463 if (rc)
464 return rc;
465
466 rc = pci_register_driver(&wil6210_driver);
467 if (rc)
468 wil_platform_modexit();
469 return rc;
470}
471module_init(wil6210_driver_init);
472
473static void __exit wil6210_driver_exit(void)
474{
475 pci_unregister_driver(&wil6210_driver);
476 wil_platform_modexit();
477}
478module_exit(wil6210_driver_exit);
479
480MODULE_LICENSE("Dual BSD/GPL");
481MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>");
482MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card");
483