linux/drivers/net/wireless/ath/wil6210/pcie_bus.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
   3 *
   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
  17#include <linux/module.h>
  18#include <linux/pci.h>
  19#include <linux/moduleparam.h>
  20#include <linux/interrupt.h>
  21
  22#include "wil6210.h"
  23
  24static int use_msi = 1;
  25module_param(use_msi, int, S_IRUGO);
  26MODULE_PARM_DESC(use_msi,
  27                 " Use MSI interrupt: "
  28                 "0 - don't, 1 - (default) - single, or 3");
  29
  30static bool debug_fw; /* = false; */
  31module_param(debug_fw, bool, S_IRUGO);
  32MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug");
  33
  34void wil_disable_irq(struct wil6210_priv *wil)
  35{
  36        int irq = wil->pdev->irq;
  37
  38        disable_irq(irq);
  39        if (wil->n_msi == 3) {
  40                disable_irq(irq + 1);
  41                disable_irq(irq + 2);
  42        }
  43}
  44
  45void wil_enable_irq(struct wil6210_priv *wil)
  46{
  47        int irq = wil->pdev->irq;
  48
  49        enable_irq(irq);
  50        if (wil->n_msi == 3) {
  51                enable_irq(irq + 1);
  52                enable_irq(irq + 2);
  53        }
  54}
  55
  56/* Bus ops */
  57static int wil_if_pcie_enable(struct wil6210_priv *wil)
  58{
  59        struct pci_dev *pdev = wil->pdev;
  60        int rc;
  61        /* on platforms with buggy ACPI, pdev->msi_enabled may be set to
  62         * allow pci_enable_device to work. This indicates INTx was not routed
  63         * and only MSI should be used
  64         */
  65        int msi_only = pdev->msi_enabled;
  66
  67        wil_dbg_misc(wil, "%s()\n", __func__);
  68
  69        pdev->msi_enabled = 0;
  70
  71        pci_set_master(pdev);
  72
  73        /*
  74         * how many MSI interrupts to request?
  75         */
  76        switch (use_msi) {
  77        case 3:
  78        case 1:
  79                wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi);
  80                break;
  81        case 0:
  82                wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n");
  83                break;
  84        default:
  85                wil_err(wil, "Invalid use_msi=%d, default to 1\n", use_msi);
  86                use_msi = 1;
  87        }
  88
  89        if (use_msi == 3 && pci_enable_msi_range(pdev, 3, 3) < 0) {
  90                wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
  91                use_msi = 1;
  92        }
  93
  94        if (use_msi == 1 && pci_enable_msi(pdev)) {
  95                wil_err(wil, "pci_enable_msi failed, use INTx\n");
  96                use_msi = 0;
  97        }
  98
  99        wil->n_msi = use_msi;
 100
 101        if ((wil->n_msi == 0) && msi_only) {
 102                wil_err(wil, "Interrupt pin not routed, unable to use INTx\n");
 103                rc = -ENODEV;
 104                goto stop_master;
 105        }
 106
 107        rc = wil6210_init_irq(wil, pdev->irq);
 108        if (rc)
 109                goto stop_master;
 110
 111        /* need reset here to obtain MAC */
 112        mutex_lock(&wil->mutex);
 113        rc = wil_reset(wil);
 114        mutex_unlock(&wil->mutex);
 115        if (debug_fw)
 116                rc = 0;
 117        if (rc)
 118                goto release_irq;
 119
 120        return 0;
 121
 122 release_irq:
 123        wil6210_fini_irq(wil, pdev->irq);
 124        /* safe to call if no MSI */
 125        pci_disable_msi(pdev);
 126 stop_master:
 127        pci_clear_master(pdev);
 128        return rc;
 129}
 130
 131static int wil_if_pcie_disable(struct wil6210_priv *wil)
 132{
 133        struct pci_dev *pdev = wil->pdev;
 134
 135        wil_dbg_misc(wil, "%s()\n", __func__);
 136
 137        pci_clear_master(pdev);
 138        /* disable and release IRQ */
 139        wil6210_fini_irq(wil, pdev->irq);
 140        /* safe to call if no MSI */
 141        pci_disable_msi(pdev);
 142        /* TODO: disable HW */
 143
 144        return 0;
 145}
 146
 147static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 148{
 149        struct wil6210_priv *wil;
 150        struct device *dev = &pdev->dev;
 151        void __iomem *csr;
 152        struct wil_board *board = (struct wil_board *)id->driver_data;
 153        int rc;
 154
 155        /* check HW */
 156        dev_info(&pdev->dev, WIL_NAME
 157                 " \"%s\" device found [%04x:%04x] (rev %x)\n", board->name,
 158                 (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
 159
 160        if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
 161                dev_err(&pdev->dev, "Not " WIL_NAME "? "
 162                        "BAR0 size is %lu while expecting %lu\n",
 163                        (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE);
 164                return -ENODEV;
 165        }
 166
 167        rc = pci_enable_device(pdev);
 168        if (rc) {
 169                dev_err(&pdev->dev,
 170                        "pci_enable_device failed, retry with MSI only\n");
 171                /* Work around for platforms that can't allocate IRQ:
 172                 * retry with MSI only
 173                 */
 174                pdev->msi_enabled = 1;
 175                rc = pci_enable_device(pdev);
 176        }
 177        if (rc)
 178                return -ENODEV;
 179        /* rollback to err_disable_pdev */
 180
 181        rc = pci_request_region(pdev, 0, WIL_NAME);
 182        if (rc) {
 183                dev_err(&pdev->dev, "pci_request_region failed\n");
 184                goto err_disable_pdev;
 185        }
 186        /* rollback to err_release_reg */
 187
 188        csr = pci_ioremap_bar(pdev, 0);
 189        if (!csr) {
 190                dev_err(&pdev->dev, "pci_ioremap_bar failed\n");
 191                rc = -ENODEV;
 192                goto err_release_reg;
 193        }
 194        /* rollback to err_iounmap */
 195        dev_info(&pdev->dev, "CSR at %pR -> 0x%p\n", &pdev->resource[0], csr);
 196
 197        wil = wil_if_alloc(dev, csr);
 198        if (IS_ERR(wil)) {
 199                rc = (int)PTR_ERR(wil);
 200                dev_err(dev, "wil_if_alloc failed: %d\n", rc);
 201                goto err_iounmap;
 202        }
 203        /* rollback to if_free */
 204
 205        pci_set_drvdata(pdev, wil);
 206        wil->pdev = pdev;
 207        wil->board = board;
 208
 209        wil6210_clear_irq(wil);
 210
 211        wil->platform_handle =
 212                        wil_platform_init(&pdev->dev, &wil->platform_ops);
 213
 214        /* FW should raise IRQ when ready */
 215        rc = wil_if_pcie_enable(wil);
 216        if (rc) {
 217                wil_err(wil, "Enable device failed\n");
 218                goto if_free;
 219        }
 220        /* rollback to bus_disable */
 221
 222        rc = wil_if_add(wil);
 223        if (rc) {
 224                wil_err(wil, "wil_if_add failed: %d\n", rc);
 225                goto bus_disable;
 226        }
 227
 228        wil6210_debugfs_init(wil);
 229
 230        /* check FW is alive */
 231        wmi_echo(wil);
 232
 233        return 0;
 234
 235 bus_disable:
 236        wil_if_pcie_disable(wil);
 237 if_free:
 238        if (wil->platform_ops.uninit)
 239                wil->platform_ops.uninit(wil->platform_handle);
 240        wil_if_free(wil);
 241 err_iounmap:
 242        pci_iounmap(pdev, csr);
 243 err_release_reg:
 244        pci_release_region(pdev, 0);
 245 err_disable_pdev:
 246        pci_disable_device(pdev);
 247
 248        return rc;
 249}
 250
 251static void wil_pcie_remove(struct pci_dev *pdev)
 252{
 253        struct wil6210_priv *wil = pci_get_drvdata(pdev);
 254        void __iomem *csr = wil->csr;
 255
 256        wil_dbg_misc(wil, "%s()\n", __func__);
 257
 258        wil6210_debugfs_remove(wil);
 259        wil_if_remove(wil);
 260        wil_if_pcie_disable(wil);
 261        if (wil->platform_ops.uninit)
 262                wil->platform_ops.uninit(wil->platform_handle);
 263        wil_if_free(wil);
 264        pci_iounmap(pdev, csr);
 265        pci_release_region(pdev, 0);
 266        pci_disable_device(pdev);
 267}
 268
 269static const struct wil_board wil_board_marlon = {
 270        .board = WIL_BOARD_MARLON,
 271        .name = "marlon",
 272};
 273
 274static const struct wil_board wil_board_sparrow = {
 275        .board = WIL_BOARD_SPARROW,
 276        .name = "sparrow",
 277};
 278
 279static const struct pci_device_id wil6210_pcie_ids[] = {
 280        { PCI_DEVICE(0x1ae9, 0x0301),
 281          .driver_data = (kernel_ulong_t)&wil_board_marlon },
 282        { PCI_DEVICE(0x1ae9, 0x0310),
 283          .driver_data = (kernel_ulong_t)&wil_board_sparrow },
 284        { PCI_DEVICE(0x1ae9, 0x0302), /* same as above, firmware broken */
 285          .driver_data = (kernel_ulong_t)&wil_board_sparrow },
 286        { /* end: all zeroes */ },
 287};
 288MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
 289
 290static struct pci_driver wil6210_driver = {
 291        .probe          = wil_pcie_probe,
 292        .remove         = wil_pcie_remove,
 293        .id_table       = wil6210_pcie_ids,
 294        .name           = WIL_NAME,
 295};
 296
 297module_pci_driver(wil6210_driver);
 298
 299MODULE_LICENSE("Dual BSD/GPL");
 300MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>");
 301MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card");
 302
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.