blob: 7b5c4222bc33e1bb5d533fb002dcbbc64d2d0766 [file] [log] [blame]
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001/*
Maya Ereze34dc642016-05-16 22:23:34 +03002 * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08003 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080017#include <linux/module.h>
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080018#include <linux/pci.h>
19#include <linux/moduleparam.h>
Vladimir Kondratiev73d839a2014-09-10 16:34:50 +030020#include <linux/interrupt.h>
Maya Ereze34dc642016-05-16 22:23:34 +030021#include <linux/suspend.h>
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080022#include "wil6210.h"
23
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030024static bool use_msi = true;
25module_param(use_msi, bool, S_IRUGO);
26MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080027
Maya Ereze34dc642016-05-16 22:23:34 +030028#ifdef CONFIG_PM
29#ifdef CONFIG_PM_SLEEP
30static int wil6210_pm_notify(struct notifier_block *notify_block,
31 unsigned long mode, void *unused);
32#endif /* CONFIG_PM_SLEEP */
33#endif /* CONFIG_PM */
34
Vladimir Kondratievd8cfb802014-12-23 09:47:17 +020035static
36void wil_set_capabilities(struct wil6210_priv *wil)
37{
Vladimir Kondratievb9eeb512015-07-30 13:52:03 +030038 u32 rev_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
Vladimir Kondratievd8cfb802014-12-23 09:47:17 +020039
40 bitmap_zero(wil->hw_capabilities, hw_capability_last);
41
42 switch (rev_id) {
Vladimir Kondratievd8cfb802014-12-23 09:47:17 +020043 case JTAG_DEV_ID_SPARROW_B0:
Vladimir Kondratiev1aeda132014-12-23 09:47:18 +020044 wil->hw_name = "Sparrow B0";
Vladimir Kondratievd8cfb802014-12-23 09:47:17 +020045 wil->hw_version = HW_VER_SPARROW_B0;
46 break;
47 default:
48 wil_err(wil, "Unknown board hardware 0x%08x\n", rev_id);
Vladimir Kondratiev1aeda132014-12-23 09:47:18 +020049 wil->hw_name = "Unknown";
Vladimir Kondratievd8cfb802014-12-23 09:47:17 +020050 wil->hw_version = HW_VER_UNKNOWN;
51 }
Vladimir Kondratiev1aeda132014-12-23 09:47:18 +020052
53 wil_info(wil, "Board hardware is %s\n", wil->hw_name);
Vladimir Kondratievd8cfb802014-12-23 09:47:17 +020054}
55
Vladimir Kondratiev73d839a2014-09-10 16:34:50 +030056void wil_disable_irq(struct wil6210_priv *wil)
57{
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030058 disable_irq(wil->pdev->irq);
Vladimir Kondratiev73d839a2014-09-10 16:34:50 +030059}
60
61void wil_enable_irq(struct wil6210_priv *wil)
62{
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030063 enable_irq(wil->pdev->irq);
Vladimir Kondratiev73d839a2014-09-10 16:34:50 +030064}
65
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080066/* Bus ops */
67static int wil_if_pcie_enable(struct wil6210_priv *wil)
68{
69 struct pci_dev *pdev = wil->pdev;
70 int rc;
Vladimir Kondratiev2bdc0702014-06-16 19:37:20 +030071 /* on platforms with buggy ACPI, pdev->msi_enabled may be set to
72 * allow pci_enable_device to work. This indicates INTx was not routed
73 * and only MSI should be used
74 */
75 int msi_only = pdev->msi_enabled;
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030076 bool _use_msi = use_msi;
Vladimir Kondratiev2bdc0702014-06-16 19:37:20 +030077
Vladimir Kondratiev9cf10d62014-09-10 16:34:36 +030078 wil_dbg_misc(wil, "%s()\n", __func__);
79
Vladimir Kondratiev2bdc0702014-06-16 19:37:20 +030080 pdev->msi_enabled = 0;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080081
82 pci_set_master(pdev);
83
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030084 wil_dbg_misc(wil, "Setup %s interrupt\n", use_msi ? "MSI" : "INTx");
Alexander Gordeevb4b39062014-02-18 11:12:04 +010085
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030086 if (use_msi && pci_enable_msi(pdev)) {
Alexander Gordeevb4b39062014-02-18 11:12:04 +010087 wil_err(wil, "pci_enable_msi failed, use INTx\n");
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030088 _use_msi = false;
Alexander Gordeevb4b39062014-02-18 11:12:04 +010089 }
90
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030091 if (!_use_msi && msi_only) {
Vladimir Kondratiev2bdc0702014-06-16 19:37:20 +030092 wil_err(wil, "Interrupt pin not routed, unable to use INTx\n");
93 rc = -ENODEV;
94 goto stop_master;
95 }
96
Vladimir Kondratievbd2d18b2015-07-30 13:52:02 +030097 rc = wil6210_init_irq(wil, pdev->irq, _use_msi);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080098 if (rc)
99 goto stop_master;
100
101 /* need reset here to obtain MAC */
Vladimir Kondratiev097638a2014-03-17 15:34:25 +0200102 mutex_lock(&wil->mutex);
Vladimir Kondratiev2cd0f022015-02-15 14:02:30 +0200103 rc = wil_reset(wil, false);
Vladimir Kondratiev097638a2014-03-17 15:34:25 +0200104 mutex_unlock(&wil->mutex);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800105 if (rc)
106 goto release_irq;
107
108 return 0;
109
110 release_irq:
111 wil6210_fini_irq(wil, pdev->irq);
112 /* safe to call if no MSI */
113 pci_disable_msi(pdev);
114 stop_master:
115 pci_clear_master(pdev);
116 return rc;
117}
118
119static int wil_if_pcie_disable(struct wil6210_priv *wil)
120{
121 struct pci_dev *pdev = wil->pdev;
122
Vladimir Kondratiev9cf10d62014-09-10 16:34:36 +0300123 wil_dbg_misc(wil, "%s()\n", __func__);
124
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800125 pci_clear_master(pdev);
126 /* disable and release IRQ */
127 wil6210_fini_irq(wil, pdev->irq);
128 /* safe to call if no MSI */
129 pci_disable_msi(pdev);
130 /* TODO: disable HW */
131
132 return 0;
133}
134
Lior Davidea3ade72015-12-16 17:51:46 +0200135static int wil_platform_rop_ramdump(void *wil_handle, void *buf, uint32_t size)
136{
137 struct wil6210_priv *wil = wil_handle;
138
139 if (!wil)
140 return -EINVAL;
141
142 return wil_fw_copy_crash_dump(wil, buf, size);
143}
144
145static int wil_platform_rop_fw_recovery(void *wil_handle)
146{
147 struct wil6210_priv *wil = wil_handle;
148
149 if (!wil)
150 return -EINVAL;
151
152 wil_fw_error_recovery(wil);
153
154 return 0;
155}
156
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800157static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
158{
159 struct wil6210_priv *wil;
160 struct device *dev = &pdev->dev;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800161 int rc;
Lior Davidea3ade72015-12-16 17:51:46 +0200162 const struct wil_platform_rops rops = {
163 .ramdump = wil_platform_rop_ramdump,
164 .fw_recovery = wil_platform_rop_fw_recovery,
165 };
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800166
167 /* check HW */
Vladimir Kondratiev65082812014-07-14 09:49:37 +0300168 dev_info(&pdev->dev, WIL_NAME
Vladimir Kondratiev1aeda132014-12-23 09:47:18 +0200169 " device found [%04x:%04x] (rev %x)\n",
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800170 (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
171
172 if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
173 dev_err(&pdev->dev, "Not " WIL_NAME "? "
174 "BAR0 size is %lu while expecting %lu\n",
175 (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE);
176 return -ENODEV;
177 }
178
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300179 wil = wil_if_alloc(dev);
180 if (IS_ERR(wil)) {
181 rc = (int)PTR_ERR(wil);
182 dev_err(dev, "wil_if_alloc failed: %d\n", rc);
183 return rc;
184 }
185 wil->pdev = pdev;
186 pci_set_drvdata(pdev, wil);
187 /* rollback to if_free */
188
189 wil->platform_handle =
Lior Davidea3ade72015-12-16 17:51:46 +0200190 wil_platform_init(&pdev->dev, &wil->platform_ops, &rops, wil);
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300191 if (!wil->platform_handle) {
192 rc = -ENODEV;
193 wil_err(wil, "wil_platform_init failed\n");
194 goto if_free;
195 }
196 /* rollback to err_plat */
197
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800198 rc = pci_enable_device(pdev);
199 if (rc) {
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300200 wil_err(wil,
Vladimir Kondratiev2bdc0702014-06-16 19:37:20 +0300201 "pci_enable_device failed, retry with MSI only\n");
202 /* Work around for platforms that can't allocate IRQ:
203 * retry with MSI only
204 */
205 pdev->msi_enabled = 1;
206 rc = pci_enable_device(pdev);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800207 }
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300208 if (rc) {
209 wil_err(wil,
210 "pci_enable_device failed, even with MSI only\n");
211 goto err_plat;
212 }
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800213 /* rollback to err_disable_pdev */
214
215 rc = pci_request_region(pdev, 0, WIL_NAME);
216 if (rc) {
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300217 wil_err(wil, "pci_request_region failed\n");
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800218 goto err_disable_pdev;
219 }
220 /* rollback to err_release_reg */
221
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300222 wil->csr = pci_ioremap_bar(pdev, 0);
223 if (!wil->csr) {
224 wil_err(wil, "pci_ioremap_bar failed\n");
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800225 rc = -ENODEV;
226 goto err_release_reg;
227 }
228 /* rollback to err_iounmap */
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300229 wil_info(wil, "CSR at %pR -> 0x%p\n", &pdev->resource[0], wil->csr);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800230
Vladimir Kondratievd8cfb802014-12-23 09:47:17 +0200231 wil_set_capabilities(wil);
Vladimir Kondratievf4b5a802014-03-17 15:34:13 +0200232 wil6210_clear_irq(wil);
Vladimir Kondratievf772ebf2014-09-10 16:34:35 +0300233
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800234 /* FW should raise IRQ when ready */
235 rc = wil_if_pcie_enable(wil);
236 if (rc) {
237 wil_err(wil, "Enable device failed\n");
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300238 goto err_iounmap;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800239 }
240 /* rollback to bus_disable */
241
242 rc = wil_if_add(wil);
243 if (rc) {
244 wil_err(wil, "wil_if_add failed: %d\n", rc);
245 goto bus_disable;
246 }
247
Maya Ereze34dc642016-05-16 22:23:34 +0300248#ifdef CONFIG_PM
249#ifdef CONFIG_PM_SLEEP
250 wil->pm_notify.notifier_call = wil6210_pm_notify;
251 rc = register_pm_notifier(&wil->pm_notify);
252 if (rc)
253 /* Do not fail the driver initialization, as suspend can
254 * be prevented in a later phase if needed
255 */
256 wil_err(wil, "register_pm_notifier failed: %d\n", rc);
257#endif /* CONFIG_PM_SLEEP */
258#endif /* CONFIG_PM */
259
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800260 wil6210_debugfs_init(wil);
261
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800262
263 return 0;
264
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300265bus_disable:
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800266 wil_if_pcie_disable(wil);
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300267err_iounmap:
268 pci_iounmap(pdev, wil->csr);
269err_release_reg:
270 pci_release_region(pdev, 0);
271err_disable_pdev:
272 pci_disable_device(pdev);
273err_plat:
Vladimir Kondratievf772ebf2014-09-10 16:34:35 +0300274 if (wil->platform_ops.uninit)
275 wil->platform_ops.uninit(wil->platform_handle);
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300276if_free:
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800277 wil_if_free(wil);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800278
279 return rc;
280}
281
282static void wil_pcie_remove(struct pci_dev *pdev)
283{
284 struct wil6210_priv *wil = pci_get_drvdata(pdev);
Vladimir Kondratiev560ce302014-08-06 10:32:01 +0300285 void __iomem *csr = wil->csr;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800286
Vladimir Kondratiev9cf10d62014-09-10 16:34:36 +0300287 wil_dbg_misc(wil, "%s()\n", __func__);
288
Maya Ereze34dc642016-05-16 22:23:34 +0300289#ifdef CONFIG_PM
290#ifdef CONFIG_PM_SLEEP
291 unregister_pm_notifier(&wil->pm_notify);
292#endif /* CONFIG_PM_SLEEP */
293#endif /* CONFIG_PM */
294
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800295 wil6210_debugfs_remove(wil);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800296 wil_if_remove(wil);
Dedy Lanskyf172b562014-09-10 16:34:38 +0300297 wil_if_pcie_disable(wil);
Vladimir Kondratiev560ce302014-08-06 10:32:01 +0300298 pci_iounmap(pdev, csr);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800299 pci_release_region(pdev, 0);
300 pci_disable_device(pdev);
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300301 if (wil->platform_ops.uninit)
302 wil->platform_ops.uninit(wil->platform_handle);
Lior David4332cac2016-03-01 19:18:13 +0200303 wil_p2p_wdev_free(wil);
Vladimir Kondratiev3e2d8e12015-06-09 14:11:19 +0300304 wil_if_free(wil);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800305}
306
Vladimir Kondratiev65082812014-07-14 09:49:37 +0300307static const struct pci_device_id wil6210_pcie_ids[] = {
Vladimir Kondratiev1aeda132014-12-23 09:47:18 +0200308 { PCI_DEVICE(0x1ae9, 0x0310) },
309 { PCI_DEVICE(0x1ae9, 0x0302) }, /* same as above, firmware broken */
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800310 { /* end: all zeroes */ },
311};
312MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
313
Vladimir Kondratiev93cb6792015-07-30 13:52:05 +0300314#ifdef CONFIG_PM
Vladimir Kondratiev5a813da2015-10-04 10:23:18 +0300315#ifdef CONFIG_PM_SLEEP
Vladimir Kondratiev93cb6792015-07-30 13:52:05 +0300316
317static int wil6210_suspend(struct device *dev, bool is_runtime)
318{
319 int rc = 0;
320 struct pci_dev *pdev = to_pci_dev(dev);
321 struct wil6210_priv *wil = pci_get_drvdata(pdev);
322
323 wil_dbg_pm(wil, "%s(%s)\n", __func__,
324 is_runtime ? "runtime" : "system");
325
326 rc = wil_can_suspend(wil, is_runtime);
327 if (rc)
328 goto out;
329
330 rc = wil_suspend(wil, is_runtime);
331 if (rc)
332 goto out;
333
334 /* TODO: how do I bring card in low power state? */
335
336 /* disable bus mastering */
337 pci_clear_master(pdev);
338 /* PCI will call pci_save_state(pdev) and pci_prepare_to_sleep(pdev) */
339
340out:
341 return rc;
342}
343
344static int wil6210_resume(struct device *dev, bool is_runtime)
345{
346 int rc = 0;
347 struct pci_dev *pdev = to_pci_dev(dev);
348 struct wil6210_priv *wil = pci_get_drvdata(pdev);
349
350 wil_dbg_pm(wil, "%s(%s)\n", __func__,
351 is_runtime ? "runtime" : "system");
352
353 /* allow master */
354 pci_set_master(pdev);
355
356 rc = wil_resume(wil, is_runtime);
357 if (rc)
358 pci_clear_master(pdev);
359
360 return rc;
361}
362
Maya Ereze34dc642016-05-16 22:23:34 +0300363static int wil6210_pm_notify(struct notifier_block *notify_block,
364 unsigned long mode, void *unused)
365{
366 struct wil6210_priv *wil = container_of(
367 notify_block, struct wil6210_priv, pm_notify);
368 int rc = 0;
369 enum wil_platform_event evt;
370
371 wil_dbg_pm(wil, "%s: mode (%ld)\n", __func__, mode);
372
373 switch (mode) {
374 case PM_HIBERNATION_PREPARE:
375 case PM_SUSPEND_PREPARE:
376 case PM_RESTORE_PREPARE:
377 rc = wil_can_suspend(wil, false);
378 if (rc)
379 break;
380 evt = WIL_PLATFORM_EVT_PRE_SUSPEND;
381 if (wil->platform_ops.notify)
382 rc = wil->platform_ops.notify(wil->platform_handle,
383 evt);
384 break;
385 case PM_POST_SUSPEND:
386 case PM_POST_HIBERNATION:
387 case PM_POST_RESTORE:
388 evt = WIL_PLATFORM_EVT_POST_SUSPEND;
389 if (wil->platform_ops.notify)
390 rc = wil->platform_ops.notify(wil->platform_handle,
391 evt);
392 break;
393 default:
394 wil_dbg_pm(wil, "unhandled notify mode %ld\n", mode);
395 break;
396 }
397
398 wil_dbg_pm(wil, "notification mode %ld: rc (%d)\n", mode, rc);
399 return rc;
400}
401
Vladimir Kondratiev93cb6792015-07-30 13:52:05 +0300402static int wil6210_pm_suspend(struct device *dev)
403{
404 return wil6210_suspend(dev, false);
405}
406
407static int wil6210_pm_resume(struct device *dev)
408{
409 return wil6210_resume(dev, false);
410}
411#endif /* CONFIG_PM_SLEEP */
412
413#endif /* CONFIG_PM */
414
415static const struct dev_pm_ops wil6210_pm_ops = {
416 SET_SYSTEM_SLEEP_PM_OPS(wil6210_pm_suspend, wil6210_pm_resume)
417};
418
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800419static struct pci_driver wil6210_driver = {
420 .probe = wil_pcie_probe,
421 .remove = wil_pcie_remove,
422 .id_table = wil6210_pcie_ids,
423 .name = WIL_NAME,
Vladimir Kondratiev93cb6792015-07-30 13:52:05 +0300424 .driver = {
425 .pm = &wil6210_pm_ops,
426 },
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800427};
428
Vladimir Kondratievb39d6932015-06-09 14:11:19 +0300429static int __init wil6210_driver_init(void)
430{
431 int rc;
432
433 rc = wil_platform_modinit();
434 if (rc)
435 return rc;
436
437 rc = pci_register_driver(&wil6210_driver);
438 if (rc)
439 wil_platform_modexit();
440 return rc;
441}
442module_init(wil6210_driver_init);
443
444static void __exit wil6210_driver_exit(void)
445{
446 pci_unregister_driver(&wil6210_driver);
447 wil_platform_modexit();
448}
449module_exit(wil6210_driver_exit);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800450
451MODULE_LICENSE("Dual BSD/GPL");
452MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>");
453MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card");