Commit | Line | Data |
---|---|---|
fb9987d0 | 1 | /* |
5b68138e | 2 | * Copyright (c) 2010-2011 Atheros Communications Inc. |
fb9987d0 S |
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 | ||
516304b0 JP |
17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
18 | ||
fb9987d0 S |
19 | #include "htc.h" |
20 | ||
21 | MODULE_AUTHOR("Atheros Communications"); | |
22 | MODULE_LICENSE("Dual BSD/GPL"); | |
23 | MODULE_DESCRIPTION("Atheros driver 802.11n HTC based wireless devices"); | |
24 | ||
25 | static unsigned int ath9k_debug = ATH_DBG_DEFAULT; | |
26 | module_param_named(debug, ath9k_debug, uint, 0); | |
27 | MODULE_PARM_DESC(debug, "Debugging mask"); | |
28 | ||
e1572c5e S |
29 | int htc_modparam_nohwcrypt; |
30 | module_param_named(nohwcrypt, htc_modparam_nohwcrypt, int, 0444); | |
fb9987d0 S |
31 | MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); |
32 | ||
7f34778e MSS |
33 | static int ath9k_htc_btcoex_enable; |
34 | module_param_named(btcoex_enable, ath9k_htc_btcoex_enable, int, 0444); | |
35 | MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence"); | |
36 | ||
6bca610d OR |
37 | static int ath9k_ps_enable; |
38 | module_param_named(ps_enable, ath9k_ps_enable, int, 0444); | |
39 | MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); | |
40 | ||
e904cf6f OR |
41 | int htc_use_dev_fw = 0; |
42 | module_param_named(use_dev_fw, htc_use_dev_fw, int, 0444); | |
43 | MODULE_PARM_DESC(use_dev_fw, "Use development FW version"); | |
44 | ||
d244f21e | 45 | #ifdef CONFIG_MAC80211_LEDS |
0c8a1e43 HX |
46 | int ath9k_htc_led_blink = 1; |
47 | module_param_named(blink, ath9k_htc_led_blink, int, 0444); | |
3a939a67 HX |
48 | MODULE_PARM_DESC(blink, "Enable LED blink on activity"); |
49 | ||
d244f21e SM |
50 | static const struct ieee80211_tpt_blink ath9k_htc_tpt_blink[] = { |
51 | { .throughput = 0 * 1024, .blink_time = 334 }, | |
52 | { .throughput = 1 * 1024, .blink_time = 260 }, | |
53 | { .throughput = 5 * 1024, .blink_time = 220 }, | |
54 | { .throughput = 10 * 1024, .blink_time = 190 }, | |
55 | { .throughput = 20 * 1024, .blink_time = 170 }, | |
56 | { .throughput = 50 * 1024, .blink_time = 150 }, | |
57 | { .throughput = 70 * 1024, .blink_time = 130 }, | |
58 | { .throughput = 100 * 1024, .blink_time = 110 }, | |
59 | { .throughput = 200 * 1024, .blink_time = 80 }, | |
60 | { .throughput = 300 * 1024, .blink_time = 50 }, | |
61 | }; | |
62 | #endif | |
63 | ||
7d031552 | 64 | static void ath9k_htc_op_ps_wakeup(struct ath_common *common) |
94cd95c2 OR |
65 | { |
66 | ath9k_htc_ps_wakeup((struct ath9k_htc_priv *) common->priv); | |
67 | } | |
68 | ||
7d031552 | 69 | static void ath9k_htc_op_ps_restore(struct ath_common *common) |
94cd95c2 OR |
70 | { |
71 | ath9k_htc_ps_restore((struct ath9k_htc_priv *) common->priv); | |
72 | } | |
73 | ||
7d031552 | 74 | static struct ath_ps_ops ath9k_htc_ps_ops = { |
94cd95c2 OR |
75 | .wakeup = ath9k_htc_op_ps_wakeup, |
76 | .restore = ath9k_htc_op_ps_restore, | |
77 | }; | |
78 | ||
fb9987d0 S |
79 | static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv) |
80 | { | |
70a56550 | 81 | unsigned long time_left; |
fb9987d0 | 82 | |
d8c49ffb SM |
83 | if (atomic_read(&priv->htc->tgt_ready) > 0) { |
84 | atomic_dec(&priv->htc->tgt_ready); | |
85 | return 0; | |
86 | } | |
87 | ||
fb9987d0 S |
88 | /* Firmware can take up to 50ms to get ready, to be safe use 1 second */ |
89 | time_left = wait_for_completion_timeout(&priv->htc->target_wait, HZ); | |
90 | if (!time_left) { | |
91 | dev_err(priv->dev, "ath9k_htc: Target is unresponsive\n"); | |
92 | return -ETIMEDOUT; | |
93 | } | |
94 | ||
d8c49ffb SM |
95 | atomic_dec(&priv->htc->tgt_ready); |
96 | ||
fb9987d0 S |
97 | return 0; |
98 | } | |
99 | ||
100 | static void ath9k_deinit_priv(struct ath9k_htc_priv *priv) | |
101 | { | |
fb9987d0 | 102 | ath9k_hw_deinit(priv->ah); |
fb9987d0 S |
103 | kfree(priv->ah); |
104 | priv->ah = NULL; | |
105 | } | |
106 | ||
107 | static void ath9k_deinit_device(struct ath9k_htc_priv *priv) | |
108 | { | |
109 | struct ieee80211_hw *hw = priv->hw; | |
110 | ||
111 | wiphy_rfkill_stop_polling(hw->wiphy); | |
112 | ath9k_deinit_leds(priv); | |
3f2aa13f | 113 | ath9k_htc_deinit_debug(priv); |
fb9987d0 S |
114 | ieee80211_unregister_hw(hw); |
115 | ath9k_rx_cleanup(priv); | |
116 | ath9k_tx_cleanup(priv); | |
117 | ath9k_deinit_priv(priv); | |
118 | } | |
119 | ||
120 | static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv, | |
121 | u16 service_id, | |
122 | void (*tx) (void *, | |
123 | struct sk_buff *, | |
124 | enum htc_endpoint_id, | |
125 | bool txok), | |
126 | enum htc_endpoint_id *ep_id) | |
127 | { | |
128 | struct htc_service_connreq req; | |
129 | ||
130 | memset(&req, 0, sizeof(struct htc_service_connreq)); | |
131 | ||
132 | req.service_id = service_id; | |
133 | req.ep_callbacks.priv = priv; | |
134 | req.ep_callbacks.rx = ath9k_htc_rxep; | |
135 | req.ep_callbacks.tx = tx; | |
136 | ||
137 | return htc_connect_service(priv->htc, &req, ep_id); | |
138 | } | |
139 | ||
fa6e15e0 RM |
140 | static int ath9k_init_htc_services(struct ath9k_htc_priv *priv, u16 devid, |
141 | u32 drv_info) | |
fb9987d0 S |
142 | { |
143 | int ret; | |
144 | ||
145 | /* WMI CMD*/ | |
146 | ret = ath9k_wmi_connect(priv->htc, priv->wmi, &priv->wmi_cmd_ep); | |
147 | if (ret) | |
148 | goto err; | |
149 | ||
150 | /* Beacon */ | |
9c6dda4e | 151 | ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, ath9k_htc_beaconep, |
fb9987d0 S |
152 | &priv->beacon_ep); |
153 | if (ret) | |
154 | goto err; | |
155 | ||
156 | /* CAB */ | |
157 | ret = ath9k_htc_connect_svc(priv, WMI_CAB_SVC, ath9k_htc_txep, | |
158 | &priv->cab_ep); | |
159 | if (ret) | |
160 | goto err; | |
161 | ||
162 | ||
163 | /* UAPSD */ | |
164 | ret = ath9k_htc_connect_svc(priv, WMI_UAPSD_SVC, ath9k_htc_txep, | |
165 | &priv->uapsd_ep); | |
166 | if (ret) | |
167 | goto err; | |
168 | ||
169 | /* MGMT */ | |
170 | ret = ath9k_htc_connect_svc(priv, WMI_MGMT_SVC, ath9k_htc_txep, | |
171 | &priv->mgmt_ep); | |
172 | if (ret) | |
173 | goto err; | |
174 | ||
175 | /* DATA BE */ | |
176 | ret = ath9k_htc_connect_svc(priv, WMI_DATA_BE_SVC, ath9k_htc_txep, | |
177 | &priv->data_be_ep); | |
178 | if (ret) | |
179 | goto err; | |
180 | ||
181 | /* DATA BK */ | |
182 | ret = ath9k_htc_connect_svc(priv, WMI_DATA_BK_SVC, ath9k_htc_txep, | |
183 | &priv->data_bk_ep); | |
184 | if (ret) | |
185 | goto err; | |
186 | ||
187 | /* DATA VI */ | |
188 | ret = ath9k_htc_connect_svc(priv, WMI_DATA_VI_SVC, ath9k_htc_txep, | |
189 | &priv->data_vi_ep); | |
190 | if (ret) | |
191 | goto err; | |
192 | ||
193 | /* DATA VO */ | |
194 | ret = ath9k_htc_connect_svc(priv, WMI_DATA_VO_SVC, ath9k_htc_txep, | |
195 | &priv->data_vo_ep); | |
196 | if (ret) | |
197 | goto err; | |
198 | ||
6267dc70 S |
199 | /* |
200 | * Setup required credits before initializing HTC. | |
201 | * This is a bit hacky, but, since queuing is done in | |
202 | * the HIF layer, shouldn't matter much. | |
203 | */ | |
204 | ||
0b5ead91 | 205 | if (IS_AR7010_DEVICE(drv_info)) |
d108e8b9 | 206 | priv->htc->credits = 45; |
fa6e15e0 | 207 | else |
4e63f768 | 208 | priv->htc->credits = 33; |
6267dc70 | 209 | |
fb9987d0 S |
210 | ret = htc_init(priv->htc); |
211 | if (ret) | |
212 | goto err; | |
213 | ||
6267dc70 S |
214 | dev_info(priv->dev, "ath9k_htc: HTC initialized with %d credits\n", |
215 | priv->htc->credits); | |
216 | ||
fb9987d0 S |
217 | return 0; |
218 | ||
219 | err: | |
220 | dev_err(priv->dev, "ath9k_htc: Unable to initialize HTC services\n"); | |
221 | return ret; | |
222 | } | |
223 | ||
0c0280bd LR |
224 | static void ath9k_reg_notifier(struct wiphy *wiphy, |
225 | struct regulatory_request *request) | |
fb9987d0 S |
226 | { |
227 | struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); | |
228 | struct ath9k_htc_priv *priv = hw->priv; | |
229 | ||
0c0280bd LR |
230 | ath_reg_notifier_apply(wiphy, request, |
231 | ath9k_hw_regulatory(priv->ah)); | |
fb9987d0 S |
232 | } |
233 | ||
4a22fe10 | 234 | static unsigned int ath9k_regread(void *hw_priv, u32 reg_offset) |
fb9987d0 S |
235 | { |
236 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
237 | struct ath_common *common = ath9k_hw_common(ah); | |
238 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
239 | __be32 val, reg = cpu_to_be32(reg_offset); | |
240 | int r; | |
241 | ||
242 | r = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID, | |
243 | (u8 *) ®, sizeof(reg), | |
244 | (u8 *) &val, sizeof(val), | |
245 | 100); | |
246 | if (unlikely(r)) { | |
d2182b69 | 247 | ath_dbg(common, WMI, "REGISTER READ FAILED: (0x%04x, %d)\n", |
226afe68 | 248 | reg_offset, r); |
fb9987d0 S |
249 | return -EIO; |
250 | } | |
251 | ||
252 | return be32_to_cpu(val); | |
253 | } | |
254 | ||
09a525d3 SM |
255 | static void ath9k_multi_regread(void *hw_priv, u32 *addr, |
256 | u32 *val, u16 count) | |
257 | { | |
258 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
259 | struct ath_common *common = ath9k_hw_common(ah); | |
260 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
261 | __be32 tmpaddr[8]; | |
262 | __be32 tmpval[8]; | |
263 | int i, ret; | |
264 | ||
c8c91b02 BC |
265 | for (i = 0; i < count; i++) { |
266 | tmpaddr[i] = cpu_to_be32(addr[i]); | |
267 | } | |
09a525d3 | 268 | |
c8c91b02 | 269 | ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID, |
09a525d3 SM |
270 | (u8 *)tmpaddr , sizeof(u32) * count, |
271 | (u8 *)tmpval, sizeof(u32) * count, | |
272 | 100); | |
273 | if (unlikely(ret)) { | |
d2182b69 | 274 | ath_dbg(common, WMI, |
09a525d3 SM |
275 | "Multiple REGISTER READ FAILED (count: %d)\n", count); |
276 | } | |
277 | ||
c8c91b02 BC |
278 | for (i = 0; i < count; i++) { |
279 | val[i] = be32_to_cpu(tmpval[i]); | |
280 | } | |
09a525d3 SM |
281 | } |
282 | ||
2259ba38 OR |
283 | static void ath9k_regwrite_multi(struct ath_common *common) |
284 | { | |
285 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
286 | u32 rsp_status; | |
287 | int r; | |
288 | ||
289 | r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, | |
290 | (u8 *) &priv->wmi->multi_write, | |
291 | sizeof(struct register_write) * priv->wmi->multi_write_idx, | |
292 | (u8 *) &rsp_status, sizeof(rsp_status), | |
293 | 100); | |
294 | if (unlikely(r)) { | |
295 | ath_dbg(common, WMI, | |
296 | "REGISTER WRITE FAILED, multi len: %d\n", | |
297 | priv->wmi->multi_write_idx); | |
298 | } | |
299 | priv->wmi->multi_write_idx = 0; | |
300 | } | |
301 | ||
4a22fe10 | 302 | static void ath9k_regwrite_single(void *hw_priv, u32 val, u32 reg_offset) |
fb9987d0 S |
303 | { |
304 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
305 | struct ath_common *common = ath9k_hw_common(ah); | |
306 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
07b2fa5a | 307 | const __be32 buf[2] = { |
fb9987d0 S |
308 | cpu_to_be32(reg_offset), |
309 | cpu_to_be32(val), | |
310 | }; | |
311 | int r; | |
312 | ||
313 | r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, | |
314 | (u8 *) &buf, sizeof(buf), | |
315 | (u8 *) &val, sizeof(val), | |
316 | 100); | |
317 | if (unlikely(r)) { | |
d2182b69 | 318 | ath_dbg(common, WMI, "REGISTER WRITE FAILED:(0x%04x, %d)\n", |
226afe68 | 319 | reg_offset, r); |
fb9987d0 S |
320 | } |
321 | } | |
322 | ||
4a22fe10 S |
323 | static void ath9k_regwrite_buffer(void *hw_priv, u32 val, u32 reg_offset) |
324 | { | |
325 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
326 | struct ath_common *common = ath9k_hw_common(ah); | |
327 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
4a22fe10 S |
328 | |
329 | mutex_lock(&priv->wmi->multi_write_mutex); | |
330 | ||
331 | /* Store the register/value */ | |
332 | priv->wmi->multi_write[priv->wmi->multi_write_idx].reg = | |
333 | cpu_to_be32(reg_offset); | |
334 | priv->wmi->multi_write[priv->wmi->multi_write_idx].val = | |
335 | cpu_to_be32(val); | |
336 | ||
337 | priv->wmi->multi_write_idx++; | |
338 | ||
339 | /* If the buffer is full, send it out. */ | |
2259ba38 OR |
340 | if (priv->wmi->multi_write_idx == MAX_CMD_NUMBER) |
341 | ath9k_regwrite_multi(common); | |
4a22fe10 S |
342 | |
343 | mutex_unlock(&priv->wmi->multi_write_mutex); | |
344 | } | |
345 | ||
346 | static void ath9k_regwrite(void *hw_priv, u32 val, u32 reg_offset) | |
347 | { | |
348 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
349 | struct ath_common *common = ath9k_hw_common(ah); | |
350 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
351 | ||
352 | if (atomic_read(&priv->wmi->mwrite_cnt)) | |
353 | ath9k_regwrite_buffer(hw_priv, val, reg_offset); | |
354 | else | |
355 | ath9k_regwrite_single(hw_priv, val, reg_offset); | |
356 | } | |
357 | ||
358 | static void ath9k_enable_regwrite_buffer(void *hw_priv) | |
359 | { | |
360 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
361 | struct ath_common *common = ath9k_hw_common(ah); | |
362 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
363 | ||
364 | atomic_inc(&priv->wmi->mwrite_cnt); | |
365 | } | |
366 | ||
4a22fe10 S |
367 | static void ath9k_regwrite_flush(void *hw_priv) |
368 | { | |
369 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
370 | struct ath_common *common = ath9k_hw_common(ah); | |
371 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
4a22fe10 | 372 | |
435c1610 FF |
373 | atomic_dec(&priv->wmi->mwrite_cnt); |
374 | ||
4a22fe10 S |
375 | mutex_lock(&priv->wmi->multi_write_mutex); |
376 | ||
2259ba38 OR |
377 | if (priv->wmi->multi_write_idx) |
378 | ath9k_regwrite_multi(common); | |
4a22fe10 S |
379 | |
380 | mutex_unlock(&priv->wmi->multi_write_mutex); | |
381 | } | |
382 | ||
8badb50c OR |
383 | static void ath9k_reg_rmw_buffer(void *hw_priv, |
384 | u32 reg_offset, u32 set, u32 clr) | |
385 | { | |
386 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
387 | struct ath_common *common = ath9k_hw_common(ah); | |
388 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
389 | u32 rsp_status; | |
390 | int r; | |
391 | ||
392 | mutex_lock(&priv->wmi->multi_rmw_mutex); | |
393 | ||
394 | /* Store the register/value */ | |
395 | priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg = | |
396 | cpu_to_be32(reg_offset); | |
397 | priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set = | |
398 | cpu_to_be32(set); | |
399 | priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr = | |
400 | cpu_to_be32(clr); | |
401 | ||
402 | priv->wmi->multi_rmw_idx++; | |
403 | ||
404 | /* If the buffer is full, send it out. */ | |
405 | if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) { | |
406 | r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, | |
407 | (u8 *) &priv->wmi->multi_rmw, | |
408 | sizeof(struct register_write) * priv->wmi->multi_rmw_idx, | |
409 | (u8 *) &rsp_status, sizeof(rsp_status), | |
410 | 100); | |
411 | if (unlikely(r)) { | |
412 | ath_dbg(common, WMI, | |
413 | "REGISTER RMW FAILED, multi len: %d\n", | |
414 | priv->wmi->multi_rmw_idx); | |
415 | } | |
416 | priv->wmi->multi_rmw_idx = 0; | |
417 | } | |
418 | ||
419 | mutex_unlock(&priv->wmi->multi_rmw_mutex); | |
420 | } | |
421 | ||
422 | static void ath9k_reg_rmw_flush(void *hw_priv) | |
845e03c9 | 423 | { |
8badb50c OR |
424 | struct ath_hw *ah = (struct ath_hw *) hw_priv; |
425 | struct ath_common *common = ath9k_hw_common(ah); | |
426 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
427 | u32 rsp_status; | |
428 | int r; | |
429 | ||
430 | if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) | |
431 | return; | |
432 | ||
433 | atomic_dec(&priv->wmi->m_rmw_cnt); | |
845e03c9 | 434 | |
8badb50c OR |
435 | mutex_lock(&priv->wmi->multi_rmw_mutex); |
436 | ||
437 | if (priv->wmi->multi_rmw_idx) { | |
438 | r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, | |
439 | (u8 *) &priv->wmi->multi_rmw, | |
440 | sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx, | |
441 | (u8 *) &rsp_status, sizeof(rsp_status), | |
442 | 100); | |
443 | if (unlikely(r)) { | |
444 | ath_dbg(common, WMI, | |
445 | "REGISTER RMW FAILED, multi len: %d\n", | |
446 | priv->wmi->multi_rmw_idx); | |
447 | } | |
448 | priv->wmi->multi_rmw_idx = 0; | |
449 | } | |
450 | ||
451 | mutex_unlock(&priv->wmi->multi_rmw_mutex); | |
452 | } | |
453 | ||
454 | static void ath9k_enable_rmw_buffer(void *hw_priv) | |
455 | { | |
456 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
457 | struct ath_common *common = ath9k_hw_common(ah); | |
458 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
459 | ||
460 | if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) | |
461 | return; | |
462 | ||
463 | atomic_inc(&priv->wmi->m_rmw_cnt); | |
464 | } | |
465 | ||
466 | static u32 ath9k_reg_rmw_single(void *hw_priv, | |
467 | u32 reg_offset, u32 set, u32 clr) | |
468 | { | |
469 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
470 | struct ath_common *common = ath9k_hw_common(ah); | |
471 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
472 | struct register_rmw buf, buf_ret; | |
473 | int ret; | |
474 | u32 val = 0; | |
475 | ||
476 | buf.reg = cpu_to_be32(reg_offset); | |
477 | buf.set = cpu_to_be32(set); | |
478 | buf.clr = cpu_to_be32(clr); | |
479 | ||
480 | ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, | |
481 | (u8 *) &buf, sizeof(buf), | |
482 | (u8 *) &buf_ret, sizeof(buf_ret), | |
483 | 100); | |
484 | if (unlikely(ret)) { | |
485 | ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n", | |
486 | reg_offset, ret); | |
487 | } | |
845e03c9 FF |
488 | return val; |
489 | } | |
490 | ||
8badb50c OR |
491 | static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) |
492 | { | |
493 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | |
494 | struct ath_common *common = ath9k_hw_common(ah); | |
495 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
496 | ||
497 | if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) { | |
498 | u32 val; | |
499 | ||
500 | val = REG_READ(ah, reg_offset); | |
501 | val &= ~clr; | |
502 | val |= set; | |
503 | REG_WRITE(ah, reg_offset, val); | |
504 | ||
505 | return 0; | |
506 | } | |
507 | ||
508 | if (atomic_read(&priv->wmi->m_rmw_cnt)) | |
509 | ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr); | |
510 | else | |
511 | ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr); | |
512 | ||
513 | return 0; | |
514 | } | |
515 | ||
fb9987d0 S |
516 | static void ath_usb_read_cachesize(struct ath_common *common, int *csz) |
517 | { | |
518 | *csz = L1_CACHE_BYTES >> 2; | |
519 | } | |
520 | ||
521 | static bool ath_usb_eeprom_read(struct ath_common *common, u32 off, u16 *data) | |
522 | { | |
523 | struct ath_hw *ah = (struct ath_hw *) common->ah; | |
524 | ||
525 | (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); | |
526 | ||
527 | if (!ath9k_hw_wait(ah, | |
528 | AR_EEPROM_STATUS_DATA, | |
529 | AR_EEPROM_STATUS_DATA_BUSY | | |
530 | AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0, | |
531 | AH_WAIT_TIMEOUT)) | |
532 | return false; | |
533 | ||
534 | *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), | |
535 | AR_EEPROM_STATUS_DATA_VAL); | |
536 | ||
537 | return true; | |
538 | } | |
539 | ||
540 | static const struct ath_bus_ops ath9k_usb_bus_ops = { | |
497ad9ad | 541 | .ath_bus_type = ATH_USB, |
fb9987d0 S |
542 | .read_cachesize = ath_usb_read_cachesize, |
543 | .eeprom_read = ath_usb_eeprom_read, | |
544 | }; | |
545 | ||
fb9987d0 S |
546 | static int ath9k_init_queues(struct ath9k_htc_priv *priv) |
547 | { | |
548 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
549 | int i; | |
550 | ||
551 | for (i = 0; i < ARRAY_SIZE(priv->hwq_map); i++) | |
552 | priv->hwq_map[i] = -1; | |
553 | ||
a099874e OR |
554 | priv->beacon.beaconq = ath9k_hw_beaconq_setup(priv->ah); |
555 | if (priv->beacon.beaconq == -1) { | |
3800276a | 556 | ath_err(common, "Unable to setup BEACON xmit queue\n"); |
ca74b83b S |
557 | goto err; |
558 | } | |
559 | ||
560 | priv->cabq = ath9k_htc_cabq_setup(priv); | |
561 | if (priv->cabq == -1) { | |
3800276a | 562 | ath_err(common, "Unable to setup CAB xmit queue\n"); |
ca74b83b S |
563 | goto err; |
564 | } | |
565 | ||
bea843c7 | 566 | if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_BE)) { |
3800276a | 567 | ath_err(common, "Unable to setup xmit queue for BE traffic\n"); |
fb9987d0 S |
568 | goto err; |
569 | } | |
570 | ||
bea843c7 | 571 | if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_BK)) { |
3800276a | 572 | ath_err(common, "Unable to setup xmit queue for BK traffic\n"); |
fb9987d0 S |
573 | goto err; |
574 | } | |
bea843c7 | 575 | if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_VI)) { |
3800276a | 576 | ath_err(common, "Unable to setup xmit queue for VI traffic\n"); |
fb9987d0 S |
577 | goto err; |
578 | } | |
bea843c7 | 579 | if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_VO)) { |
3800276a | 580 | ath_err(common, "Unable to setup xmit queue for VO traffic\n"); |
fb9987d0 S |
581 | goto err; |
582 | } | |
583 | ||
584 | return 0; | |
585 | ||
586 | err: | |
587 | return -EINVAL; | |
588 | } | |
589 | ||
fb9987d0 S |
590 | static void ath9k_init_misc(struct ath9k_htc_priv *priv) |
591 | { | |
592 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
593 | ||
364734fa | 594 | memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); |
fb9987d0 | 595 | |
e5ba18c6 | 596 | common->last_rssi = ATH_RSSI_DUMMY_MARKER; |
9f01a84e | 597 | priv->ah->opmode = NL80211_IFTYPE_STATION; |
88a2e3fb OR |
598 | |
599 | priv->spec_priv.ah = priv->ah; | |
600 | priv->spec_priv.spec_config.enabled = 0; | |
a534f3b6 | 601 | priv->spec_priv.spec_config.short_repeat = true; |
88a2e3fb OR |
602 | priv->spec_priv.spec_config.count = 8; |
603 | priv->spec_priv.spec_config.endless = false; | |
604 | priv->spec_priv.spec_config.period = 0x12; | |
605 | priv->spec_priv.spec_config.fft_period = 0x02; | |
fb9987d0 S |
606 | } |
607 | ||
21cb9879 | 608 | static int ath9k_init_priv(struct ath9k_htc_priv *priv, |
fa6e15e0 RM |
609 | u16 devid, char *product, |
610 | u32 drv_info) | |
fb9987d0 S |
611 | { |
612 | struct ath_hw *ah = NULL; | |
613 | struct ath_common *common; | |
832f6a18 | 614 | int i, ret = 0, csz = 0; |
fb9987d0 | 615 | |
fb9987d0 S |
616 | ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); |
617 | if (!ah) | |
618 | return -ENOMEM; | |
619 | ||
31f023a1 | 620 | ah->dev = priv->dev; |
c393d179 | 621 | ah->hw = priv->hw; |
fb9987d0 | 622 | ah->hw_version.devid = devid; |
0b5ead91 | 623 | ah->hw_version.usbdev = drv_info; |
f8afa42b | 624 | ah->ah_flags |= AH_USE_EEPROM; |
f9f84e96 FF |
625 | ah->reg_ops.read = ath9k_regread; |
626 | ah->reg_ops.multi_read = ath9k_multi_regread; | |
627 | ah->reg_ops.write = ath9k_regwrite; | |
628 | ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer; | |
629 | ah->reg_ops.write_flush = ath9k_regwrite_flush; | |
8badb50c OR |
630 | ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer; |
631 | ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush; | |
845e03c9 | 632 | ah->reg_ops.rmw = ath9k_reg_rmw; |
fb9987d0 S |
633 | priv->ah = ah; |
634 | ||
635 | common = ath9k_hw_common(ah); | |
f9f84e96 | 636 | common->ops = &ah->reg_ops; |
94cd95c2 | 637 | common->ps_ops = &ath9k_htc_ps_ops; |
fb9987d0 S |
638 | common->bus_ops = &ath9k_usb_bus_ops; |
639 | common->ah = ah; | |
640 | common->hw = priv->hw; | |
641 | common->priv = priv; | |
642 | common->debug_mask = ath9k_debug; | |
7f34778e | 643 | common->btcoex_enabled = ath9k_htc_btcoex_enable == 1; |
92c3f7ef | 644 | set_bit(ATH_OP_INVALID, &common->op_flags); |
fb9987d0 | 645 | |
fb9987d0 | 646 | spin_lock_init(&priv->beacon_lock); |
658ef04f | 647 | spin_lock_init(&priv->tx.tx_lock); |
fb9987d0 | 648 | mutex_init(&priv->mutex); |
bde748a4 | 649 | mutex_init(&priv->htc_pm_lock); |
fb9987d0 S |
650 | tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet, |
651 | (unsigned long)priv); | |
27876a29 | 652 | tasklet_init(&priv->tx_failed_tasklet, ath9k_tx_failed_tasklet, |
73908674 | 653 | (unsigned long)priv); |
a236254c | 654 | INIT_DELAYED_WORK(&priv->ani_work, ath9k_htc_ani_work); |
bde748a4 | 655 | INIT_WORK(&priv->ps_work, ath9k_ps_work); |
73908674 | 656 | INIT_WORK(&priv->fatal_work, ath9k_fatal_work); |
859c3ca1 SM |
657 | setup_timer(&priv->tx.cleanup_timer, ath9k_htc_tx_cleanup_timer, |
658 | (unsigned long)priv); | |
fb9987d0 S |
659 | |
660 | /* | |
661 | * Cache line size is used to size and align various | |
662 | * structures used to communicate with the hardware. | |
663 | */ | |
664 | ath_read_cachesize(common, &csz); | |
665 | common->cachelsz = csz << 2; /* convert to bytes */ | |
666 | ||
667 | ret = ath9k_hw_init(ah); | |
668 | if (ret) { | |
3800276a JP |
669 | ath_err(common, |
670 | "Unable to initialize hardware; initialization status: %d\n", | |
671 | ret); | |
fb9987d0 S |
672 | goto err_hw; |
673 | } | |
674 | ||
fb9987d0 S |
675 | ret = ath9k_init_queues(priv); |
676 | if (ret) | |
677 | goto err_queues; | |
678 | ||
832f6a18 | 679 | for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) |
3c4816d9 | 680 | priv->beacon.bslot[i] = NULL; |
cc24c86f | 681 | priv->beacon.slottime = ATH9K_SLOT_TIME_9; |
832f6a18 | 682 | |
31f023a1 | 683 | ath9k_cmn_init_channels_rates(common); |
f82b4bde | 684 | ath9k_cmn_init_crypto(ah); |
fb9987d0 | 685 | ath9k_init_misc(priv); |
cee5341d | 686 | ath9k_htc_init_btcoex(priv, product); |
21cb9879 | 687 | |
fb9987d0 S |
688 | return 0; |
689 | ||
690 | err_queues: | |
fb9987d0 S |
691 | ath9k_hw_deinit(ah); |
692 | err_hw: | |
693 | ||
694 | kfree(ah); | |
695 | priv->ah = NULL; | |
696 | ||
697 | return ret; | |
698 | } | |
699 | ||
8b0b6be5 MSS |
700 | static const struct ieee80211_iface_limit if_limits[] = { |
701 | { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | | |
702 | BIT(NL80211_IFTYPE_P2P_CLIENT) }, | |
703 | { .max = 2, .types = BIT(NL80211_IFTYPE_AP) | | |
0c9acaa8 TP |
704 | #ifdef CONFIG_MAC80211_MESH |
705 | BIT(NL80211_IFTYPE_MESH_POINT) | | |
706 | #endif | |
707 | BIT(NL80211_IFTYPE_P2P_GO) }, | |
8b0b6be5 MSS |
708 | }; |
709 | ||
710 | static const struct ieee80211_iface_combination if_comb = { | |
711 | .limits = if_limits, | |
712 | .n_limits = ARRAY_SIZE(if_limits), | |
713 | .max_interfaces = 2, | |
714 | .num_different_channels = 1, | |
715 | }; | |
716 | ||
fb9987d0 S |
717 | static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, |
718 | struct ieee80211_hw *hw) | |
719 | { | |
e9fb5888 | 720 | struct ath_hw *ah = priv->ah; |
fb9987d0 | 721 | struct ath_common *common = ath9k_hw_common(priv->ah); |
156652bb | 722 | struct base_eep_header *pBase; |
fb9987d0 | 723 | |
30686bf7 JB |
724 | ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); |
725 | ieee80211_hw_set(hw, MFP_CAPABLE); | |
726 | ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); | |
727 | ieee80211_hw_set(hw, PS_NULLFUNC_STACK); | |
728 | ieee80211_hw_set(hw, RX_INCLUDES_FCS); | |
729 | ieee80211_hw_set(hw, HAS_RATE_CONTROL); | |
730 | ieee80211_hw_set(hw, SPECTRUM_MGMT); | |
731 | ieee80211_hw_set(hw, SIGNAL_DBM); | |
732 | ieee80211_hw_set(hw, AMPDU_AGGREGATION); | |
fb9987d0 | 733 | |
6bca610d | 734 | if (ath9k_ps_enable) |
30686bf7 | 735 | ieee80211_hw_set(hw, SUPPORTS_PS); |
6bca610d | 736 | |
fb9987d0 S |
737 | hw->wiphy->interface_modes = |
738 | BIT(NL80211_IFTYPE_STATION) | | |
09d5b94d SM |
739 | BIT(NL80211_IFTYPE_ADHOC) | |
740 | BIT(NL80211_IFTYPE_AP) | | |
741 | BIT(NL80211_IFTYPE_P2P_GO) | | |
594e65b6 | 742 | BIT(NL80211_IFTYPE_P2P_CLIENT) | |
862a336c JK |
743 | BIT(NL80211_IFTYPE_MESH_POINT) | |
744 | BIT(NL80211_IFTYPE_OCB); | |
fb9987d0 | 745 | |
8b0b6be5 MSS |
746 | hw->wiphy->iface_combinations = &if_comb; |
747 | hw->wiphy->n_iface_combinations = 1; | |
748 | ||
bde748a4 VN |
749 | hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; |
750 | ||
81ddbb5c | 751 | hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN | |
f0e44962 CYY |
752 | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | |
753 | WIPHY_FLAG_HAS_CHANNEL_SWITCH; | |
d7d312ca | 754 | |
ba1fa3cd CK |
755 | hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; |
756 | ||
fb9987d0 | 757 | hw->queues = 4; |
fb1c078e | 758 | hw->max_listen_interval = 1; |
3a0593ef | 759 | |
fb9987d0 S |
760 | hw->vif_data_size = sizeof(struct ath9k_htc_vif); |
761 | hw->sta_data_size = sizeof(struct ath9k_htc_sta); | |
762 | ||
763 | /* tx_frame_hdr is larger than tx_mgmt_hdr anyway */ | |
764 | hw->extra_tx_headroom = sizeof(struct tx_frame_hdr) + | |
765 | sizeof(struct htc_frame_hdr) + 4; | |
766 | ||
d4659912 | 767 | if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) |
57fbcce3 JB |
768 | hw->wiphy->bands[NL80211_BAND_2GHZ] = |
769 | &common->sbands[NL80211_BAND_2GHZ]; | |
d4659912 | 770 | if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) |
57fbcce3 JB |
771 | hw->wiphy->bands[NL80211_BAND_5GHZ] = |
772 | &common->sbands[NL80211_BAND_5GHZ]; | |
fb9987d0 | 773 | |
6da2f4ad | 774 | ath9k_cmn_reload_chainmask(ah); |
fb9987d0 | 775 | |
156652bb BG |
776 | pBase = ath9k_htc_get_eeprom_base(priv); |
777 | if (pBase) { | |
778 | hw->wiphy->available_antennas_rx = pBase->rxMask; | |
779 | hw->wiphy->available_antennas_tx = pBase->txMask; | |
780 | } | |
781 | ||
fb9987d0 S |
782 | SET_IEEE80211_PERM_ADDR(hw, common->macaddr); |
783 | } | |
784 | ||
29bbfb24 SM |
785 | static int ath9k_init_firmware_version(struct ath9k_htc_priv *priv) |
786 | { | |
787 | struct ieee80211_hw *hw = priv->hw; | |
788 | struct wmi_fw_version cmd_rsp; | |
789 | int ret; | |
790 | ||
791 | memset(&cmd_rsp, 0, sizeof(cmd_rsp)); | |
792 | ||
793 | WMI_CMD(WMI_GET_FW_VERSION); | |
794 | if (ret) | |
795 | return -EINVAL; | |
796 | ||
797 | priv->fw_version_major = be16_to_cpu(cmd_rsp.major); | |
798 | priv->fw_version_minor = be16_to_cpu(cmd_rsp.minor); | |
799 | ||
81135548 | 800 | snprintf(hw->wiphy->fw_version, sizeof(hw->wiphy->fw_version), "%d.%d", |
29bbfb24 SM |
801 | priv->fw_version_major, |
802 | priv->fw_version_minor); | |
803 | ||
804 | dev_info(priv->dev, "ath9k_htc: FW Version: %d.%d\n", | |
805 | priv->fw_version_major, | |
806 | priv->fw_version_minor); | |
807 | ||
3a0593ef SM |
808 | /* |
809 | * Check if the available FW matches the driver's | |
810 | * required version. | |
811 | */ | |
812 | if (priv->fw_version_major != MAJOR_VERSION_REQ || | |
319e7bd9 | 813 | priv->fw_version_minor < MINOR_VERSION_REQ) { |
3a0593ef SM |
814 | dev_err(priv->dev, "ath9k_htc: Please upgrade to FW version %d.%d\n", |
815 | MAJOR_VERSION_REQ, MINOR_VERSION_REQ); | |
816 | return -EINVAL; | |
817 | } | |
818 | ||
8badb50c OR |
819 | if (priv->fw_version_major == 1 && priv->fw_version_minor < 4) |
820 | set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags); | |
821 | ||
822 | dev_info(priv->dev, "FW RMW support: %s\n", | |
823 | test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On"); | |
824 | ||
29bbfb24 SM |
825 | return 0; |
826 | } | |
827 | ||
21cb9879 | 828 | static int ath9k_init_device(struct ath9k_htc_priv *priv, |
fa6e15e0 | 829 | u16 devid, char *product, u32 drv_info) |
fb9987d0 S |
830 | { |
831 | struct ieee80211_hw *hw = priv->hw; | |
832 | struct ath_common *common; | |
833 | struct ath_hw *ah; | |
834 | int error = 0; | |
835 | struct ath_regulatory *reg; | |
3e3f1d19 | 836 | char hw_name[64]; |
fb9987d0 S |
837 | |
838 | /* Bring up device */ | |
fa6e15e0 | 839 | error = ath9k_init_priv(priv, devid, product, drv_info); |
fb9987d0 S |
840 | if (error != 0) |
841 | goto err_init; | |
842 | ||
843 | ah = priv->ah; | |
844 | common = ath9k_hw_common(ah); | |
845 | ath9k_set_hw_capab(priv, hw); | |
846 | ||
29bbfb24 SM |
847 | error = ath9k_init_firmware_version(priv); |
848 | if (error != 0) | |
849 | goto err_fw; | |
850 | ||
fb9987d0 S |
851 | /* Initialize regulatory */ |
852 | error = ath_regd_init(&common->regulatory, priv->hw->wiphy, | |
853 | ath9k_reg_notifier); | |
854 | if (error) | |
855 | goto err_regd; | |
856 | ||
857 | reg = &common->regulatory; | |
858 | ||
859 | /* Setup TX */ | |
860 | error = ath9k_tx_init(priv); | |
861 | if (error != 0) | |
862 | goto err_tx; | |
863 | ||
864 | /* Setup RX */ | |
865 | error = ath9k_rx_init(priv); | |
866 | if (error != 0) | |
867 | goto err_rx; | |
868 | ||
dc2a87f5 | 869 | ath9k_hw_disable(priv->ah); |
d244f21e SM |
870 | #ifdef CONFIG_MAC80211_LEDS |
871 | /* must be initialized before ieee80211_register_hw */ | |
872 | priv->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(priv->hw, | |
873 | IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_htc_tpt_blink, | |
874 | ARRAY_SIZE(ath9k_htc_tpt_blink)); | |
875 | #endif | |
876 | ||
fb9987d0 S |
877 | /* Register with mac80211 */ |
878 | error = ieee80211_register_hw(hw); | |
879 | if (error) | |
880 | goto err_register; | |
881 | ||
882 | /* Handle world regulatory */ | |
883 | if (!ath_is_world_regd(reg)) { | |
884 | error = regulatory_hint(hw->wiphy, reg->alpha2); | |
885 | if (error) | |
886 | goto err_world; | |
887 | } | |
888 | ||
e5facc75 RM |
889 | error = ath9k_htc_init_debug(priv->ah); |
890 | if (error) { | |
891 | ath_err(common, "Unable to create debugfs files\n"); | |
892 | goto err_world; | |
893 | } | |
894 | ||
d2182b69 JP |
895 | ath_dbg(common, CONFIG, |
896 | "WMI:%d, BCN:%d, CAB:%d, UAPSD:%d, MGMT:%d, BE:%d, BK:%d, VI:%d, VO:%d\n", | |
3e3f1d19 SM |
897 | priv->wmi_cmd_ep, |
898 | priv->beacon_ep, | |
899 | priv->cab_ep, | |
900 | priv->uapsd_ep, | |
901 | priv->mgmt_ep, | |
902 | priv->data_be_ep, | |
903 | priv->data_bk_ep, | |
904 | priv->data_vi_ep, | |
905 | priv->data_vo_ep); | |
906 | ||
907 | ath9k_hw_name(priv->ah, hw_name, sizeof(hw_name)); | |
908 | wiphy_info(hw->wiphy, "%s\n", hw_name); | |
909 | ||
fb9987d0 S |
910 | ath9k_init_leds(priv); |
911 | ath9k_start_rfkill_poll(priv); | |
912 | ||
913 | return 0; | |
914 | ||
915 | err_world: | |
916 | ieee80211_unregister_hw(hw); | |
917 | err_register: | |
918 | ath9k_rx_cleanup(priv); | |
919 | err_rx: | |
920 | ath9k_tx_cleanup(priv); | |
921 | err_tx: | |
922 | /* Nothing */ | |
923 | err_regd: | |
29bbfb24 SM |
924 | /* Nothing */ |
925 | err_fw: | |
fb9987d0 S |
926 | ath9k_deinit_priv(priv); |
927 | err_init: | |
928 | return error; | |
929 | } | |
930 | ||
931 | int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, | |
fa6e15e0 | 932 | u16 devid, char *product, u32 drv_info) |
fb9987d0 S |
933 | { |
934 | struct ieee80211_hw *hw; | |
935 | struct ath9k_htc_priv *priv; | |
936 | int ret; | |
937 | ||
938 | hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops); | |
939 | if (!hw) | |
940 | return -ENOMEM; | |
941 | ||
942 | priv = hw->priv; | |
943 | priv->hw = hw; | |
944 | priv->htc = htc_handle; | |
945 | priv->dev = dev; | |
946 | htc_handle->drv_priv = priv; | |
947 | SET_IEEE80211_DEV(hw, priv->dev); | |
948 | ||
949 | ret = ath9k_htc_wait_for_target(priv); | |
950 | if (ret) | |
951 | goto err_free; | |
952 | ||
953 | priv->wmi = ath9k_init_wmi(priv); | |
954 | if (!priv->wmi) { | |
955 | ret = -EINVAL; | |
956 | goto err_free; | |
957 | } | |
958 | ||
fa6e15e0 | 959 | ret = ath9k_init_htc_services(priv, devid, drv_info); |
fb9987d0 S |
960 | if (ret) |
961 | goto err_init; | |
962 | ||
fa6e15e0 | 963 | ret = ath9k_init_device(priv, devid, product, drv_info); |
fb9987d0 S |
964 | if (ret) |
965 | goto err_init; | |
966 | ||
967 | return 0; | |
968 | ||
969 | err_init: | |
970 | ath9k_deinit_wmi(priv); | |
971 | err_free: | |
972 | ieee80211_free_hw(hw); | |
973 | return ret; | |
974 | } | |
975 | ||
976 | void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug) | |
977 | { | |
978 | if (htc_handle->drv_priv) { | |
a3be14b7 S |
979 | |
980 | /* Check if the device has been yanked out. */ | |
981 | if (hotunplug) | |
97dcec57 | 982 | htc_handle->drv_priv->ah->ah_flags |= AH_UNPLUGGED; |
a3be14b7 | 983 | |
fb9987d0 S |
984 | ath9k_deinit_device(htc_handle->drv_priv); |
985 | ath9k_deinit_wmi(htc_handle->drv_priv); | |
986 | ieee80211_free_hw(htc_handle->drv_priv->hw); | |
987 | } | |
988 | } | |
989 | ||
990 | #ifdef CONFIG_PM | |
f933ebed SM |
991 | |
992 | void ath9k_htc_suspend(struct htc_target *htc_handle) | |
993 | { | |
994 | ath9k_htc_setpower(htc_handle->drv_priv, ATH9K_PM_FULL_SLEEP); | |
995 | } | |
996 | ||
fb9987d0 S |
997 | int ath9k_htc_resume(struct htc_target *htc_handle) |
998 | { | |
fa6e15e0 | 999 | struct ath9k_htc_priv *priv = htc_handle->drv_priv; |
fb9987d0 S |
1000 | int ret; |
1001 | ||
fa6e15e0 | 1002 | ret = ath9k_htc_wait_for_target(priv); |
fb9987d0 S |
1003 | if (ret) |
1004 | return ret; | |
1005 | ||
fa6e15e0 | 1006 | ret = ath9k_init_htc_services(priv, priv->ah->hw_version.devid, |
0b5ead91 | 1007 | priv->ah->hw_version.usbdev); |
1e51acaa OR |
1008 | ath9k_configure_leds(priv); |
1009 | ||
fb9987d0 S |
1010 | return ret; |
1011 | } | |
1012 | #endif | |
1013 | ||
1014 | static int __init ath9k_htc_init(void) | |
1015 | { | |
e5facc75 | 1016 | if (ath9k_hif_usb_init() < 0) { |
516304b0 | 1017 | pr_err("No USB devices found, driver not installed\n"); |
e5facc75 | 1018 | return -ENODEV; |
fb9987d0 S |
1019 | } |
1020 | ||
1021 | return 0; | |
fb9987d0 S |
1022 | } |
1023 | module_init(ath9k_htc_init); | |
1024 | ||
1025 | static void __exit ath9k_htc_exit(void) | |
1026 | { | |
1027 | ath9k_hif_usb_exit(); | |
516304b0 | 1028 | pr_info("Driver unloaded\n"); |
fb9987d0 S |
1029 | } |
1030 | module_exit(ath9k_htc_exit); |