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 VK |
17 | #include <linux/moduleparam.h> |
18 | #include <linux/if_arp.h> | |
19 | ||
20 | #include "wil6210.h" | |
21 | ||
22 | /* | |
23 | * Due to a hardware issue, | |
24 | * one has to read/write to/from NIC in 32-bit chunks; | |
25 | * regular memcpy_fromio and siblings will | |
26 | * not work on 64-bit platform - it uses 64-bit transactions | |
27 | * | |
28 | * Force 32-bit transactions to enable NIC on 64-bit platforms | |
29 | * | |
30 | * To avoid byte swap on big endian host, __raw_{read|write}l | |
31 | * should be used - {read|write}l would swap bytes to provide | |
32 | * little endian on PCI value in host endianness. | |
33 | */ | |
34 | void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, | |
35 | size_t count) | |
36 | { | |
37 | u32 *d = dst; | |
38 | const volatile u32 __iomem *s = src; | |
39 | ||
40 | /* size_t is unsigned, if (count%4 != 0) it will wrap */ | |
41 | for (count += 4; count > 4; count -= 4) | |
42 | *d++ = __raw_readl(s++); | |
43 | } | |
44 | ||
45 | void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | |
46 | size_t count) | |
47 | { | |
48 | volatile u32 __iomem *d = dst; | |
49 | const u32 *s = src; | |
50 | ||
51 | for (count += 4; count > 4; count -= 4) | |
52 | __raw_writel(*s++, d++); | |
53 | } | |
54 | ||
55 | static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | |
56 | { | |
57 | uint i; | |
58 | struct net_device *ndev = wil_to_ndev(wil); | |
2be7d22f | 59 | |
7743882d | 60 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
61 | |
62 | wil_link_off(wil); | |
b338f74e VK |
63 | if (test_bit(wil_status_fwconnected, &wil->status)) { |
64 | clear_bit(wil_status_fwconnected, &wil->status); | |
65 | cfg80211_disconnected(ndev, | |
66 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
2be7d22f | 67 | NULL, 0, GFP_KERNEL); |
b338f74e | 68 | } else if (test_bit(wil_status_fwconnecting, &wil->status)) { |
2be7d22f VK |
69 | cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, |
70 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
71 | GFP_KERNEL); | |
2be7d22f | 72 | } |
b338f74e | 73 | clear_bit(wil_status_fwconnecting, &wil->status); |
2be7d22f VK |
74 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) |
75 | wil_vring_fini_tx(wil, i); | |
b98917d7 VK |
76 | |
77 | clear_bit(wil_status_dontscan, &wil->status); | |
2be7d22f VK |
78 | } |
79 | ||
80 | static void wil_disconnect_worker(struct work_struct *work) | |
81 | { | |
82 | struct wil6210_priv *wil = container_of(work, | |
83 | struct wil6210_priv, disconnect_worker); | |
84 | ||
85 | _wil6210_disconnect(wil, NULL); | |
86 | } | |
87 | ||
88 | static void wil_connect_timer_fn(ulong x) | |
89 | { | |
90 | struct wil6210_priv *wil = (void *)x; | |
91 | ||
7743882d | 92 | wil_dbg_misc(wil, "Connect timeout\n"); |
2be7d22f VK |
93 | |
94 | /* reschedule to thread context - disconnect won't | |
95 | * run from atomic context | |
96 | */ | |
97 | schedule_work(&wil->disconnect_worker); | |
98 | } | |
99 | ||
d81079f1 VK |
100 | static void wil_connect_worker(struct work_struct *work) |
101 | { | |
102 | int rc; | |
103 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | |
104 | connect_worker); | |
105 | int cid = wil->pending_connect_cid; | |
106 | ||
107 | if (cid < 0) { | |
108 | wil_err(wil, "No connection pending\n"); | |
109 | return; | |
110 | } | |
111 | ||
112 | wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); | |
113 | ||
114 | rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, cid, 0); | |
115 | wil->pending_connect_cid = -1; | |
3df2cd36 VK |
116 | if (rc == 0) { |
117 | wil->sta[cid].status = wil_sta_connected; | |
d81079f1 | 118 | wil_link_on(wil); |
3df2cd36 VK |
119 | } else { |
120 | wil->sta[cid].status = wil_sta_unused; | |
121 | } | |
d81079f1 VK |
122 | } |
123 | ||
2be7d22f VK |
124 | int wil_priv_init(struct wil6210_priv *wil) |
125 | { | |
7743882d | 126 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f | 127 | |
3df2cd36 VK |
128 | memset(wil->sta, 0, sizeof(wil->sta)); |
129 | ||
2be7d22f VK |
130 | mutex_init(&wil->mutex); |
131 | mutex_init(&wil->wmi_mutex); | |
132 | ||
133 | init_completion(&wil->wmi_ready); | |
134 | ||
135 | wil->pending_connect_cid = -1; | |
136 | setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); | |
137 | ||
d81079f1 | 138 | INIT_WORK(&wil->connect_worker, wil_connect_worker); |
2be7d22f VK |
139 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); |
140 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | |
141 | ||
142 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | |
143 | spin_lock_init(&wil->wmi_ev_lock); | |
144 | ||
145 | wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); | |
146 | if (!wil->wmi_wq) | |
147 | return -EAGAIN; | |
148 | ||
149 | wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); | |
150 | if (!wil->wmi_wq_conn) { | |
151 | destroy_workqueue(wil->wmi_wq); | |
152 | return -EAGAIN; | |
153 | } | |
154 | ||
2be7d22f VK |
155 | return 0; |
156 | } | |
157 | ||
158 | void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | |
159 | { | |
160 | del_timer_sync(&wil->connect_timer); | |
161 | _wil6210_disconnect(wil, bssid); | |
162 | } | |
163 | ||
164 | void wil_priv_deinit(struct wil6210_priv *wil) | |
165 | { | |
166 | cancel_work_sync(&wil->disconnect_worker); | |
167 | wil6210_disconnect(wil, NULL); | |
168 | wmi_event_flush(wil); | |
169 | destroy_workqueue(wil->wmi_wq_conn); | |
170 | destroy_workqueue(wil->wmi_wq); | |
171 | } | |
172 | ||
173 | static void wil_target_reset(struct wil6210_priv *wil) | |
174 | { | |
7743882d | 175 | wil_dbg_misc(wil, "Resetting...\n"); |
2be7d22f VK |
176 | |
177 | /* register write */ | |
178 | #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) | |
179 | /* register set = read, OR, write */ | |
180 | #define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ | |
181 | wil->csr + HOSTADDR(a)) | |
182 | ||
183 | /* hpal_perst_from_pad_src_n_mask */ | |
184 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); | |
185 | /* car_perst_rst_src_n_mask */ | |
186 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); | |
187 | ||
188 | W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ | |
189 | W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ | |
190 | ||
2be7d22f VK |
191 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); |
192 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | |
193 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); | |
194 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); | |
195 | ||
2be7d22f VK |
196 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); |
197 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | |
198 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | |
199 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | |
200 | ||
201 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); | |
202 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); | |
203 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | |
204 | ||
7743882d | 205 | wil_dbg_misc(wil, "Reset completed\n"); |
2be7d22f VK |
206 | |
207 | #undef W | |
208 | #undef S | |
209 | } | |
210 | ||
211 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) | |
212 | { | |
213 | le32_to_cpus(&r->base); | |
214 | le16_to_cpus(&r->entry_size); | |
215 | le16_to_cpus(&r->size); | |
216 | le32_to_cpus(&r->tail); | |
217 | le32_to_cpus(&r->head); | |
218 | } | |
219 | ||
220 | static int wil_wait_for_fw_ready(struct wil6210_priv *wil) | |
221 | { | |
222 | ulong to = msecs_to_jiffies(1000); | |
223 | ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); | |
224 | if (0 == left) { | |
225 | wil_err(wil, "Firmware not ready\n"); | |
226 | return -ETIME; | |
227 | } else { | |
7743882d | 228 | wil_dbg_misc(wil, "FW ready after %d ms\n", |
2057ebb2 | 229 | jiffies_to_msecs(to-left)); |
2be7d22f VK |
230 | } |
231 | return 0; | |
232 | } | |
233 | ||
234 | /* | |
235 | * We reset all the structures, and we reset the UMAC. | |
236 | * After calling this routine, you're expected to reload | |
237 | * the firmware. | |
238 | */ | |
239 | int wil_reset(struct wil6210_priv *wil) | |
240 | { | |
241 | int rc; | |
242 | ||
243 | cancel_work_sync(&wil->disconnect_worker); | |
244 | wil6210_disconnect(wil, NULL); | |
245 | ||
e08b5906 VK |
246 | wil6210_disable_irq(wil); |
247 | wil->status = 0; | |
248 | ||
2be7d22f VK |
249 | wmi_event_flush(wil); |
250 | ||
2be7d22f | 251 | flush_workqueue(wil->wmi_wq_conn); |
e08b5906 | 252 | flush_workqueue(wil->wmi_wq); |
2be7d22f VK |
253 | |
254 | /* TODO: put MAC in reset */ | |
255 | wil_target_reset(wil); | |
256 | ||
257 | /* init after reset */ | |
258 | wil->pending_connect_cid = -1; | |
16735d02 | 259 | reinit_completion(&wil->wmi_ready); |
2be7d22f | 260 | |
2be7d22f VK |
261 | /* TODO: release MAC reset */ |
262 | wil6210_enable_irq(wil); | |
263 | ||
264 | /* we just started MAC, wait for FW ready */ | |
265 | rc = wil_wait_for_fw_ready(wil); | |
266 | ||
267 | return rc; | |
268 | } | |
269 | ||
270 | ||
271 | void wil_link_on(struct wil6210_priv *wil) | |
272 | { | |
273 | struct net_device *ndev = wil_to_ndev(wil); | |
274 | ||
7743882d | 275 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
276 | |
277 | netif_carrier_on(ndev); | |
278 | netif_tx_wake_all_queues(ndev); | |
279 | } | |
280 | ||
281 | void wil_link_off(struct wil6210_priv *wil) | |
282 | { | |
283 | struct net_device *ndev = wil_to_ndev(wil); | |
284 | ||
7743882d | 285 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
286 | |
287 | netif_tx_stop_all_queues(ndev); | |
288 | netif_carrier_off(ndev); | |
289 | } | |
290 | ||
291 | static int __wil_up(struct wil6210_priv *wil) | |
292 | { | |
293 | struct net_device *ndev = wil_to_ndev(wil); | |
294 | struct wireless_dev *wdev = wil->wdev; | |
2be7d22f | 295 | int rc; |
2be7d22f VK |
296 | |
297 | rc = wil_reset(wil); | |
298 | if (rc) | |
299 | return rc; | |
300 | ||
e31b2562 VK |
301 | /* Rx VRING. After MAC and beacon */ |
302 | rc = wil_rx_init(wil); | |
303 | if (rc) | |
304 | return rc; | |
305 | ||
2be7d22f VK |
306 | switch (wdev->iftype) { |
307 | case NL80211_IFTYPE_STATION: | |
7743882d | 308 | wil_dbg_misc(wil, "type: STATION\n"); |
2be7d22f VK |
309 | ndev->type = ARPHRD_ETHER; |
310 | break; | |
311 | case NL80211_IFTYPE_AP: | |
7743882d | 312 | wil_dbg_misc(wil, "type: AP\n"); |
2be7d22f VK |
313 | ndev->type = ARPHRD_ETHER; |
314 | break; | |
315 | case NL80211_IFTYPE_P2P_CLIENT: | |
7743882d | 316 | wil_dbg_misc(wil, "type: P2P_CLIENT\n"); |
2be7d22f VK |
317 | ndev->type = ARPHRD_ETHER; |
318 | break; | |
319 | case NL80211_IFTYPE_P2P_GO: | |
7743882d | 320 | wil_dbg_misc(wil, "type: P2P_GO\n"); |
2be7d22f VK |
321 | ndev->type = ARPHRD_ETHER; |
322 | break; | |
323 | case NL80211_IFTYPE_MONITOR: | |
7743882d | 324 | wil_dbg_misc(wil, "type: Monitor\n"); |
2be7d22f VK |
325 | ndev->type = ARPHRD_IEEE80211_RADIOTAP; |
326 | /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ | |
327 | break; | |
328 | default: | |
329 | return -EOPNOTSUPP; | |
330 | } | |
331 | ||
2be7d22f VK |
332 | /* MAC address - pre-requisite for other commands */ |
333 | wmi_set_mac_address(wil, ndev->dev_addr); | |
334 | ||
2be7d22f | 335 | |
e0287c4a VK |
336 | napi_enable(&wil->napi_rx); |
337 | napi_enable(&wil->napi_tx); | |
338 | ||
2be7d22f VK |
339 | return 0; |
340 | } | |
341 | ||
342 | int wil_up(struct wil6210_priv *wil) | |
343 | { | |
344 | int rc; | |
345 | ||
346 | mutex_lock(&wil->mutex); | |
347 | rc = __wil_up(wil); | |
348 | mutex_unlock(&wil->mutex); | |
349 | ||
350 | return rc; | |
351 | } | |
352 | ||
353 | static int __wil_down(struct wil6210_priv *wil) | |
354 | { | |
e0287c4a VK |
355 | napi_disable(&wil->napi_rx); |
356 | napi_disable(&wil->napi_tx); | |
357 | ||
2be7d22f VK |
358 | if (wil->scan_request) { |
359 | cfg80211_scan_done(wil->scan_request, true); | |
360 | wil->scan_request = NULL; | |
361 | } | |
362 | ||
363 | wil6210_disconnect(wil, NULL); | |
364 | wil_rx_fini(wil); | |
365 | ||
366 | return 0; | |
367 | } | |
368 | ||
369 | int wil_down(struct wil6210_priv *wil) | |
370 | { | |
371 | int rc; | |
372 | ||
373 | mutex_lock(&wil->mutex); | |
374 | rc = __wil_down(wil); | |
375 | mutex_unlock(&wil->mutex); | |
376 | ||
377 | return rc; | |
378 | } | |
3df2cd36 VK |
379 | |
380 | int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) | |
381 | { | |
382 | int i; | |
383 | int rc = -ENOENT; | |
384 | ||
385 | for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { | |
386 | if ((wil->sta[i].status != wil_sta_unused) && | |
387 | (0 == memcmp(wil->sta[i].addr, mac, ETH_ALEN))) { | |
388 | rc = i; | |
389 | break; | |
390 | } | |
391 | } | |
392 | ||
393 | return rc; | |
394 | } |