123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /*
- * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <linux/module.h>
- #include <linux/pci.h>
- #include <linux/moduleparam.h>
- #include <linux/interrupt.h>
- #include "wil6210.h"
- static bool use_msi = true;
- module_param(use_msi, bool, S_IRUGO);
- MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
- static
- void wil_set_capabilities(struct wil6210_priv *wil)
- {
- u32 rev_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
- bitmap_zero(wil->hw_capabilities, hw_capability_last);
- switch (rev_id) {
- case JTAG_DEV_ID_SPARROW_B0:
- wil->hw_name = "Sparrow B0";
- wil->hw_version = HW_VER_SPARROW_B0;
- break;
- default:
- wil_err(wil, "Unknown board hardware 0x%08x\n", rev_id);
- wil->hw_name = "Unknown";
- wil->hw_version = HW_VER_UNKNOWN;
- }
- wil_info(wil, "Board hardware is %s\n", wil->hw_name);
- }
- void wil_disable_irq(struct wil6210_priv *wil)
- {
- disable_irq(wil->pdev->irq);
- }
- void wil_enable_irq(struct wil6210_priv *wil)
- {
- enable_irq(wil->pdev->irq);
- }
- /* Bus ops */
- static int wil_if_pcie_enable(struct wil6210_priv *wil)
- {
- struct pci_dev *pdev = wil->pdev;
- int rc;
- /* on platforms with buggy ACPI, pdev->msi_enabled may be set to
- * allow pci_enable_device to work. This indicates INTx was not routed
- * and only MSI should be used
- */
- int msi_only = pdev->msi_enabled;
- bool _use_msi = use_msi;
- wil_dbg_misc(wil, "%s()\n", __func__);
- pdev->msi_enabled = 0;
- pci_set_master(pdev);
- wil_dbg_misc(wil, "Setup %s interrupt\n", use_msi ? "MSI" : "INTx");
- if (use_msi && pci_enable_msi(pdev)) {
- wil_err(wil, "pci_enable_msi failed, use INTx\n");
- _use_msi = false;
- }
- if (!_use_msi && msi_only) {
- wil_err(wil, "Interrupt pin not routed, unable to use INTx\n");
- rc = -ENODEV;
- goto stop_master;
- }
- rc = wil6210_init_irq(wil, pdev->irq, _use_msi);
- if (rc)
- goto stop_master;
- /* need reset here to obtain MAC */
- mutex_lock(&wil->mutex);
- rc = wil_reset(wil, false);
- mutex_unlock(&wil->mutex);
- if (rc)
- goto release_irq;
- return 0;
- release_irq:
- wil6210_fini_irq(wil, pdev->irq);
- /* safe to call if no MSI */
- pci_disable_msi(pdev);
- stop_master:
- pci_clear_master(pdev);
- return rc;
- }
- static int wil_if_pcie_disable(struct wil6210_priv *wil)
- {
- struct pci_dev *pdev = wil->pdev;
- wil_dbg_misc(wil, "%s()\n", __func__);
- pci_clear_master(pdev);
- /* disable and release IRQ */
- wil6210_fini_irq(wil, pdev->irq);
- /* safe to call if no MSI */
- pci_disable_msi(pdev);
- /* TODO: disable HW */
- return 0;
- }
- static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
- {
- struct wil6210_priv *wil;
- struct device *dev = &pdev->dev;
- int rc;
- /* check HW */
- dev_info(&pdev->dev, WIL_NAME
- " device found [%04x:%04x] (rev %x)\n",
- (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
- if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
- dev_err(&pdev->dev, "Not " WIL_NAME "? "
- "BAR0 size is %lu while expecting %lu\n",
- (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE);
- return -ENODEV;
- }
- wil = wil_if_alloc(dev);
- if (IS_ERR(wil)) {
- rc = (int)PTR_ERR(wil);
- dev_err(dev, "wil_if_alloc failed: %d\n", rc);
- return rc;
- }
- wil->pdev = pdev;
- pci_set_drvdata(pdev, wil);
- /* rollback to if_free */
- wil->platform_handle =
- wil_platform_init(&pdev->dev, &wil->platform_ops);
- if (!wil->platform_handle) {
- rc = -ENODEV;
- wil_err(wil, "wil_platform_init failed\n");
- goto if_free;
- }
- /* rollback to err_plat */
- rc = pci_enable_device(pdev);
- if (rc) {
- wil_err(wil,
- "pci_enable_device failed, retry with MSI only\n");
- /* Work around for platforms that can't allocate IRQ:
- * retry with MSI only
- */
- pdev->msi_enabled = 1;
- rc = pci_enable_device(pdev);
- }
- if (rc) {
- wil_err(wil,
- "pci_enable_device failed, even with MSI only\n");
- goto err_plat;
- }
- /* rollback to err_disable_pdev */
- rc = pci_request_region(pdev, 0, WIL_NAME);
- if (rc) {
- wil_err(wil, "pci_request_region failed\n");
- goto err_disable_pdev;
- }
- /* rollback to err_release_reg */
- wil->csr = pci_ioremap_bar(pdev, 0);
- if (!wil->csr) {
- wil_err(wil, "pci_ioremap_bar failed\n");
- rc = -ENODEV;
- goto err_release_reg;
- }
- /* rollback to err_iounmap */
- wil_info(wil, "CSR at %pR -> 0x%p\n", &pdev->resource[0], wil->csr);
- wil_set_capabilities(wil);
- wil6210_clear_irq(wil);
- /* FW should raise IRQ when ready */
- rc = wil_if_pcie_enable(wil);
- if (rc) {
- wil_err(wil, "Enable device failed\n");
- goto err_iounmap;
- }
- /* rollback to bus_disable */
- rc = wil_if_add(wil);
- if (rc) {
- wil_err(wil, "wil_if_add failed: %d\n", rc);
- goto bus_disable;
- }
- wil6210_debugfs_init(wil);
- return 0;
- bus_disable:
- wil_if_pcie_disable(wil);
- err_iounmap:
- pci_iounmap(pdev, wil->csr);
- err_release_reg:
- pci_release_region(pdev, 0);
- err_disable_pdev:
- pci_disable_device(pdev);
- err_plat:
- if (wil->platform_ops.uninit)
- wil->platform_ops.uninit(wil->platform_handle);
- if_free:
- wil_if_free(wil);
- return rc;
- }
- static void wil_pcie_remove(struct pci_dev *pdev)
- {
- struct wil6210_priv *wil = pci_get_drvdata(pdev);
- void __iomem *csr = wil->csr;
- wil_dbg_misc(wil, "%s()\n", __func__);
- wil6210_debugfs_remove(wil);
- wil_if_remove(wil);
- wil_if_pcie_disable(wil);
- pci_iounmap(pdev, csr);
- pci_release_region(pdev, 0);
- pci_disable_device(pdev);
- if (wil->platform_ops.uninit)
- wil->platform_ops.uninit(wil->platform_handle);
- wil_if_free(wil);
- }
- static const struct pci_device_id wil6210_pcie_ids[] = {
- { PCI_DEVICE(0x1ae9, 0x0310) },
- { PCI_DEVICE(0x1ae9, 0x0302) }, /* same as above, firmware broken */
- { /* end: all zeroes */ },
- };
- MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
- #ifdef CONFIG_PM
- #ifdef CONFIG_PM_SLEEP
- static int wil6210_suspend(struct device *dev, bool is_runtime)
- {
- int rc = 0;
- struct pci_dev *pdev = to_pci_dev(dev);
- struct wil6210_priv *wil = pci_get_drvdata(pdev);
- wil_dbg_pm(wil, "%s(%s)\n", __func__,
- is_runtime ? "runtime" : "system");
- rc = wil_can_suspend(wil, is_runtime);
- if (rc)
- goto out;
- rc = wil_suspend(wil, is_runtime);
- if (rc)
- goto out;
- /* TODO: how do I bring card in low power state? */
- /* disable bus mastering */
- pci_clear_master(pdev);
- /* PCI will call pci_save_state(pdev) and pci_prepare_to_sleep(pdev) */
- out:
- return rc;
- }
- static int wil6210_resume(struct device *dev, bool is_runtime)
- {
- int rc = 0;
- struct pci_dev *pdev = to_pci_dev(dev);
- struct wil6210_priv *wil = pci_get_drvdata(pdev);
- wil_dbg_pm(wil, "%s(%s)\n", __func__,
- is_runtime ? "runtime" : "system");
- /* allow master */
- pci_set_master(pdev);
- rc = wil_resume(wil, is_runtime);
- if (rc)
- pci_clear_master(pdev);
- return rc;
- }
- static int wil6210_pm_suspend(struct device *dev)
- {
- return wil6210_suspend(dev, false);
- }
- static int wil6210_pm_resume(struct device *dev)
- {
- return wil6210_resume(dev, false);
- }
- #endif /* CONFIG_PM_SLEEP */
- #endif /* CONFIG_PM */
- static const struct dev_pm_ops wil6210_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(wil6210_pm_suspend, wil6210_pm_resume)
- };
- static struct pci_driver wil6210_driver = {
- .probe = wil_pcie_probe,
- .remove = wil_pcie_remove,
- .id_table = wil6210_pcie_ids,
- .name = WIL_NAME,
- .driver = {
- .pm = &wil6210_pm_ops,
- },
- };
- static int __init wil6210_driver_init(void)
- {
- int rc;
- rc = wil_platform_modinit();
- if (rc)
- return rc;
- rc = pci_register_driver(&wil6210_driver);
- if (rc)
- wil_platform_modexit();
- return rc;
- }
- module_init(wil6210_driver_init);
- static void __exit wil6210_driver_exit(void)
- {
- pci_unregister_driver(&wil6210_driver);
- wil_platform_modexit();
- }
- module_exit(wil6210_driver_exit);
- MODULE_LICENSE("Dual BSD/GPL");
- MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>");
- MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card");
|