Commit | Line | Data |
---|---|---|
2be7d22f VK |
1 | /* |
2 | * Copyright (c) 2012 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 | ||
2be7d22f | 17 | #include <linux/module.h> |
2be7d22f VK |
18 | #include <linux/debugfs.h> |
19 | #include <linux/pci.h> | |
20 | #include <linux/moduleparam.h> | |
21 | ||
22 | #include "wil6210.h" | |
23 | ||
24 | static int use_msi = 1; | |
25 | module_param(use_msi, int, S_IRUGO); | |
26 | MODULE_PARM_DESC(use_msi, | |
27 | " Use MSI interrupt: " | |
28 | "0 - don't, 1 - (default) - single, or 3"); | |
29 | ||
30 | /* Bus ops */ | |
31 | static int wil_if_pcie_enable(struct wil6210_priv *wil) | |
32 | { | |
33 | struct pci_dev *pdev = wil->pdev; | |
34 | int rc; | |
35 | ||
36 | pci_set_master(pdev); | |
37 | ||
38 | /* | |
39 | * how many MSI interrupts to request? | |
40 | */ | |
41 | switch (use_msi) { | |
42 | case 3: | |
43 | case 1: | |
b4b39061 AG |
44 | wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi); |
45 | break; | |
2be7d22f | 46 | case 0: |
b4b39061 | 47 | wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n"); |
2be7d22f VK |
48 | break; |
49 | default: | |
b4b39061 | 50 | wil_err(wil, "Invalid use_msi=%d, default to 1\n", use_msi); |
2be7d22f VK |
51 | use_msi = 1; |
52 | } | |
b4b39061 AG |
53 | |
54 | if (use_msi == 3 && pci_enable_msi_range(pdev, 3, 3) < 0) { | |
55 | wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); | |
56 | use_msi = 1; | |
57 | } | |
58 | ||
59 | if (use_msi == 1 && pci_enable_msi(pdev)) { | |
60 | wil_err(wil, "pci_enable_msi failed, use INTx\n"); | |
61 | use_msi = 0; | |
2be7d22f VK |
62 | } |
63 | ||
b4b39061 AG |
64 | wil->n_msi = use_msi; |
65 | ||
2be7d22f VK |
66 | rc = wil6210_init_irq(wil, pdev->irq); |
67 | if (rc) | |
68 | goto stop_master; | |
69 | ||
70 | /* need reset here to obtain MAC */ | |
097638a0 | 71 | mutex_lock(&wil->mutex); |
2be7d22f | 72 | rc = wil_reset(wil); |
097638a0 | 73 | mutex_unlock(&wil->mutex); |
2be7d22f VK |
74 | if (rc) |
75 | goto release_irq; | |
76 | ||
77 | return 0; | |
78 | ||
79 | release_irq: | |
80 | wil6210_fini_irq(wil, pdev->irq); | |
81 | /* safe to call if no MSI */ | |
82 | pci_disable_msi(pdev); | |
83 | stop_master: | |
84 | pci_clear_master(pdev); | |
85 | return rc; | |
86 | } | |
87 | ||
88 | static int wil_if_pcie_disable(struct wil6210_priv *wil) | |
89 | { | |
90 | struct pci_dev *pdev = wil->pdev; | |
91 | ||
92 | pci_clear_master(pdev); | |
93 | /* disable and release IRQ */ | |
94 | wil6210_fini_irq(wil, pdev->irq); | |
95 | /* safe to call if no MSI */ | |
96 | pci_disable_msi(pdev); | |
97 | /* TODO: disable HW */ | |
98 | ||
99 | return 0; | |
100 | } | |
101 | ||
102 | static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |
103 | { | |
104 | struct wil6210_priv *wil; | |
105 | struct device *dev = &pdev->dev; | |
106 | void __iomem *csr; | |
107 | int rc; | |
108 | ||
109 | /* check HW */ | |
110 | dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", | |
111 | (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); | |
112 | ||
113 | if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { | |
114 | dev_err(&pdev->dev, "Not " WIL_NAME "? " | |
115 | "BAR0 size is %lu while expecting %lu\n", | |
116 | (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); | |
117 | return -ENODEV; | |
118 | } | |
119 | ||
120 | rc = pci_enable_device(pdev); | |
121 | if (rc) { | |
122 | dev_err(&pdev->dev, "pci_enable_device failed\n"); | |
123 | return -ENODEV; | |
124 | } | |
125 | /* rollback to err_disable_pdev */ | |
126 | ||
127 | rc = pci_request_region(pdev, 0, WIL_NAME); | |
128 | if (rc) { | |
129 | dev_err(&pdev->dev, "pci_request_region failed\n"); | |
130 | goto err_disable_pdev; | |
131 | } | |
132 | /* rollback to err_release_reg */ | |
133 | ||
134 | csr = pci_ioremap_bar(pdev, 0); | |
135 | if (!csr) { | |
136 | dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); | |
137 | rc = -ENODEV; | |
138 | goto err_release_reg; | |
139 | } | |
140 | /* rollback to err_iounmap */ | |
141 | dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); | |
142 | ||
143 | wil = wil_if_alloc(dev, csr); | |
144 | if (IS_ERR(wil)) { | |
145 | rc = (int)PTR_ERR(wil); | |
146 | dev_err(dev, "wil_if_alloc failed: %d\n", rc); | |
147 | goto err_iounmap; | |
148 | } | |
149 | /* rollback to if_free */ | |
150 | ||
151 | pci_set_drvdata(pdev, wil); | |
152 | wil->pdev = pdev; | |
153 | ||
f4b5a803 | 154 | wil6210_clear_irq(wil); |
2be7d22f VK |
155 | /* FW should raise IRQ when ready */ |
156 | rc = wil_if_pcie_enable(wil); | |
157 | if (rc) { | |
158 | wil_err(wil, "Enable device failed\n"); | |
159 | goto if_free; | |
160 | } | |
161 | /* rollback to bus_disable */ | |
162 | ||
163 | rc = wil_if_add(wil); | |
164 | if (rc) { | |
165 | wil_err(wil, "wil_if_add failed: %d\n", rc); | |
166 | goto bus_disable; | |
167 | } | |
168 | ||
169 | wil6210_debugfs_init(wil); | |
170 | ||
171 | /* check FW is alive */ | |
172 | wmi_echo(wil); | |
173 | ||
174 | return 0; | |
175 | ||
176 | bus_disable: | |
177 | wil_if_pcie_disable(wil); | |
178 | if_free: | |
179 | wil_if_free(wil); | |
180 | err_iounmap: | |
181 | pci_iounmap(pdev, csr); | |
182 | err_release_reg: | |
183 | pci_release_region(pdev, 0); | |
184 | err_disable_pdev: | |
185 | pci_disable_device(pdev); | |
186 | ||
187 | return rc; | |
188 | } | |
189 | ||
190 | static void wil_pcie_remove(struct pci_dev *pdev) | |
191 | { | |
192 | struct wil6210_priv *wil = pci_get_drvdata(pdev); | |
193 | ||
194 | wil6210_debugfs_remove(wil); | |
195 | wil_if_pcie_disable(wil); | |
196 | wil_if_remove(wil); | |
197 | wil_if_free(wil); | |
198 | pci_iounmap(pdev, wil->csr); | |
199 | pci_release_region(pdev, 0); | |
200 | pci_disable_device(pdev); | |
2be7d22f VK |
201 | } |
202 | ||
203 | static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { | |
204 | { PCI_DEVICE(0x1ae9, 0x0301) }, | |
205 | { /* end: all zeroes */ }, | |
206 | }; | |
207 | MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); | |
208 | ||
209 | static struct pci_driver wil6210_driver = { | |
210 | .probe = wil_pcie_probe, | |
211 | .remove = wil_pcie_remove, | |
212 | .id_table = wil6210_pcie_ids, | |
213 | .name = WIL_NAME, | |
214 | }; | |
215 | ||
216 | module_pci_driver(wil6210_driver); | |
217 | ||
218 | MODULE_LICENSE("Dual BSD/GPL"); | |
219 | MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>"); | |
220 | MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); |