diff options
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi/main.c')
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/main.c | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c new file mode 100644 index 000000000000..6a2640f16b6d --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/main.c | |||
@@ -0,0 +1,680 @@ | |||
1 | /* | ||
2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
3 | * | ||
4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * | ||
10 | * * Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer. | ||
12 | * * Redistributions in binary form must reproduce the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer in | ||
14 | * the documentation and/or other materials provided with the | ||
15 | * distribution. | ||
16 | * * Neither the name of Intel Corporation nor the names of its | ||
17 | * contributors may be used to endorse or promote products derived | ||
18 | * from this software without specific prior written permission. | ||
19 | * | ||
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
31 | * | ||
32 | * | ||
33 | * Intel Corporation <ilw@linux.intel.com> | ||
34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
35 | * Zhu Yi <yi.zhu@intel.com> | ||
36 | * | ||
37 | */ | ||
38 | |||
39 | #include <linux/kernel.h> | ||
40 | #include <linux/netdevice.h> | ||
41 | #include <linux/ieee80211.h> | ||
42 | #include <linux/wireless.h> | ||
43 | |||
44 | #include "iwm.h" | ||
45 | #include "debug.h" | ||
46 | #include "bus.h" | ||
47 | #include "umac.h" | ||
48 | #include "commands.h" | ||
49 | #include "hal.h" | ||
50 | #include "fw.h" | ||
51 | #include "rx.h" | ||
52 | |||
53 | static struct iwm_conf def_iwm_conf = { | ||
54 | |||
55 | .sdio_ior_timeout = 5000, | ||
56 | .init_calib_map = BIT(PHY_CALIBRATE_DC_CMD) | | ||
57 | BIT(PHY_CALIBRATE_LO_CMD) | | ||
58 | BIT(PHY_CALIBRATE_TX_IQ_CMD) | | ||
59 | BIT(PHY_CALIBRATE_RX_IQ_CMD), | ||
60 | .periodic_calib_map = BIT(PHY_CALIBRATE_DC_CMD) | | ||
61 | BIT(PHY_CALIBRATE_LO_CMD) | | ||
62 | BIT(PHY_CALIBRATE_TX_IQ_CMD) | | ||
63 | BIT(PHY_CALIBRATE_RX_IQ_CMD) | | ||
64 | BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD), | ||
65 | .reset_on_fatal_err = 1, | ||
66 | .auto_connect = 1, | ||
67 | .wimax_not_present = 0, | ||
68 | .enable_qos = 1, | ||
69 | .mode = UMAC_MODE_BSS, | ||
70 | |||
71 | /* UMAC configuration */ | ||
72 | .power_index = 0, | ||
73 | .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, | ||
74 | .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, | ||
75 | .cts_to_self = 0, | ||
76 | |||
77 | .assoc_timeout = 2, | ||
78 | .roam_timeout = 10, | ||
79 | .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G, | ||
80 | .coexist_mode = COEX_MODE_CM, | ||
81 | |||
82 | /* IBSS */ | ||
83 | .ibss_band = UMAC_BAND_2GHZ, | ||
84 | .ibss_channel = 1, | ||
85 | |||
86 | .mac_addr = {0x00, 0x02, 0xb3, 0x01, 0x02, 0x03}, | ||
87 | }; | ||
88 | |||
89 | static int modparam_reset; | ||
90 | module_param_named(reset, modparam_reset, bool, 0644); | ||
91 | MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])"); | ||
92 | |||
93 | int iwm_mode_to_nl80211_iftype(int mode) | ||
94 | { | ||
95 | switch (mode) { | ||
96 | case UMAC_MODE_BSS: | ||
97 | return NL80211_IFTYPE_STATION; | ||
98 | case UMAC_MODE_IBSS: | ||
99 | return NL80211_IFTYPE_ADHOC; | ||
100 | default: | ||
101 | return NL80211_IFTYPE_UNSPECIFIED; | ||
102 | } | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static void iwm_statistics_request(struct work_struct *work) | ||
108 | { | ||
109 | struct iwm_priv *iwm = | ||
110 | container_of(work, struct iwm_priv, stats_request.work); | ||
111 | |||
112 | iwm_send_umac_stats_req(iwm, 0); | ||
113 | } | ||
114 | |||
115 | static void iwm_reset_worker(struct work_struct *work) | ||
116 | { | ||
117 | struct iwm_priv *iwm; | ||
118 | struct iwm_umac_profile *profile = NULL; | ||
119 | int uninitialized_var(ret), retry = 0; | ||
120 | |||
121 | iwm = container_of(work, struct iwm_priv, reset_worker); | ||
122 | |||
123 | if (iwm->umac_profile_active) { | ||
124 | profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL); | ||
125 | if (profile) | ||
126 | memcpy(profile, iwm->umac_profile, sizeof(*profile)); | ||
127 | else | ||
128 | IWM_ERR(iwm, "Couldn't alloc memory for profile\n"); | ||
129 | } | ||
130 | |||
131 | iwm_down(iwm); | ||
132 | |||
133 | while (retry++ < 3) { | ||
134 | ret = iwm_up(iwm); | ||
135 | if (!ret) | ||
136 | break; | ||
137 | |||
138 | schedule_timeout_uninterruptible(10 * HZ); | ||
139 | } | ||
140 | |||
141 | if (ret) { | ||
142 | IWM_WARN(iwm, "iwm_up() failed: %d\n", ret); | ||
143 | |||
144 | kfree(profile); | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | if (profile) { | ||
149 | IWM_DBG_MLME(iwm, DBG, "Resend UMAC profile\n"); | ||
150 | memcpy(iwm->umac_profile, profile, sizeof(*profile)); | ||
151 | iwm_send_mlme_profile(iwm); | ||
152 | kfree(profile); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | static void iwm_watchdog(unsigned long data) | ||
157 | { | ||
158 | struct iwm_priv *iwm = (struct iwm_priv *)data; | ||
159 | |||
160 | IWM_WARN(iwm, "Watchdog expired: UMAC stalls!\n"); | ||
161 | |||
162 | if (modparam_reset) | ||
163 | schedule_work(&iwm->reset_worker); | ||
164 | } | ||
165 | |||
166 | int iwm_priv_init(struct iwm_priv *iwm) | ||
167 | { | ||
168 | int i; | ||
169 | char name[32]; | ||
170 | |||
171 | iwm->status = 0; | ||
172 | INIT_LIST_HEAD(&iwm->pending_notif); | ||
173 | init_waitqueue_head(&iwm->notif_queue); | ||
174 | init_waitqueue_head(&iwm->nonwifi_queue); | ||
175 | init_waitqueue_head(&iwm->mlme_queue); | ||
176 | memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf)); | ||
177 | spin_lock_init(&iwm->tx_credit.lock); | ||
178 | INIT_LIST_HEAD(&iwm->wifi_pending_cmd); | ||
179 | INIT_LIST_HEAD(&iwm->nonwifi_pending_cmd); | ||
180 | iwm->wifi_seq_num = UMAC_WIFI_SEQ_NUM_BASE; | ||
181 | iwm->nonwifi_seq_num = UMAC_NONWIFI_SEQ_NUM_BASE; | ||
182 | spin_lock_init(&iwm->cmd_lock); | ||
183 | iwm->scan_id = 1; | ||
184 | INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request); | ||
185 | INIT_WORK(&iwm->reset_worker, iwm_reset_worker); | ||
186 | INIT_LIST_HEAD(&iwm->bss_list); | ||
187 | |||
188 | skb_queue_head_init(&iwm->rx_list); | ||
189 | INIT_LIST_HEAD(&iwm->rx_tickets); | ||
190 | for (i = 0; i < IWM_RX_ID_HASH; i++) | ||
191 | INIT_LIST_HEAD(&iwm->rx_packets[i]); | ||
192 | |||
193 | INIT_WORK(&iwm->rx_worker, iwm_rx_worker); | ||
194 | |||
195 | iwm->rx_wq = create_singlethread_workqueue(KBUILD_MODNAME "_rx"); | ||
196 | if (!iwm->rx_wq) | ||
197 | return -EAGAIN; | ||
198 | |||
199 | for (i = 0; i < IWM_TX_QUEUES; i++) { | ||
200 | INIT_WORK(&iwm->txq[i].worker, iwm_tx_worker); | ||
201 | snprintf(name, 32, KBUILD_MODNAME "_tx_%d", i); | ||
202 | iwm->txq[i].id = i; | ||
203 | iwm->txq[i].wq = create_singlethread_workqueue(name); | ||
204 | if (!iwm->txq[i].wq) | ||
205 | return -EAGAIN; | ||
206 | |||
207 | skb_queue_head_init(&iwm->txq[i].queue); | ||
208 | } | ||
209 | |||
210 | for (i = 0; i < IWM_NUM_KEYS; i++) | ||
211 | memset(&iwm->keys[i], 0, sizeof(struct iwm_key)); | ||
212 | |||
213 | iwm->default_key = NULL; | ||
214 | |||
215 | init_timer(&iwm->watchdog); | ||
216 | iwm->watchdog.function = iwm_watchdog; | ||
217 | iwm->watchdog.data = (unsigned long)iwm; | ||
218 | |||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * We reset all the structures, and we reset the UMAC. | ||
224 | * After calling this routine, you're expected to reload | ||
225 | * the firmware. | ||
226 | */ | ||
227 | void iwm_reset(struct iwm_priv *iwm) | ||
228 | { | ||
229 | struct iwm_notif *notif, *next; | ||
230 | |||
231 | if (test_bit(IWM_STATUS_READY, &iwm->status)) | ||
232 | iwm_target_reset(iwm); | ||
233 | |||
234 | iwm->status = 0; | ||
235 | iwm->scan_id = 1; | ||
236 | |||
237 | list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) { | ||
238 | list_del(¬if->pending); | ||
239 | kfree(notif->buf); | ||
240 | kfree(notif); | ||
241 | } | ||
242 | |||
243 | iwm_cmd_flush(iwm); | ||
244 | |||
245 | flush_workqueue(iwm->rx_wq); | ||
246 | |||
247 | iwm_link_off(iwm); | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Notification code: | ||
252 | * | ||
253 | * We're faced with the following issue: Any host command can | ||
254 | * have an answer or not, and if there's an answer to expect, | ||
255 | * it can be treated synchronously or asynchronously. | ||
256 | * To work around the synchronous answer case, we implemented | ||
257 | * our notification mechanism. | ||
258 | * When a code path needs to wait for a command response | ||
259 | * synchronously, it calls notif_handle(), which waits for the | ||
260 | * right notification to show up, and then process it. Before | ||
261 | * starting to wait, it registered as a waiter for this specific | ||
262 | * answer (by toggling a bit in on of the handler_map), so that | ||
263 | * the rx code knows that it needs to send a notification to the | ||
264 | * waiting processes. It does so by calling iwm_notif_send(), | ||
265 | * which adds the notification to the pending notifications list, | ||
266 | * and then wakes the waiting processes up. | ||
267 | */ | ||
268 | int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd, | ||
269 | u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size) | ||
270 | { | ||
271 | struct iwm_notif *notif; | ||
272 | |||
273 | notif = kzalloc(sizeof(struct iwm_notif), GFP_KERNEL); | ||
274 | if (!notif) { | ||
275 | IWM_ERR(iwm, "Couldn't alloc memory for notification\n"); | ||
276 | return -ENOMEM; | ||
277 | } | ||
278 | |||
279 | INIT_LIST_HEAD(¬if->pending); | ||
280 | notif->cmd = cmd; | ||
281 | notif->cmd_id = cmd_id; | ||
282 | notif->src = source; | ||
283 | notif->buf = kzalloc(buf_size, GFP_KERNEL); | ||
284 | if (!notif->buf) { | ||
285 | IWM_ERR(iwm, "Couldn't alloc notification buffer\n"); | ||
286 | kfree(notif); | ||
287 | return -ENOMEM; | ||
288 | } | ||
289 | notif->buf_size = buf_size; | ||
290 | memcpy(notif->buf, buf, buf_size); | ||
291 | list_add_tail(¬if->pending, &iwm->pending_notif); | ||
292 | |||
293 | wake_up_interruptible(&iwm->notif_queue); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static struct iwm_notif *iwm_notif_find(struct iwm_priv *iwm, u32 cmd, | ||
299 | u8 source) | ||
300 | { | ||
301 | struct iwm_notif *notif, *next; | ||
302 | |||
303 | list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) { | ||
304 | if ((notif->cmd_id == cmd) && (notif->src == source)) { | ||
305 | list_del(¬if->pending); | ||
306 | return notif; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | return NULL; | ||
311 | } | ||
312 | |||
313 | static struct iwm_notif *iwm_notif_wait(struct iwm_priv *iwm, u32 cmd, | ||
314 | u8 source, long timeout) | ||
315 | { | ||
316 | int ret; | ||
317 | struct iwm_notif *notif; | ||
318 | unsigned long *map = NULL; | ||
319 | |||
320 | switch (source) { | ||
321 | case IWM_SRC_LMAC: | ||
322 | map = &iwm->lmac_handler_map[0]; | ||
323 | break; | ||
324 | case IWM_SRC_UMAC: | ||
325 | map = &iwm->umac_handler_map[0]; | ||
326 | break; | ||
327 | case IWM_SRC_UDMA: | ||
328 | map = &iwm->udma_handler_map[0]; | ||
329 | break; | ||
330 | } | ||
331 | |||
332 | set_bit(cmd, map); | ||
333 | |||
334 | ret = wait_event_interruptible_timeout(iwm->notif_queue, | ||
335 | ((notif = iwm_notif_find(iwm, cmd, source)) != NULL), | ||
336 | timeout); | ||
337 | clear_bit(cmd, map); | ||
338 | |||
339 | if (!ret) | ||
340 | return NULL; | ||
341 | |||
342 | return notif; | ||
343 | } | ||
344 | |||
345 | int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout) | ||
346 | { | ||
347 | int ret; | ||
348 | struct iwm_notif *notif; | ||
349 | |||
350 | notif = iwm_notif_wait(iwm, cmd, source, timeout); | ||
351 | if (!notif) | ||
352 | return -ETIME; | ||
353 | |||
354 | ret = iwm_rx_handle_resp(iwm, notif->buf, notif->buf_size, notif->cmd); | ||
355 | kfree(notif->buf); | ||
356 | kfree(notif); | ||
357 | |||
358 | return ret; | ||
359 | } | ||
360 | |||
361 | static int iwm_config_boot_params(struct iwm_priv *iwm) | ||
362 | { | ||
363 | struct iwm_udma_nonwifi_cmd target_cmd; | ||
364 | int ret; | ||
365 | |||
366 | /* check Wimax is off and config debug monitor */ | ||
367 | if (iwm->conf.wimax_not_present) { | ||
368 | u32 data1 = 0x1f; | ||
369 | u32 addr1 = 0x606BE258; | ||
370 | |||
371 | u32 data2_set = 0x0; | ||
372 | u32 data2_clr = 0x1; | ||
373 | u32 addr2 = 0x606BE100; | ||
374 | |||
375 | u32 data3 = 0x1; | ||
376 | u32 addr3 = 0x606BEC00; | ||
377 | |||
378 | target_cmd.resp = 0; | ||
379 | target_cmd.handle_by_hw = 0; | ||
380 | target_cmd.eop = 1; | ||
381 | |||
382 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE; | ||
383 | target_cmd.addr = cpu_to_le32(addr1); | ||
384 | target_cmd.op1_sz = cpu_to_le32(sizeof(u32)); | ||
385 | target_cmd.op2 = 0; | ||
386 | |||
387 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1); | ||
388 | if (ret < 0) { | ||
389 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | ||
390 | return ret; | ||
391 | } | ||
392 | |||
393 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE; | ||
394 | target_cmd.addr = cpu_to_le32(addr2); | ||
395 | target_cmd.op1_sz = cpu_to_le32(data2_set); | ||
396 | target_cmd.op2 = cpu_to_le32(data2_clr); | ||
397 | |||
398 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1); | ||
399 | if (ret < 0) { | ||
400 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | ||
401 | return ret; | ||
402 | } | ||
403 | |||
404 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE; | ||
405 | target_cmd.addr = cpu_to_le32(addr3); | ||
406 | target_cmd.op1_sz = cpu_to_le32(sizeof(u32)); | ||
407 | target_cmd.op2 = 0; | ||
408 | |||
409 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data3); | ||
410 | if (ret < 0) { | ||
411 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | ||
412 | return ret; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | void iwm_init_default_profile(struct iwm_priv *iwm, | ||
420 | struct iwm_umac_profile *profile) | ||
421 | { | ||
422 | memset(profile, 0, sizeof(struct iwm_umac_profile)); | ||
423 | |||
424 | profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN; | ||
425 | profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE; | ||
426 | profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_NONE; | ||
427 | profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_NONE; | ||
428 | |||
429 | if (iwm->conf.enable_qos) | ||
430 | profile->flags |= cpu_to_le16(UMAC_PROFILE_QOS_ALLOWED); | ||
431 | |||
432 | profile->wireless_mode = iwm->conf.wireless_mode; | ||
433 | profile->mode = cpu_to_le32(iwm->conf.mode); | ||
434 | |||
435 | profile->ibss.atim = 0; | ||
436 | profile->ibss.beacon_interval = 100; | ||
437 | profile->ibss.join_only = 0; | ||
438 | profile->ibss.band = iwm->conf.ibss_band; | ||
439 | profile->ibss.channel = iwm->conf.ibss_channel; | ||
440 | } | ||
441 | |||
442 | void iwm_link_on(struct iwm_priv *iwm) | ||
443 | { | ||
444 | netif_carrier_on(iwm_to_ndev(iwm)); | ||
445 | netif_tx_wake_all_queues(iwm_to_ndev(iwm)); | ||
446 | |||
447 | iwm_send_umac_stats_req(iwm, 0); | ||
448 | } | ||
449 | |||
450 | void iwm_link_off(struct iwm_priv *iwm) | ||
451 | { | ||
452 | struct iw_statistics *wstats = &iwm->wstats; | ||
453 | int i; | ||
454 | |||
455 | netif_tx_stop_all_queues(iwm_to_ndev(iwm)); | ||
456 | netif_carrier_off(iwm_to_ndev(iwm)); | ||
457 | |||
458 | for (i = 0; i < IWM_TX_QUEUES; i++) { | ||
459 | skb_queue_purge(&iwm->txq[i].queue); | ||
460 | |||
461 | iwm->txq[i].concat_count = 0; | ||
462 | iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf; | ||
463 | |||
464 | flush_workqueue(iwm->txq[i].wq); | ||
465 | } | ||
466 | |||
467 | iwm_rx_free(iwm); | ||
468 | |||
469 | cancel_delayed_work(&iwm->stats_request); | ||
470 | memset(wstats, 0, sizeof(struct iw_statistics)); | ||
471 | wstats->qual.updated = IW_QUAL_ALL_INVALID; | ||
472 | |||
473 | del_timer_sync(&iwm->watchdog); | ||
474 | } | ||
475 | |||
476 | static void iwm_bss_list_clean(struct iwm_priv *iwm) | ||
477 | { | ||
478 | struct iwm_bss_info *bss, *next; | ||
479 | |||
480 | list_for_each_entry_safe(bss, next, &iwm->bss_list, node) { | ||
481 | list_del(&bss->node); | ||
482 | kfree(bss->bss); | ||
483 | kfree(bss); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static int iwm_channels_init(struct iwm_priv *iwm) | ||
488 | { | ||
489 | int ret; | ||
490 | |||
491 | #ifdef CONFIG_IWM_B0_HW_SUPPORT | ||
492 | if (iwm->conf.hw_b0) { | ||
493 | IWM_INFO(iwm, "Workaround EEPROM channels for B0 hardware\n"); | ||
494 | return 0; | ||
495 | } | ||
496 | #endif | ||
497 | |||
498 | ret = iwm_send_umac_channel_list(iwm); | ||
499 | if (ret) { | ||
500 | IWM_ERR(iwm, "Send channel list failed\n"); | ||
501 | return ret; | ||
502 | } | ||
503 | |||
504 | ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST, | ||
505 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | ||
506 | if (ret) { | ||
507 | IWM_ERR(iwm, "Didn't get a channel list notification\n"); | ||
508 | return ret; | ||
509 | } | ||
510 | |||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | int iwm_up(struct iwm_priv *iwm) | ||
515 | { | ||
516 | int ret; | ||
517 | struct iwm_notif *notif_reboot, *notif_ack = NULL; | ||
518 | |||
519 | ret = iwm_bus_enable(iwm); | ||
520 | if (ret) { | ||
521 | IWM_ERR(iwm, "Couldn't enable function\n"); | ||
522 | return ret; | ||
523 | } | ||
524 | |||
525 | iwm_rx_setup_handlers(iwm); | ||
526 | |||
527 | /* Wait for initial BARKER_REBOOT from hardware */ | ||
528 | notif_reboot = iwm_notif_wait(iwm, IWM_BARKER_REBOOT_NOTIFICATION, | ||
529 | IWM_SRC_UDMA, 2 * HZ); | ||
530 | if (!notif_reboot) { | ||
531 | IWM_ERR(iwm, "Wait for REBOOT_BARKER timeout\n"); | ||
532 | goto err_disable; | ||
533 | } | ||
534 | |||
535 | /* We send the barker back */ | ||
536 | ret = iwm_bus_send_chunk(iwm, notif_reboot->buf, 16); | ||
537 | if (ret) { | ||
538 | IWM_ERR(iwm, "REBOOT barker response failed\n"); | ||
539 | kfree(notif_reboot); | ||
540 | goto err_disable; | ||
541 | } | ||
542 | |||
543 | kfree(notif_reboot->buf); | ||
544 | kfree(notif_reboot); | ||
545 | |||
546 | /* Wait for ACK_BARKER from hardware */ | ||
547 | notif_ack = iwm_notif_wait(iwm, IWM_ACK_BARKER_NOTIFICATION, | ||
548 | IWM_SRC_UDMA, 2 * HZ); | ||
549 | if (!notif_ack) { | ||
550 | IWM_ERR(iwm, "Wait for ACK_BARKER timeout\n"); | ||
551 | goto err_disable; | ||
552 | } | ||
553 | |||
554 | kfree(notif_ack->buf); | ||
555 | kfree(notif_ack); | ||
556 | |||
557 | /* We start to config static boot parameters */ | ||
558 | ret = iwm_config_boot_params(iwm); | ||
559 | if (ret) { | ||
560 | IWM_ERR(iwm, "Config boot parameters failed\n"); | ||
561 | goto err_disable; | ||
562 | } | ||
563 | |||
564 | ret = iwm_read_mac(iwm, iwm_to_ndev(iwm)->dev_addr); | ||
565 | if (ret) { | ||
566 | IWM_ERR(iwm, "MAC reading failed\n"); | ||
567 | goto err_disable; | ||
568 | } | ||
569 | |||
570 | /* We can load the FWs */ | ||
571 | ret = iwm_load_fw(iwm); | ||
572 | if (ret) { | ||
573 | IWM_ERR(iwm, "FW loading failed\n"); | ||
574 | goto err_disable; | ||
575 | } | ||
576 | |||
577 | /* We configure the UMAC and enable the wifi module */ | ||
578 | ret = iwm_send_umac_config(iwm, | ||
579 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) | | ||
580 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_LINK_EN) | | ||
581 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_MLME_EN)); | ||
582 | if (ret) { | ||
583 | IWM_ERR(iwm, "UMAC config failed\n"); | ||
584 | goto err_fw; | ||
585 | } | ||
586 | |||
587 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS, | ||
588 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | ||
589 | if (ret) { | ||
590 | IWM_ERR(iwm, "Didn't get a wifi core status notification\n"); | ||
591 | goto err_fw; | ||
592 | } | ||
593 | |||
594 | if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN | | ||
595 | UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) { | ||
596 | IWM_DBG_BOOT(iwm, DBG, "Not all cores enabled:0x%x\n", | ||
597 | iwm->core_enabled); | ||
598 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS, | ||
599 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | ||
600 | if (ret) { | ||
601 | IWM_ERR(iwm, "Didn't get a core status notification\n"); | ||
602 | goto err_fw; | ||
603 | } | ||
604 | |||
605 | if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN | | ||
606 | UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) { | ||
607 | IWM_ERR(iwm, "Not all cores enabled: 0x%x\n", | ||
608 | iwm->core_enabled); | ||
609 | goto err_fw; | ||
610 | } else { | ||
611 | IWM_INFO(iwm, "All cores enabled\n"); | ||
612 | } | ||
613 | } | ||
614 | |||
615 | iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile), | ||
616 | GFP_KERNEL); | ||
617 | if (!iwm->umac_profile) { | ||
618 | IWM_ERR(iwm, "Couldn't alloc memory for profile\n"); | ||
619 | goto err_fw; | ||
620 | } | ||
621 | |||
622 | iwm_init_default_profile(iwm, iwm->umac_profile); | ||
623 | |||
624 | ret = iwm_channels_init(iwm); | ||
625 | if (ret < 0) { | ||
626 | IWM_ERR(iwm, "Couldn't init channels\n"); | ||
627 | goto err_profile; | ||
628 | } | ||
629 | |||
630 | /* Set the READY bit to indicate interface is brought up successfully */ | ||
631 | set_bit(IWM_STATUS_READY, &iwm->status); | ||
632 | |||
633 | return 0; | ||
634 | |||
635 | err_profile: | ||
636 | kfree(iwm->umac_profile); | ||
637 | iwm->umac_profile = NULL; | ||
638 | |||
639 | err_fw: | ||
640 | iwm_eeprom_exit(iwm); | ||
641 | |||
642 | err_disable: | ||
643 | ret = iwm_bus_disable(iwm); | ||
644 | if (ret < 0) | ||
645 | IWM_ERR(iwm, "Couldn't disable function\n"); | ||
646 | |||
647 | return -EIO; | ||
648 | } | ||
649 | |||
650 | int iwm_down(struct iwm_priv *iwm) | ||
651 | { | ||
652 | int ret; | ||
653 | |||
654 | /* The interface is already down */ | ||
655 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) | ||
656 | return 0; | ||
657 | |||
658 | if (iwm->scan_request) { | ||
659 | cfg80211_scan_done(iwm->scan_request, true); | ||
660 | iwm->scan_request = NULL; | ||
661 | } | ||
662 | |||
663 | clear_bit(IWM_STATUS_READY, &iwm->status); | ||
664 | |||
665 | iwm_eeprom_exit(iwm); | ||
666 | kfree(iwm->umac_profile); | ||
667 | iwm->umac_profile = NULL; | ||
668 | iwm_bss_list_clean(iwm); | ||
669 | |||
670 | iwm->default_key = NULL; | ||
671 | iwm->core_enabled = 0; | ||
672 | |||
673 | ret = iwm_bus_disable(iwm); | ||
674 | if (ret < 0) { | ||
675 | IWM_ERR(iwm, "Couldn't disable function\n"); | ||
676 | return ret; | ||
677 | } | ||
678 | |||
679 | return 0; | ||
680 | } | ||