1bdcd8170SKalle Valo 2bdcd8170SKalle Valo /* 3bdcd8170SKalle Valo * Copyright (c) 2011 Atheros Communications Inc. 4bdcd8170SKalle Valo * 5bdcd8170SKalle Valo * Permission to use, copy, modify, and/or distribute this software for any 6bdcd8170SKalle Valo * purpose with or without fee is hereby granted, provided that the above 7bdcd8170SKalle Valo * copyright notice and this permission notice appear in all copies. 8bdcd8170SKalle Valo * 9bdcd8170SKalle Valo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10bdcd8170SKalle Valo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11bdcd8170SKalle Valo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12bdcd8170SKalle Valo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13bdcd8170SKalle Valo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14bdcd8170SKalle Valo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15bdcd8170SKalle Valo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16bdcd8170SKalle Valo */ 17bdcd8170SKalle Valo 18c6efe578SStephen Rothwell #include <linux/moduleparam.h> 1992ecbff4SSam Leffler #include <linux/of.h> 20bdcd8170SKalle Valo #include <linux/mmc/sdio_func.h> 21bdcd8170SKalle Valo #include "core.h" 22bdcd8170SKalle Valo #include "cfg80211.h" 23bdcd8170SKalle Valo #include "target.h" 24bdcd8170SKalle Valo #include "debug.h" 25bdcd8170SKalle Valo #include "hif-ops.h" 26bdcd8170SKalle Valo 27bdcd8170SKalle Valo unsigned int debug_mask; 28003353b0SKalle Valo static unsigned int testmode; 29bdcd8170SKalle Valo 30bdcd8170SKalle Valo module_param(debug_mask, uint, 0644); 31003353b0SKalle Valo module_param(testmode, uint, 0644); 32bdcd8170SKalle Valo 33bdcd8170SKalle Valo /* 34bdcd8170SKalle Valo * Include definitions here that can be used to tune the WLAN module 35bdcd8170SKalle Valo * behavior. Different customers can tune the behavior as per their needs, 36bdcd8170SKalle Valo * here. 37bdcd8170SKalle Valo */ 38bdcd8170SKalle Valo 39bdcd8170SKalle Valo /* 40bdcd8170SKalle Valo * This configuration item enable/disable keepalive support. 41bdcd8170SKalle Valo * Keepalive support: In the absence of any data traffic to AP, null 42bdcd8170SKalle Valo * frames will be sent to the AP at periodic interval, to keep the association 43bdcd8170SKalle Valo * active. This configuration item defines the periodic interval. 44bdcd8170SKalle Valo * Use value of zero to disable keepalive support 45bdcd8170SKalle Valo * Default: 60 seconds 46bdcd8170SKalle Valo */ 47bdcd8170SKalle Valo #define WLAN_CONFIG_KEEP_ALIVE_INTERVAL 60 48bdcd8170SKalle Valo 49bdcd8170SKalle Valo /* 50bdcd8170SKalle Valo * This configuration item sets the value of disconnect timeout 51bdcd8170SKalle Valo * Firmware delays sending the disconnec event to the host for this 52bdcd8170SKalle Valo * timeout after is gets disconnected from the current AP. 53bdcd8170SKalle Valo * If the firmware successly roams within the disconnect timeout 54bdcd8170SKalle Valo * it sends a new connect event 55bdcd8170SKalle Valo */ 56bdcd8170SKalle Valo #define WLAN_CONFIG_DISCONNECT_TIMEOUT 10 57bdcd8170SKalle Valo 58bdcd8170SKalle Valo #define CONFIG_AR600x_DEBUG_UART_TX_PIN 8 59bdcd8170SKalle Valo 60bdcd8170SKalle Valo #define ATH6KL_DATA_OFFSET 64 61bdcd8170SKalle Valo struct sk_buff *ath6kl_buf_alloc(int size) 62bdcd8170SKalle Valo { 63bdcd8170SKalle Valo struct sk_buff *skb; 64bdcd8170SKalle Valo u16 reserved; 65bdcd8170SKalle Valo 66bdcd8170SKalle Valo /* Add chacheline space at front and back of buffer */ 67bdcd8170SKalle Valo reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + 681df94a85SVasanthakumar Thiagarajan sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES; 69bdcd8170SKalle Valo skb = dev_alloc_skb(size + reserved); 70bdcd8170SKalle Valo 71bdcd8170SKalle Valo if (skb) 72bdcd8170SKalle Valo skb_reserve(skb, reserved - L1_CACHE_BYTES); 73bdcd8170SKalle Valo return skb; 74bdcd8170SKalle Valo } 75bdcd8170SKalle Valo 76bdcd8170SKalle Valo void ath6kl_init_profile_info(struct ath6kl *ar) 77bdcd8170SKalle Valo { 783450334fSVasanthakumar Thiagarajan /* TODO: Findout vif */ 793450334fSVasanthakumar Thiagarajan struct ath6kl_vif *vif = ar->vif; 80bdcd8170SKalle Valo 813450334fSVasanthakumar Thiagarajan vif->ssid_len = 0; 823450334fSVasanthakumar Thiagarajan memset(vif->ssid, 0, sizeof(vif->ssid)); 833450334fSVasanthakumar Thiagarajan 843450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode = OPEN_AUTH; 853450334fSVasanthakumar Thiagarajan vif->auth_mode = NONE_AUTH; 863450334fSVasanthakumar Thiagarajan vif->prwise_crypto = NONE_CRYPT; 873450334fSVasanthakumar Thiagarajan vif->prwise_crypto_len = 0; 883450334fSVasanthakumar Thiagarajan vif->grp_crypto = NONE_CRYPT; 893450334fSVasanthakumar Thiagarajan vif->grp_crypto_len = 0; 906f2a73f9SVasanthakumar Thiagarajan memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); 918c8b65e3SVasanthakumar Thiagarajan memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); 928c8b65e3SVasanthakumar Thiagarajan memset(vif->bssid, 0, sizeof(vif->bssid)); 93f74bac54SVasanthakumar Thiagarajan vif->bss_ch = 0; 94f5938f24SVasanthakumar Thiagarajan vif->nw_type = vif->next_mode = INFRA_NETWORK; 95bdcd8170SKalle Valo } 96bdcd8170SKalle Valo 97bdcd8170SKalle Valo static int ath6kl_set_host_app_area(struct ath6kl *ar) 98bdcd8170SKalle Valo { 99bdcd8170SKalle Valo u32 address, data; 100bdcd8170SKalle Valo struct host_app_area host_app_area; 101bdcd8170SKalle Valo 102bdcd8170SKalle Valo /* Fetch the address of the host_app_area_s 103bdcd8170SKalle Valo * instance in the host interest area */ 104bdcd8170SKalle Valo address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); 10531024d99SKevin Fang address = TARG_VTOP(ar->target_type, address); 106bdcd8170SKalle Valo 107addb44beSKalle Valo if (ath6kl_diag_read32(ar, address, &data)) 108bdcd8170SKalle Valo return -EIO; 109bdcd8170SKalle Valo 11031024d99SKevin Fang address = TARG_VTOP(ar->target_type, data); 111cbf49a6fSKalle Valo host_app_area.wmi_protocol_ver = cpu_to_le32(WMI_PROTOCOL_VERSION); 112addb44beSKalle Valo if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area, 113addb44beSKalle Valo sizeof(struct host_app_area))) 114bdcd8170SKalle Valo return -EIO; 115bdcd8170SKalle Valo 116bdcd8170SKalle Valo return 0; 117bdcd8170SKalle Valo } 118bdcd8170SKalle Valo 119bdcd8170SKalle Valo static inline void set_ac2_ep_map(struct ath6kl *ar, 120bdcd8170SKalle Valo u8 ac, 121bdcd8170SKalle Valo enum htc_endpoint_id ep) 122bdcd8170SKalle Valo { 123bdcd8170SKalle Valo ar->ac2ep_map[ac] = ep; 124bdcd8170SKalle Valo ar->ep2ac_map[ep] = ac; 125bdcd8170SKalle Valo } 126bdcd8170SKalle Valo 127bdcd8170SKalle Valo /* connect to a service */ 128bdcd8170SKalle Valo static int ath6kl_connectservice(struct ath6kl *ar, 129bdcd8170SKalle Valo struct htc_service_connect_req *con_req, 130bdcd8170SKalle Valo char *desc) 131bdcd8170SKalle Valo { 132bdcd8170SKalle Valo int status; 133bdcd8170SKalle Valo struct htc_service_connect_resp response; 134bdcd8170SKalle Valo 135bdcd8170SKalle Valo memset(&response, 0, sizeof(response)); 136bdcd8170SKalle Valo 137ad226ec2SKalle Valo status = ath6kl_htc_conn_service(ar->htc_target, con_req, &response); 138bdcd8170SKalle Valo if (status) { 139bdcd8170SKalle Valo ath6kl_err("failed to connect to %s service status:%d\n", 140bdcd8170SKalle Valo desc, status); 141bdcd8170SKalle Valo return status; 142bdcd8170SKalle Valo } 143bdcd8170SKalle Valo 144bdcd8170SKalle Valo switch (con_req->svc_id) { 145bdcd8170SKalle Valo case WMI_CONTROL_SVC: 146bdcd8170SKalle Valo if (test_bit(WMI_ENABLED, &ar->flag)) 147bdcd8170SKalle Valo ath6kl_wmi_set_control_ep(ar->wmi, response.endpoint); 148bdcd8170SKalle Valo ar->ctrl_ep = response.endpoint; 149bdcd8170SKalle Valo break; 150bdcd8170SKalle Valo case WMI_DATA_BE_SVC: 151bdcd8170SKalle Valo set_ac2_ep_map(ar, WMM_AC_BE, response.endpoint); 152bdcd8170SKalle Valo break; 153bdcd8170SKalle Valo case WMI_DATA_BK_SVC: 154bdcd8170SKalle Valo set_ac2_ep_map(ar, WMM_AC_BK, response.endpoint); 155bdcd8170SKalle Valo break; 156bdcd8170SKalle Valo case WMI_DATA_VI_SVC: 157bdcd8170SKalle Valo set_ac2_ep_map(ar, WMM_AC_VI, response.endpoint); 158bdcd8170SKalle Valo break; 159bdcd8170SKalle Valo case WMI_DATA_VO_SVC: 160bdcd8170SKalle Valo set_ac2_ep_map(ar, WMM_AC_VO, response.endpoint); 161bdcd8170SKalle Valo break; 162bdcd8170SKalle Valo default: 163bdcd8170SKalle Valo ath6kl_err("service id is not mapped %d\n", con_req->svc_id); 164bdcd8170SKalle Valo return -EINVAL; 165bdcd8170SKalle Valo } 166bdcd8170SKalle Valo 167bdcd8170SKalle Valo return 0; 168bdcd8170SKalle Valo } 169bdcd8170SKalle Valo 170bdcd8170SKalle Valo static int ath6kl_init_service_ep(struct ath6kl *ar) 171bdcd8170SKalle Valo { 172bdcd8170SKalle Valo struct htc_service_connect_req connect; 173bdcd8170SKalle Valo 174bdcd8170SKalle Valo memset(&connect, 0, sizeof(connect)); 175bdcd8170SKalle Valo 176bdcd8170SKalle Valo /* these fields are the same for all service endpoints */ 177bdcd8170SKalle Valo connect.ep_cb.rx = ath6kl_rx; 178bdcd8170SKalle Valo connect.ep_cb.rx_refill = ath6kl_rx_refill; 179bdcd8170SKalle Valo connect.ep_cb.tx_full = ath6kl_tx_queue_full; 180bdcd8170SKalle Valo 181bdcd8170SKalle Valo /* 182bdcd8170SKalle Valo * Set the max queue depth so that our ath6kl_tx_queue_full handler 183bdcd8170SKalle Valo * gets called. 184bdcd8170SKalle Valo */ 185bdcd8170SKalle Valo connect.max_txq_depth = MAX_DEFAULT_SEND_QUEUE_DEPTH; 186bdcd8170SKalle Valo connect.ep_cb.rx_refill_thresh = ATH6KL_MAX_RX_BUFFERS / 4; 187bdcd8170SKalle Valo if (!connect.ep_cb.rx_refill_thresh) 188bdcd8170SKalle Valo connect.ep_cb.rx_refill_thresh++; 189bdcd8170SKalle Valo 190bdcd8170SKalle Valo /* connect to control service */ 191bdcd8170SKalle Valo connect.svc_id = WMI_CONTROL_SVC; 192bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI CONTROL")) 193bdcd8170SKalle Valo return -EIO; 194bdcd8170SKalle Valo 195bdcd8170SKalle Valo connect.flags |= HTC_FLGS_TX_BNDL_PAD_EN; 196bdcd8170SKalle Valo 197bdcd8170SKalle Valo /* 198bdcd8170SKalle Valo * Limit the HTC message size on the send path, although e can 199bdcd8170SKalle Valo * receive A-MSDU frames of 4K, we will only send ethernet-sized 200bdcd8170SKalle Valo * (802.3) frames on the send path. 201bdcd8170SKalle Valo */ 202bdcd8170SKalle Valo connect.max_rxmsg_sz = WMI_MAX_TX_DATA_FRAME_LENGTH; 203bdcd8170SKalle Valo 204bdcd8170SKalle Valo /* 205bdcd8170SKalle Valo * To reduce the amount of committed memory for larger A_MSDU 206bdcd8170SKalle Valo * frames, use the recv-alloc threshold mechanism for larger 207bdcd8170SKalle Valo * packets. 208bdcd8170SKalle Valo */ 209bdcd8170SKalle Valo connect.ep_cb.rx_alloc_thresh = ATH6KL_BUFFER_SIZE; 210bdcd8170SKalle Valo connect.ep_cb.rx_allocthresh = ath6kl_alloc_amsdu_rxbuf; 211bdcd8170SKalle Valo 212bdcd8170SKalle Valo /* 213bdcd8170SKalle Valo * For the remaining data services set the connection flag to 214bdcd8170SKalle Valo * reduce dribbling, if configured to do so. 215bdcd8170SKalle Valo */ 216bdcd8170SKalle Valo connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB; 217bdcd8170SKalle Valo connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK; 218bdcd8170SKalle Valo connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF; 219bdcd8170SKalle Valo 220bdcd8170SKalle Valo connect.svc_id = WMI_DATA_BE_SVC; 221bdcd8170SKalle Valo 222bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI DATA BE")) 223bdcd8170SKalle Valo return -EIO; 224bdcd8170SKalle Valo 225bdcd8170SKalle Valo /* connect to back-ground map this to WMI LOW_PRI */ 226bdcd8170SKalle Valo connect.svc_id = WMI_DATA_BK_SVC; 227bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI DATA BK")) 228bdcd8170SKalle Valo return -EIO; 229bdcd8170SKalle Valo 230bdcd8170SKalle Valo /* connect to Video service, map this to to HI PRI */ 231bdcd8170SKalle Valo connect.svc_id = WMI_DATA_VI_SVC; 232bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI DATA VI")) 233bdcd8170SKalle Valo return -EIO; 234bdcd8170SKalle Valo 235bdcd8170SKalle Valo /* 236bdcd8170SKalle Valo * Connect to VO service, this is currently not mapped to a WMI 237bdcd8170SKalle Valo * priority stream due to historical reasons. WMI originally 238bdcd8170SKalle Valo * defined 3 priorities over 3 mailboxes We can change this when 239bdcd8170SKalle Valo * WMI is reworked so that priorities are not dependent on 240bdcd8170SKalle Valo * mailboxes. 241bdcd8170SKalle Valo */ 242bdcd8170SKalle Valo connect.svc_id = WMI_DATA_VO_SVC; 243bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI DATA VO")) 244bdcd8170SKalle Valo return -EIO; 245bdcd8170SKalle Valo 246bdcd8170SKalle Valo return 0; 247bdcd8170SKalle Valo } 248bdcd8170SKalle Valo 2498dafb70eSVasanthakumar Thiagarajan void ath6kl_init_control_info(struct ath6kl *ar) 250bdcd8170SKalle Valo { 2516f2a73f9SVasanthakumar Thiagarajan /* TODO: Findout vif */ 2523450334fSVasanthakumar Thiagarajan struct ath6kl_vif *vif = ar->vif; 2533450334fSVasanthakumar Thiagarajan 254bdcd8170SKalle Valo ath6kl_init_profile_info(ar); 2553450334fSVasanthakumar Thiagarajan vif->def_txkey_index = 0; 2566f2a73f9SVasanthakumar Thiagarajan memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); 257f74bac54SVasanthakumar Thiagarajan vif->ch_hint = 0; 258bdcd8170SKalle Valo } 259bdcd8170SKalle Valo 260bdcd8170SKalle Valo /* 261bdcd8170SKalle Valo * Set HTC/Mbox operational parameters, this can only be called when the 262bdcd8170SKalle Valo * target is in the BMI phase. 263bdcd8170SKalle Valo */ 264bdcd8170SKalle Valo static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, 265bdcd8170SKalle Valo u8 htc_ctrl_buf) 266bdcd8170SKalle Valo { 267bdcd8170SKalle Valo int status; 268bdcd8170SKalle Valo u32 blk_size; 269bdcd8170SKalle Valo 270bdcd8170SKalle Valo blk_size = ar->mbox_info.block_size; 271bdcd8170SKalle Valo 272bdcd8170SKalle Valo if (htc_ctrl_buf) 273bdcd8170SKalle Valo blk_size |= ((u32)htc_ctrl_buf) << 16; 274bdcd8170SKalle Valo 275bdcd8170SKalle Valo /* set the host interest area for the block size */ 276bdcd8170SKalle Valo status = ath6kl_bmi_write(ar, 277bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 278bdcd8170SKalle Valo HI_ITEM(hi_mbox_io_block_sz)), 279bdcd8170SKalle Valo (u8 *)&blk_size, 280bdcd8170SKalle Valo 4); 281bdcd8170SKalle Valo if (status) { 282bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for IO block size failed\n"); 283bdcd8170SKalle Valo goto out; 284bdcd8170SKalle Valo } 285bdcd8170SKalle Valo 286bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "block size set: %d (target addr:0x%X)\n", 287bdcd8170SKalle Valo blk_size, 288bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz))); 289bdcd8170SKalle Valo 290bdcd8170SKalle Valo if (mbox_isr_yield_val) { 291bdcd8170SKalle Valo /* set the host interest area for the mbox ISR yield limit */ 292bdcd8170SKalle Valo status = ath6kl_bmi_write(ar, 293bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 294bdcd8170SKalle Valo HI_ITEM(hi_mbox_isr_yield_limit)), 295bdcd8170SKalle Valo (u8 *)&mbox_isr_yield_val, 296bdcd8170SKalle Valo 4); 297bdcd8170SKalle Valo if (status) { 298bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for yield limit failed\n"); 299bdcd8170SKalle Valo goto out; 300bdcd8170SKalle Valo } 301bdcd8170SKalle Valo } 302bdcd8170SKalle Valo 303bdcd8170SKalle Valo out: 304bdcd8170SKalle Valo return status; 305bdcd8170SKalle Valo } 306bdcd8170SKalle Valo 307bdcd8170SKalle Valo #define REG_DUMP_COUNT_AR6003 60 308bdcd8170SKalle Valo #define REGISTER_DUMP_LEN_MAX 60 309bdcd8170SKalle Valo 310bdcd8170SKalle Valo static void ath6kl_dump_target_assert_info(struct ath6kl *ar) 311bdcd8170SKalle Valo { 312bdcd8170SKalle Valo u32 address; 313bdcd8170SKalle Valo u32 regdump_loc = 0; 314bdcd8170SKalle Valo int status; 315bdcd8170SKalle Valo u32 regdump_val[REGISTER_DUMP_LEN_MAX]; 316bdcd8170SKalle Valo u32 i; 317bdcd8170SKalle Valo 318bdcd8170SKalle Valo if (ar->target_type != TARGET_TYPE_AR6003) 319bdcd8170SKalle Valo return; 320bdcd8170SKalle Valo 321bdcd8170SKalle Valo /* the reg dump pointer is copied to the host interest area */ 322bdcd8170SKalle Valo address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); 32331024d99SKevin Fang address = TARG_VTOP(ar->target_type, address); 324bdcd8170SKalle Valo 325bdcd8170SKalle Valo /* read RAM location through diagnostic window */ 326addb44beSKalle Valo status = ath6kl_diag_read32(ar, address, ®dump_loc); 327bdcd8170SKalle Valo 328bdcd8170SKalle Valo if (status || !regdump_loc) { 329bdcd8170SKalle Valo ath6kl_err("failed to get ptr to register dump area\n"); 330bdcd8170SKalle Valo return; 331bdcd8170SKalle Valo } 332bdcd8170SKalle Valo 333bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n", 334bdcd8170SKalle Valo regdump_loc); 33531024d99SKevin Fang regdump_loc = TARG_VTOP(ar->target_type, regdump_loc); 336bdcd8170SKalle Valo 337bdcd8170SKalle Valo /* fetch register dump data */ 338addb44beSKalle Valo status = ath6kl_diag_read(ar, regdump_loc, (u8 *)®dump_val[0], 339addb44beSKalle Valo REG_DUMP_COUNT_AR6003 * (sizeof(u32))); 340bdcd8170SKalle Valo 341bdcd8170SKalle Valo if (status) { 342bdcd8170SKalle Valo ath6kl_err("failed to get register dump\n"); 343bdcd8170SKalle Valo return; 344bdcd8170SKalle Valo } 345bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "Register Dump:\n"); 346bdcd8170SKalle Valo 347bdcd8170SKalle Valo for (i = 0; i < REG_DUMP_COUNT_AR6003; i++) 348bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, " %d : 0x%8.8X\n", 349bdcd8170SKalle Valo i, regdump_val[i]); 350bdcd8170SKalle Valo 351bdcd8170SKalle Valo } 352bdcd8170SKalle Valo 353bdcd8170SKalle Valo void ath6kl_target_failure(struct ath6kl *ar) 354bdcd8170SKalle Valo { 355bdcd8170SKalle Valo ath6kl_err("target asserted\n"); 356bdcd8170SKalle Valo 357bdcd8170SKalle Valo /* try dumping target assertion information (if any) */ 358bdcd8170SKalle Valo ath6kl_dump_target_assert_info(ar); 359bdcd8170SKalle Valo 360bdcd8170SKalle Valo } 361bdcd8170SKalle Valo 362bdcd8170SKalle Valo static int ath6kl_target_config_wlan_params(struct ath6kl *ar) 363bdcd8170SKalle Valo { 364bdcd8170SKalle Valo int status = 0; 3654dea08e0SJouni Malinen int ret; 366bdcd8170SKalle Valo 367bdcd8170SKalle Valo /* 368bdcd8170SKalle Valo * Configure the device for rx dot11 header rules. "0,0" are the 369bdcd8170SKalle Valo * default values. Required if checksum offload is needed. Set 370bdcd8170SKalle Valo * RxMetaVersion to 2. 371bdcd8170SKalle Valo */ 372bdcd8170SKalle Valo if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, 373bdcd8170SKalle Valo ar->rx_meta_ver, 0, 0)) { 374bdcd8170SKalle Valo ath6kl_err("unable to set the rx frame format\n"); 375bdcd8170SKalle Valo status = -EIO; 376bdcd8170SKalle Valo } 377bdcd8170SKalle Valo 378bdcd8170SKalle Valo if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) 379bdcd8170SKalle Valo if ((ath6kl_wmi_pmparams_cmd(ar->wmi, 0, 1, 0, 0, 1, 380bdcd8170SKalle Valo IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN)) != 0) { 381bdcd8170SKalle Valo ath6kl_err("unable to set power save fail event policy\n"); 382bdcd8170SKalle Valo status = -EIO; 383bdcd8170SKalle Valo } 384bdcd8170SKalle Valo 385bdcd8170SKalle Valo if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) 386bdcd8170SKalle Valo if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, 0, 387bdcd8170SKalle Valo WMI_DONOT_IGNORE_BARKER_IN_ERP)) != 0) { 388bdcd8170SKalle Valo ath6kl_err("unable to set barker preamble policy\n"); 389bdcd8170SKalle Valo status = -EIO; 390bdcd8170SKalle Valo } 391bdcd8170SKalle Valo 392bdcd8170SKalle Valo if (ath6kl_wmi_set_keepalive_cmd(ar->wmi, 393bdcd8170SKalle Valo WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) { 394bdcd8170SKalle Valo ath6kl_err("unable to set keep alive interval\n"); 395bdcd8170SKalle Valo status = -EIO; 396bdcd8170SKalle Valo } 397bdcd8170SKalle Valo 398bdcd8170SKalle Valo if (ath6kl_wmi_disctimeout_cmd(ar->wmi, 399bdcd8170SKalle Valo WLAN_CONFIG_DISCONNECT_TIMEOUT)) { 400bdcd8170SKalle Valo ath6kl_err("unable to set disconnect timeout\n"); 401bdcd8170SKalle Valo status = -EIO; 402bdcd8170SKalle Valo } 403bdcd8170SKalle Valo 404bdcd8170SKalle Valo if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) 405bdcd8170SKalle Valo if (ath6kl_wmi_set_wmm_txop(ar->wmi, WMI_TXOP_DISABLED)) { 406bdcd8170SKalle Valo ath6kl_err("unable to set txop bursting\n"); 407bdcd8170SKalle Valo status = -EIO; 408bdcd8170SKalle Valo } 409bdcd8170SKalle Valo 4106bbc7c35SJouni Malinen if (ar->p2p) { 4116bbc7c35SJouni Malinen ret = ath6kl_wmi_info_req_cmd(ar->wmi, 4126bbc7c35SJouni Malinen P2P_FLAG_CAPABILITIES_REQ | 4134dea08e0SJouni Malinen P2P_FLAG_MACADDR_REQ | 4144dea08e0SJouni Malinen P2P_FLAG_HMODEL_REQ); 4154dea08e0SJouni Malinen if (ret) { 4164dea08e0SJouni Malinen ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P " 4176bbc7c35SJouni Malinen "capabilities (%d) - assuming P2P not " 4186bbc7c35SJouni Malinen "supported\n", ret); 4196bbc7c35SJouni Malinen ar->p2p = 0; 4206bbc7c35SJouni Malinen } 4216bbc7c35SJouni Malinen } 4226bbc7c35SJouni Malinen 4236bbc7c35SJouni Malinen if (ar->p2p) { 4246bbc7c35SJouni Malinen /* Enable Probe Request reporting for P2P */ 4256bbc7c35SJouni Malinen ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true); 4266bbc7c35SJouni Malinen if (ret) { 4276bbc7c35SJouni Malinen ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe " 4286bbc7c35SJouni Malinen "Request reporting (%d)\n", ret); 4296bbc7c35SJouni Malinen } 4304dea08e0SJouni Malinen } 4314dea08e0SJouni Malinen 432bdcd8170SKalle Valo return status; 433bdcd8170SKalle Valo } 434bdcd8170SKalle Valo 435bdcd8170SKalle Valo int ath6kl_configure_target(struct ath6kl *ar) 436bdcd8170SKalle Valo { 437bdcd8170SKalle Valo u32 param, ram_reserved_size; 438bdcd8170SKalle Valo u8 fw_iftype; 439bdcd8170SKalle Valo 440dd3751f7SVasanthakumar Thiagarajan fw_iftype = HI_OPTION_FW_MODE_BSS_STA; 441bdcd8170SKalle Valo 442bdcd8170SKalle Valo /* Tell target which HTC version it is used*/ 443bdcd8170SKalle Valo param = HTC_PROTOCOL_VERSION; 444bdcd8170SKalle Valo if (ath6kl_bmi_write(ar, 445bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 446bdcd8170SKalle Valo HI_ITEM(hi_app_host_interest)), 447bdcd8170SKalle Valo (u8 *)¶m, 4) != 0) { 448bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for htc version failed\n"); 449bdcd8170SKalle Valo return -EIO; 450bdcd8170SKalle Valo } 451bdcd8170SKalle Valo 452bdcd8170SKalle Valo /* set the firmware mode to STA/IBSS/AP */ 453bdcd8170SKalle Valo param = 0; 454bdcd8170SKalle Valo 455bdcd8170SKalle Valo if (ath6kl_bmi_read(ar, 456bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 457bdcd8170SKalle Valo HI_ITEM(hi_option_flag)), 458bdcd8170SKalle Valo (u8 *)¶m, 4) != 0) { 459bdcd8170SKalle Valo ath6kl_err("bmi_read_memory for setting fwmode failed\n"); 460bdcd8170SKalle Valo return -EIO; 461bdcd8170SKalle Valo } 462bdcd8170SKalle Valo 463bdcd8170SKalle Valo param |= (1 << HI_OPTION_NUM_DEV_SHIFT); 464bdcd8170SKalle Valo param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT); 4656bbc7c35SJouni Malinen if (ar->p2p && fw_iftype == HI_OPTION_FW_MODE_BSS_STA) { 4666bbc7c35SJouni Malinen param |= HI_OPTION_FW_SUBMODE_P2PDEV << 4676bbc7c35SJouni Malinen HI_OPTION_FW_SUBMODE_SHIFT; 4686bbc7c35SJouni Malinen } 469bdcd8170SKalle Valo param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); 470bdcd8170SKalle Valo param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); 471bdcd8170SKalle Valo 472bdcd8170SKalle Valo if (ath6kl_bmi_write(ar, 473bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 474bdcd8170SKalle Valo HI_ITEM(hi_option_flag)), 475bdcd8170SKalle Valo (u8 *)¶m, 476bdcd8170SKalle Valo 4) != 0) { 477bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for setting fwmode failed\n"); 478bdcd8170SKalle Valo return -EIO; 479bdcd8170SKalle Valo } 480bdcd8170SKalle Valo 481bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "firmware mode set\n"); 482bdcd8170SKalle Valo 483bdcd8170SKalle Valo /* 484bdcd8170SKalle Valo * Hardcode the address use for the extended board data 485bdcd8170SKalle Valo * Ideally this should be pre-allocate by the OS at boot time 486bdcd8170SKalle Valo * But since it is a new feature and board data is loaded 487bdcd8170SKalle Valo * at init time, we have to workaround this from host. 488bdcd8170SKalle Valo * It is difficult to patch the firmware boot code, 489bdcd8170SKalle Valo * but possible in theory. 490bdcd8170SKalle Valo */ 491bdcd8170SKalle Valo 492991b27eaSKalle Valo param = ar->hw.board_ext_data_addr; 493991b27eaSKalle Valo ram_reserved_size = ar->hw.reserved_ram_size; 494bdcd8170SKalle Valo 495991b27eaSKalle Valo if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, 496bdcd8170SKalle Valo HI_ITEM(hi_board_ext_data)), 497bdcd8170SKalle Valo (u8 *)¶m, 4) != 0) { 498bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); 499bdcd8170SKalle Valo return -EIO; 500bdcd8170SKalle Valo } 501991b27eaSKalle Valo 502991b27eaSKalle Valo if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, 503bdcd8170SKalle Valo HI_ITEM(hi_end_ram_reserve_sz)), 504bdcd8170SKalle Valo (u8 *)&ram_reserved_size, 4) != 0) { 505bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); 506bdcd8170SKalle Valo return -EIO; 507bdcd8170SKalle Valo } 508bdcd8170SKalle Valo 509bdcd8170SKalle Valo /* set the block size for the target */ 510bdcd8170SKalle Valo if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0)) 511bdcd8170SKalle Valo /* use default number of control buffers */ 512bdcd8170SKalle Valo return -EIO; 513bdcd8170SKalle Valo 514bdcd8170SKalle Valo return 0; 515bdcd8170SKalle Valo } 516bdcd8170SKalle Valo 5178dafb70eSVasanthakumar Thiagarajan void ath6kl_core_free(struct ath6kl *ar) 518bdcd8170SKalle Valo { 5198dafb70eSVasanthakumar Thiagarajan wiphy_free(ar->wiphy); 520bdcd8170SKalle Valo } 521bdcd8170SKalle Valo 522bdcd8170SKalle Valo int ath6kl_unavail_ev(struct ath6kl *ar) 523bdcd8170SKalle Valo { 524*28ae58ddSVasanthakumar Thiagarajan ath6kl_destroy(ar->vif->ndev, 1); 525bdcd8170SKalle Valo 526bdcd8170SKalle Valo return 0; 527bdcd8170SKalle Valo } 528bdcd8170SKalle Valo 529bdcd8170SKalle Valo /* firmware upload */ 530bdcd8170SKalle Valo static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, 531bdcd8170SKalle Valo u8 **fw, size_t *fw_len) 532bdcd8170SKalle Valo { 533bdcd8170SKalle Valo const struct firmware *fw_entry; 534bdcd8170SKalle Valo int ret; 535bdcd8170SKalle Valo 536bdcd8170SKalle Valo ret = request_firmware(&fw_entry, filename, ar->dev); 537bdcd8170SKalle Valo if (ret) 538bdcd8170SKalle Valo return ret; 539bdcd8170SKalle Valo 540bdcd8170SKalle Valo *fw_len = fw_entry->size; 541bdcd8170SKalle Valo *fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); 542bdcd8170SKalle Valo 543bdcd8170SKalle Valo if (*fw == NULL) 544bdcd8170SKalle Valo ret = -ENOMEM; 545bdcd8170SKalle Valo 546bdcd8170SKalle Valo release_firmware(fw_entry); 547bdcd8170SKalle Valo 548bdcd8170SKalle Valo return ret; 549bdcd8170SKalle Valo } 550bdcd8170SKalle Valo 55192ecbff4SSam Leffler #ifdef CONFIG_OF 55292ecbff4SSam Leffler static const char *get_target_ver_dir(const struct ath6kl *ar) 55392ecbff4SSam Leffler { 55492ecbff4SSam Leffler switch (ar->version.target_ver) { 55592ecbff4SSam Leffler case AR6003_REV1_VERSION: 55692ecbff4SSam Leffler return "ath6k/AR6003/hw1.0"; 55792ecbff4SSam Leffler case AR6003_REV2_VERSION: 55892ecbff4SSam Leffler return "ath6k/AR6003/hw2.0"; 55992ecbff4SSam Leffler case AR6003_REV3_VERSION: 56092ecbff4SSam Leffler return "ath6k/AR6003/hw2.1.1"; 56192ecbff4SSam Leffler } 56292ecbff4SSam Leffler ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__, 56392ecbff4SSam Leffler ar->version.target_ver); 56492ecbff4SSam Leffler return NULL; 56592ecbff4SSam Leffler } 56692ecbff4SSam Leffler 56792ecbff4SSam Leffler /* 56892ecbff4SSam Leffler * Check the device tree for a board-id and use it to construct 56992ecbff4SSam Leffler * the pathname to the firmware file. Used (for now) to find a 57092ecbff4SSam Leffler * fallback to the "bdata.bin" file--typically a symlink to the 57192ecbff4SSam Leffler * appropriate board-specific file. 57292ecbff4SSam Leffler */ 57392ecbff4SSam Leffler static bool check_device_tree(struct ath6kl *ar) 57492ecbff4SSam Leffler { 57592ecbff4SSam Leffler static const char *board_id_prop = "atheros,board-id"; 57692ecbff4SSam Leffler struct device_node *node; 57792ecbff4SSam Leffler char board_filename[64]; 57892ecbff4SSam Leffler const char *board_id; 57992ecbff4SSam Leffler int ret; 58092ecbff4SSam Leffler 58192ecbff4SSam Leffler for_each_compatible_node(node, NULL, "atheros,ath6kl") { 58292ecbff4SSam Leffler board_id = of_get_property(node, board_id_prop, NULL); 58392ecbff4SSam Leffler if (board_id == NULL) { 58492ecbff4SSam Leffler ath6kl_warn("No \"%s\" property on %s node.\n", 58592ecbff4SSam Leffler board_id_prop, node->name); 58692ecbff4SSam Leffler continue; 58792ecbff4SSam Leffler } 58892ecbff4SSam Leffler snprintf(board_filename, sizeof(board_filename), 58992ecbff4SSam Leffler "%s/bdata.%s.bin", get_target_ver_dir(ar), board_id); 59092ecbff4SSam Leffler 59192ecbff4SSam Leffler ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board, 59292ecbff4SSam Leffler &ar->fw_board_len); 59392ecbff4SSam Leffler if (ret) { 59492ecbff4SSam Leffler ath6kl_err("Failed to get DT board file %s: %d\n", 59592ecbff4SSam Leffler board_filename, ret); 59692ecbff4SSam Leffler continue; 59792ecbff4SSam Leffler } 59892ecbff4SSam Leffler return true; 59992ecbff4SSam Leffler } 60092ecbff4SSam Leffler return false; 60192ecbff4SSam Leffler } 60292ecbff4SSam Leffler #else 60392ecbff4SSam Leffler static bool check_device_tree(struct ath6kl *ar) 60492ecbff4SSam Leffler { 60592ecbff4SSam Leffler return false; 60692ecbff4SSam Leffler } 60792ecbff4SSam Leffler #endif /* CONFIG_OF */ 60892ecbff4SSam Leffler 609bdcd8170SKalle Valo static int ath6kl_fetch_board_file(struct ath6kl *ar) 610bdcd8170SKalle Valo { 611bdcd8170SKalle Valo const char *filename; 612bdcd8170SKalle Valo int ret; 613bdcd8170SKalle Valo 614772c31eeSKalle Valo if (ar->fw_board != NULL) 615772c31eeSKalle Valo return 0; 616772c31eeSKalle Valo 617bdcd8170SKalle Valo switch (ar->version.target_ver) { 618bdcd8170SKalle Valo case AR6003_REV2_VERSION: 619bdcd8170SKalle Valo filename = AR6003_REV2_BOARD_DATA_FILE; 620bdcd8170SKalle Valo break; 62131024d99SKevin Fang case AR6004_REV1_VERSION: 62231024d99SKevin Fang filename = AR6004_REV1_BOARD_DATA_FILE; 62331024d99SKevin Fang break; 624bdcd8170SKalle Valo default: 625bdcd8170SKalle Valo filename = AR6003_REV3_BOARD_DATA_FILE; 626bdcd8170SKalle Valo break; 627bdcd8170SKalle Valo } 628bdcd8170SKalle Valo 629bdcd8170SKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw_board, 630bdcd8170SKalle Valo &ar->fw_board_len); 631bdcd8170SKalle Valo if (ret == 0) { 632bdcd8170SKalle Valo /* managed to get proper board file */ 633bdcd8170SKalle Valo return 0; 634bdcd8170SKalle Valo } 635bdcd8170SKalle Valo 63692ecbff4SSam Leffler if (check_device_tree(ar)) { 63792ecbff4SSam Leffler /* got board file from device tree */ 63892ecbff4SSam Leffler return 0; 63992ecbff4SSam Leffler } 64092ecbff4SSam Leffler 641bdcd8170SKalle Valo /* there was no proper board file, try to use default instead */ 642bdcd8170SKalle Valo ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n", 643bdcd8170SKalle Valo filename, ret); 644bdcd8170SKalle Valo 645bdcd8170SKalle Valo switch (ar->version.target_ver) { 646bdcd8170SKalle Valo case AR6003_REV2_VERSION: 647bdcd8170SKalle Valo filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE; 648bdcd8170SKalle Valo break; 64931024d99SKevin Fang case AR6004_REV1_VERSION: 65031024d99SKevin Fang filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE; 65131024d99SKevin Fang break; 652bdcd8170SKalle Valo default: 653bdcd8170SKalle Valo filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE; 654bdcd8170SKalle Valo break; 655bdcd8170SKalle Valo } 656bdcd8170SKalle Valo 657bdcd8170SKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw_board, 658bdcd8170SKalle Valo &ar->fw_board_len); 659bdcd8170SKalle Valo if (ret) { 660bdcd8170SKalle Valo ath6kl_err("Failed to get default board file %s: %d\n", 661bdcd8170SKalle Valo filename, ret); 662bdcd8170SKalle Valo return ret; 663bdcd8170SKalle Valo } 664bdcd8170SKalle Valo 665bdcd8170SKalle Valo ath6kl_warn("WARNING! No proper board file was not found, instead using a default board file.\n"); 666bdcd8170SKalle Valo ath6kl_warn("Most likely your hardware won't work as specified. Install correct board file!\n"); 667bdcd8170SKalle Valo 668bdcd8170SKalle Valo return 0; 669bdcd8170SKalle Valo } 670bdcd8170SKalle Valo 671772c31eeSKalle Valo static int ath6kl_fetch_otp_file(struct ath6kl *ar) 672772c31eeSKalle Valo { 673772c31eeSKalle Valo const char *filename; 674772c31eeSKalle Valo int ret; 675772c31eeSKalle Valo 676772c31eeSKalle Valo if (ar->fw_otp != NULL) 677772c31eeSKalle Valo return 0; 678772c31eeSKalle Valo 679772c31eeSKalle Valo switch (ar->version.target_ver) { 680772c31eeSKalle Valo case AR6003_REV2_VERSION: 681772c31eeSKalle Valo filename = AR6003_REV2_OTP_FILE; 682772c31eeSKalle Valo break; 683772c31eeSKalle Valo case AR6004_REV1_VERSION: 684772c31eeSKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n"); 685772c31eeSKalle Valo return 0; 686772c31eeSKalle Valo break; 687772c31eeSKalle Valo default: 688772c31eeSKalle Valo filename = AR6003_REV3_OTP_FILE; 689772c31eeSKalle Valo break; 690772c31eeSKalle Valo } 691772c31eeSKalle Valo 692772c31eeSKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, 693772c31eeSKalle Valo &ar->fw_otp_len); 694772c31eeSKalle Valo if (ret) { 695772c31eeSKalle Valo ath6kl_err("Failed to get OTP file %s: %d\n", 696772c31eeSKalle Valo filename, ret); 697772c31eeSKalle Valo return ret; 698772c31eeSKalle Valo } 699772c31eeSKalle Valo 700772c31eeSKalle Valo return 0; 701772c31eeSKalle Valo } 702772c31eeSKalle Valo 703772c31eeSKalle Valo static int ath6kl_fetch_fw_file(struct ath6kl *ar) 704772c31eeSKalle Valo { 705772c31eeSKalle Valo const char *filename; 706772c31eeSKalle Valo int ret; 707772c31eeSKalle Valo 708772c31eeSKalle Valo if (ar->fw != NULL) 709772c31eeSKalle Valo return 0; 710772c31eeSKalle Valo 711772c31eeSKalle Valo if (testmode) { 712772c31eeSKalle Valo switch (ar->version.target_ver) { 713772c31eeSKalle Valo case AR6003_REV2_VERSION: 714772c31eeSKalle Valo filename = AR6003_REV2_TCMD_FIRMWARE_FILE; 715772c31eeSKalle Valo break; 716772c31eeSKalle Valo case AR6003_REV3_VERSION: 717772c31eeSKalle Valo filename = AR6003_REV3_TCMD_FIRMWARE_FILE; 718772c31eeSKalle Valo break; 719772c31eeSKalle Valo case AR6004_REV1_VERSION: 720772c31eeSKalle Valo ath6kl_warn("testmode not supported with ar6004\n"); 721772c31eeSKalle Valo return -EOPNOTSUPP; 722772c31eeSKalle Valo default: 723772c31eeSKalle Valo ath6kl_warn("unknown target version: 0x%x\n", 724772c31eeSKalle Valo ar->version.target_ver); 725772c31eeSKalle Valo return -EINVAL; 726772c31eeSKalle Valo } 727772c31eeSKalle Valo 728772c31eeSKalle Valo set_bit(TESTMODE, &ar->flag); 729772c31eeSKalle Valo 730772c31eeSKalle Valo goto get_fw; 731772c31eeSKalle Valo } 732772c31eeSKalle Valo 733772c31eeSKalle Valo switch (ar->version.target_ver) { 734772c31eeSKalle Valo case AR6003_REV2_VERSION: 735772c31eeSKalle Valo filename = AR6003_REV2_FIRMWARE_FILE; 736772c31eeSKalle Valo break; 737772c31eeSKalle Valo case AR6004_REV1_VERSION: 738772c31eeSKalle Valo filename = AR6004_REV1_FIRMWARE_FILE; 739772c31eeSKalle Valo break; 740772c31eeSKalle Valo default: 741772c31eeSKalle Valo filename = AR6003_REV3_FIRMWARE_FILE; 742772c31eeSKalle Valo break; 743772c31eeSKalle Valo } 744772c31eeSKalle Valo 745772c31eeSKalle Valo get_fw: 746772c31eeSKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); 747772c31eeSKalle Valo if (ret) { 748772c31eeSKalle Valo ath6kl_err("Failed to get firmware file %s: %d\n", 749772c31eeSKalle Valo filename, ret); 750772c31eeSKalle Valo return ret; 751772c31eeSKalle Valo } 752772c31eeSKalle Valo 753772c31eeSKalle Valo return 0; 754772c31eeSKalle Valo } 755772c31eeSKalle Valo 756772c31eeSKalle Valo static int ath6kl_fetch_patch_file(struct ath6kl *ar) 757772c31eeSKalle Valo { 758772c31eeSKalle Valo const char *filename; 759772c31eeSKalle Valo int ret; 760772c31eeSKalle Valo 761772c31eeSKalle Valo switch (ar->version.target_ver) { 762772c31eeSKalle Valo case AR6003_REV2_VERSION: 763772c31eeSKalle Valo filename = AR6003_REV2_PATCH_FILE; 764772c31eeSKalle Valo break; 765772c31eeSKalle Valo case AR6004_REV1_VERSION: 766772c31eeSKalle Valo /* FIXME: implement for AR6004 */ 767772c31eeSKalle Valo return 0; 768772c31eeSKalle Valo break; 769772c31eeSKalle Valo default: 770772c31eeSKalle Valo filename = AR6003_REV3_PATCH_FILE; 771772c31eeSKalle Valo break; 772772c31eeSKalle Valo } 773772c31eeSKalle Valo 774772c31eeSKalle Valo if (ar->fw_patch == NULL) { 775772c31eeSKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, 776772c31eeSKalle Valo &ar->fw_patch_len); 777772c31eeSKalle Valo if (ret) { 778772c31eeSKalle Valo ath6kl_err("Failed to get patch file %s: %d\n", 779772c31eeSKalle Valo filename, ret); 780772c31eeSKalle Valo return ret; 781772c31eeSKalle Valo } 782772c31eeSKalle Valo } 783772c31eeSKalle Valo 784772c31eeSKalle Valo return 0; 785772c31eeSKalle Valo } 786772c31eeSKalle Valo 78750d41234SKalle Valo static int ath6kl_fetch_fw_api1(struct ath6kl *ar) 788772c31eeSKalle Valo { 789772c31eeSKalle Valo int ret; 790772c31eeSKalle Valo 791772c31eeSKalle Valo ret = ath6kl_fetch_otp_file(ar); 792772c31eeSKalle Valo if (ret) 793772c31eeSKalle Valo return ret; 794772c31eeSKalle Valo 795772c31eeSKalle Valo ret = ath6kl_fetch_fw_file(ar); 796772c31eeSKalle Valo if (ret) 797772c31eeSKalle Valo return ret; 798772c31eeSKalle Valo 799772c31eeSKalle Valo ret = ath6kl_fetch_patch_file(ar); 800772c31eeSKalle Valo if (ret) 801772c31eeSKalle Valo return ret; 802772c31eeSKalle Valo 803772c31eeSKalle Valo return 0; 804772c31eeSKalle Valo } 805bdcd8170SKalle Valo 80650d41234SKalle Valo static int ath6kl_fetch_fw_api2(struct ath6kl *ar) 80750d41234SKalle Valo { 80850d41234SKalle Valo size_t magic_len, len, ie_len; 80950d41234SKalle Valo const struct firmware *fw; 81050d41234SKalle Valo struct ath6kl_fw_ie *hdr; 81150d41234SKalle Valo const char *filename; 81250d41234SKalle Valo const u8 *data; 81397e0496dSKalle Valo int ret, ie_id, i, index, bit; 8148a137480SKalle Valo __le32 *val; 81550d41234SKalle Valo 81650d41234SKalle Valo switch (ar->version.target_ver) { 81750d41234SKalle Valo case AR6003_REV2_VERSION: 81850d41234SKalle Valo filename = AR6003_REV2_FIRMWARE_2_FILE; 81950d41234SKalle Valo break; 82050d41234SKalle Valo case AR6003_REV3_VERSION: 82150d41234SKalle Valo filename = AR6003_REV3_FIRMWARE_2_FILE; 82250d41234SKalle Valo break; 82350d41234SKalle Valo case AR6004_REV1_VERSION: 82450d41234SKalle Valo filename = AR6004_REV1_FIRMWARE_2_FILE; 82550d41234SKalle Valo break; 82650d41234SKalle Valo default: 82750d41234SKalle Valo return -EOPNOTSUPP; 82850d41234SKalle Valo } 82950d41234SKalle Valo 83050d41234SKalle Valo ret = request_firmware(&fw, filename, ar->dev); 83150d41234SKalle Valo if (ret) 83250d41234SKalle Valo return ret; 83350d41234SKalle Valo 83450d41234SKalle Valo data = fw->data; 83550d41234SKalle Valo len = fw->size; 83650d41234SKalle Valo 83750d41234SKalle Valo /* magic also includes the null byte, check that as well */ 83850d41234SKalle Valo magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1; 83950d41234SKalle Valo 84050d41234SKalle Valo if (len < magic_len) { 84150d41234SKalle Valo ret = -EINVAL; 84250d41234SKalle Valo goto out; 84350d41234SKalle Valo } 84450d41234SKalle Valo 84550d41234SKalle Valo if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) { 84650d41234SKalle Valo ret = -EINVAL; 84750d41234SKalle Valo goto out; 84850d41234SKalle Valo } 84950d41234SKalle Valo 85050d41234SKalle Valo len -= magic_len; 85150d41234SKalle Valo data += magic_len; 85250d41234SKalle Valo 85350d41234SKalle Valo /* loop elements */ 85450d41234SKalle Valo while (len > sizeof(struct ath6kl_fw_ie)) { 85550d41234SKalle Valo /* hdr is unaligned! */ 85650d41234SKalle Valo hdr = (struct ath6kl_fw_ie *) data; 85750d41234SKalle Valo 85850d41234SKalle Valo ie_id = le32_to_cpup(&hdr->id); 85950d41234SKalle Valo ie_len = le32_to_cpup(&hdr->len); 86050d41234SKalle Valo 86150d41234SKalle Valo len -= sizeof(*hdr); 86250d41234SKalle Valo data += sizeof(*hdr); 86350d41234SKalle Valo 86450d41234SKalle Valo if (len < ie_len) { 86550d41234SKalle Valo ret = -EINVAL; 86650d41234SKalle Valo goto out; 86750d41234SKalle Valo } 86850d41234SKalle Valo 86950d41234SKalle Valo switch (ie_id) { 87050d41234SKalle Valo case ATH6KL_FW_IE_OTP_IMAGE: 871ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n", 8726bc36431SKalle Valo ie_len); 8736bc36431SKalle Valo 87450d41234SKalle Valo ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); 87550d41234SKalle Valo 87650d41234SKalle Valo if (ar->fw_otp == NULL) { 87750d41234SKalle Valo ret = -ENOMEM; 87850d41234SKalle Valo goto out; 87950d41234SKalle Valo } 88050d41234SKalle Valo 88150d41234SKalle Valo ar->fw_otp_len = ie_len; 88250d41234SKalle Valo break; 88350d41234SKalle Valo case ATH6KL_FW_IE_FW_IMAGE: 884ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n", 8856bc36431SKalle Valo ie_len); 8866bc36431SKalle Valo 88750d41234SKalle Valo ar->fw = kmemdup(data, ie_len, GFP_KERNEL); 88850d41234SKalle Valo 88950d41234SKalle Valo if (ar->fw == NULL) { 89050d41234SKalle Valo ret = -ENOMEM; 89150d41234SKalle Valo goto out; 89250d41234SKalle Valo } 89350d41234SKalle Valo 89450d41234SKalle Valo ar->fw_len = ie_len; 89550d41234SKalle Valo break; 89650d41234SKalle Valo case ATH6KL_FW_IE_PATCH_IMAGE: 897ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n", 8986bc36431SKalle Valo ie_len); 8996bc36431SKalle Valo 90050d41234SKalle Valo ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); 90150d41234SKalle Valo 90250d41234SKalle Valo if (ar->fw_patch == NULL) { 90350d41234SKalle Valo ret = -ENOMEM; 90450d41234SKalle Valo goto out; 90550d41234SKalle Valo } 90650d41234SKalle Valo 90750d41234SKalle Valo ar->fw_patch_len = ie_len; 90850d41234SKalle Valo break; 9098a137480SKalle Valo case ATH6KL_FW_IE_RESERVED_RAM_SIZE: 9108a137480SKalle Valo val = (__le32 *) data; 9118a137480SKalle Valo ar->hw.reserved_ram_size = le32_to_cpup(val); 9126bc36431SKalle Valo 9136bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 9146bc36431SKalle Valo "found reserved ram size ie 0x%d\n", 9156bc36431SKalle Valo ar->hw.reserved_ram_size); 9168a137480SKalle Valo break; 91797e0496dSKalle Valo case ATH6KL_FW_IE_CAPABILITIES: 9186bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 919ef548626SKalle Valo "found firmware capabilities ie (%zd B)\n", 9206bc36431SKalle Valo ie_len); 9216bc36431SKalle Valo 92297e0496dSKalle Valo for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { 92397e0496dSKalle Valo index = ALIGN(i, 8) / 8; 92497e0496dSKalle Valo bit = i % 8; 92597e0496dSKalle Valo 92697e0496dSKalle Valo if (data[index] & (1 << bit)) 92797e0496dSKalle Valo __set_bit(i, ar->fw_capabilities); 92897e0496dSKalle Valo } 9296bc36431SKalle Valo 9306bc36431SKalle Valo ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "", 9316bc36431SKalle Valo ar->fw_capabilities, 9326bc36431SKalle Valo sizeof(ar->fw_capabilities)); 93397e0496dSKalle Valo break; 9341b4304daSKalle Valo case ATH6KL_FW_IE_PATCH_ADDR: 9351b4304daSKalle Valo if (ie_len != sizeof(*val)) 9361b4304daSKalle Valo break; 9371b4304daSKalle Valo 9381b4304daSKalle Valo val = (__le32 *) data; 9391b4304daSKalle Valo ar->hw.dataset_patch_addr = le32_to_cpup(val); 9406bc36431SKalle Valo 9416bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 9426bc36431SKalle Valo "found patch address ie 0x%d\n", 9436bc36431SKalle Valo ar->hw.dataset_patch_addr); 9441b4304daSKalle Valo break; 94550d41234SKalle Valo default: 9466bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n", 94750d41234SKalle Valo le32_to_cpup(&hdr->id)); 94850d41234SKalle Valo break; 94950d41234SKalle Valo } 95050d41234SKalle Valo 95150d41234SKalle Valo len -= ie_len; 95250d41234SKalle Valo data += ie_len; 95350d41234SKalle Valo }; 95450d41234SKalle Valo 95550d41234SKalle Valo ret = 0; 95650d41234SKalle Valo out: 95750d41234SKalle Valo release_firmware(fw); 95850d41234SKalle Valo 95950d41234SKalle Valo return ret; 96050d41234SKalle Valo } 96150d41234SKalle Valo 96250d41234SKalle Valo static int ath6kl_fetch_firmwares(struct ath6kl *ar) 96350d41234SKalle Valo { 96450d41234SKalle Valo int ret; 96550d41234SKalle Valo 96650d41234SKalle Valo ret = ath6kl_fetch_board_file(ar); 96750d41234SKalle Valo if (ret) 96850d41234SKalle Valo return ret; 96950d41234SKalle Valo 97050d41234SKalle Valo ret = ath6kl_fetch_fw_api2(ar); 9716bc36431SKalle Valo if (ret == 0) { 9726bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n"); 97350d41234SKalle Valo return 0; 9746bc36431SKalle Valo } 97550d41234SKalle Valo 97650d41234SKalle Valo ret = ath6kl_fetch_fw_api1(ar); 97750d41234SKalle Valo if (ret) 97850d41234SKalle Valo return ret; 97950d41234SKalle Valo 9806bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n"); 9816bc36431SKalle Valo 98250d41234SKalle Valo return 0; 98350d41234SKalle Valo } 98450d41234SKalle Valo 985bdcd8170SKalle Valo static int ath6kl_upload_board_file(struct ath6kl *ar) 986bdcd8170SKalle Valo { 987bdcd8170SKalle Valo u32 board_address, board_ext_address, param; 98831024d99SKevin Fang u32 board_data_size, board_ext_data_size; 989bdcd8170SKalle Valo int ret; 990bdcd8170SKalle Valo 991772c31eeSKalle Valo if (WARN_ON(ar->fw_board == NULL)) 992772c31eeSKalle Valo return -ENOENT; 993bdcd8170SKalle Valo 99431024d99SKevin Fang /* 99531024d99SKevin Fang * Determine where in Target RAM to write Board Data. 99631024d99SKevin Fang * For AR6004, host determine Target RAM address for 99731024d99SKevin Fang * writing board data. 99831024d99SKevin Fang */ 99931024d99SKevin Fang if (ar->target_type == TARGET_TYPE_AR6004) { 100031024d99SKevin Fang board_address = AR6004_REV1_BOARD_DATA_ADDRESS; 100131024d99SKevin Fang ath6kl_bmi_write(ar, 100231024d99SKevin Fang ath6kl_get_hi_item_addr(ar, 100331024d99SKevin Fang HI_ITEM(hi_board_data)), 100431024d99SKevin Fang (u8 *) &board_address, 4); 100531024d99SKevin Fang } else { 1006bdcd8170SKalle Valo ath6kl_bmi_read(ar, 1007bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1008bdcd8170SKalle Valo HI_ITEM(hi_board_data)), 1009bdcd8170SKalle Valo (u8 *) &board_address, 4); 101031024d99SKevin Fang } 101131024d99SKevin Fang 1012bdcd8170SKalle Valo /* determine where in target ram to write extended board data */ 1013bdcd8170SKalle Valo ath6kl_bmi_read(ar, 1014bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1015bdcd8170SKalle Valo HI_ITEM(hi_board_ext_data)), 1016bdcd8170SKalle Valo (u8 *) &board_ext_address, 4); 1017bdcd8170SKalle Valo 1018bdcd8170SKalle Valo if (board_ext_address == 0) { 1019bdcd8170SKalle Valo ath6kl_err("Failed to get board file target address.\n"); 1020bdcd8170SKalle Valo return -EINVAL; 1021bdcd8170SKalle Valo } 1022bdcd8170SKalle Valo 102331024d99SKevin Fang switch (ar->target_type) { 102431024d99SKevin Fang case TARGET_TYPE_AR6003: 102531024d99SKevin Fang board_data_size = AR6003_BOARD_DATA_SZ; 102631024d99SKevin Fang board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ; 102731024d99SKevin Fang break; 102831024d99SKevin Fang case TARGET_TYPE_AR6004: 102931024d99SKevin Fang board_data_size = AR6004_BOARD_DATA_SZ; 103031024d99SKevin Fang board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ; 103131024d99SKevin Fang break; 103231024d99SKevin Fang default: 103331024d99SKevin Fang WARN_ON(1); 103431024d99SKevin Fang return -EINVAL; 103531024d99SKevin Fang break; 103631024d99SKevin Fang } 103731024d99SKevin Fang 103831024d99SKevin Fang if (ar->fw_board_len == (board_data_size + 103931024d99SKevin Fang board_ext_data_size)) { 104031024d99SKevin Fang 1041bdcd8170SKalle Valo /* write extended board data */ 10426bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 10436bc36431SKalle Valo "writing extended board data to 0x%x (%d B)\n", 10446bc36431SKalle Valo board_ext_address, board_ext_data_size); 10456bc36431SKalle Valo 1046bdcd8170SKalle Valo ret = ath6kl_bmi_write(ar, board_ext_address, 104731024d99SKevin Fang ar->fw_board + board_data_size, 104831024d99SKevin Fang board_ext_data_size); 1049bdcd8170SKalle Valo if (ret) { 1050bdcd8170SKalle Valo ath6kl_err("Failed to write extended board data: %d\n", 1051bdcd8170SKalle Valo ret); 1052bdcd8170SKalle Valo return ret; 1053bdcd8170SKalle Valo } 1054bdcd8170SKalle Valo 1055bdcd8170SKalle Valo /* record that extended board data is initialized */ 105631024d99SKevin Fang param = (board_ext_data_size << 16) | 1; 105731024d99SKevin Fang 1058bdcd8170SKalle Valo ath6kl_bmi_write(ar, 1059bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1060bdcd8170SKalle Valo HI_ITEM(hi_board_ext_data_config)), 1061bdcd8170SKalle Valo (unsigned char *) ¶m, 4); 1062bdcd8170SKalle Valo } 1063bdcd8170SKalle Valo 106431024d99SKevin Fang if (ar->fw_board_len < board_data_size) { 1065bdcd8170SKalle Valo ath6kl_err("Too small board file: %zu\n", ar->fw_board_len); 1066bdcd8170SKalle Valo ret = -EINVAL; 1067bdcd8170SKalle Valo return ret; 1068bdcd8170SKalle Valo } 1069bdcd8170SKalle Valo 10706bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n", 10716bc36431SKalle Valo board_address, board_data_size); 10726bc36431SKalle Valo 1073bdcd8170SKalle Valo ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, 107431024d99SKevin Fang board_data_size); 1075bdcd8170SKalle Valo 1076bdcd8170SKalle Valo if (ret) { 1077bdcd8170SKalle Valo ath6kl_err("Board file bmi write failed: %d\n", ret); 1078bdcd8170SKalle Valo return ret; 1079bdcd8170SKalle Valo } 1080bdcd8170SKalle Valo 1081bdcd8170SKalle Valo /* record the fact that Board Data IS initialized */ 1082bdcd8170SKalle Valo param = 1; 1083bdcd8170SKalle Valo ath6kl_bmi_write(ar, 1084bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1085bdcd8170SKalle Valo HI_ITEM(hi_board_data_initialized)), 1086bdcd8170SKalle Valo (u8 *)¶m, 4); 1087bdcd8170SKalle Valo 1088bdcd8170SKalle Valo return ret; 1089bdcd8170SKalle Valo } 1090bdcd8170SKalle Valo 1091bdcd8170SKalle Valo static int ath6kl_upload_otp(struct ath6kl *ar) 1092bdcd8170SKalle Valo { 1093bdcd8170SKalle Valo u32 address, param; 1094bef26a7fSKalle Valo bool from_hw = false; 1095bdcd8170SKalle Valo int ret; 1096bdcd8170SKalle Valo 1097772c31eeSKalle Valo if (WARN_ON(ar->fw_otp == NULL)) 1098772c31eeSKalle Valo return -ENOENT; 1099bdcd8170SKalle Valo 1100a01ac414SKalle Valo address = ar->hw.app_load_addr; 1101bdcd8170SKalle Valo 1102ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address, 11036bc36431SKalle Valo ar->fw_otp_len); 11046bc36431SKalle Valo 1105bdcd8170SKalle Valo ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, 1106bdcd8170SKalle Valo ar->fw_otp_len); 1107bdcd8170SKalle Valo if (ret) { 1108bdcd8170SKalle Valo ath6kl_err("Failed to upload OTP file: %d\n", ret); 1109bdcd8170SKalle Valo return ret; 1110bdcd8170SKalle Valo } 1111bdcd8170SKalle Valo 1112639d0b89SKalle Valo /* read firmware start address */ 1113639d0b89SKalle Valo ret = ath6kl_bmi_read(ar, 1114639d0b89SKalle Valo ath6kl_get_hi_item_addr(ar, 1115639d0b89SKalle Valo HI_ITEM(hi_app_start)), 1116639d0b89SKalle Valo (u8 *) &address, sizeof(address)); 1117639d0b89SKalle Valo 1118639d0b89SKalle Valo if (ret) { 1119639d0b89SKalle Valo ath6kl_err("Failed to read hi_app_start: %d\n", ret); 1120639d0b89SKalle Valo return ret; 1121639d0b89SKalle Valo } 1122639d0b89SKalle Valo 1123bef26a7fSKalle Valo if (ar->hw.app_start_override_addr == 0) { 1124639d0b89SKalle Valo ar->hw.app_start_override_addr = address; 1125bef26a7fSKalle Valo from_hw = true; 1126bef26a7fSKalle Valo } 1127639d0b89SKalle Valo 1128bef26a7fSKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr%s 0x%x\n", 1129bef26a7fSKalle Valo from_hw ? " (from hw)" : "", 11306bc36431SKalle Valo ar->hw.app_start_override_addr); 11316bc36431SKalle Valo 1132bdcd8170SKalle Valo /* execute the OTP code */ 1133bef26a7fSKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", 1134bef26a7fSKalle Valo ar->hw.app_start_override_addr); 1135bdcd8170SKalle Valo param = 0; 1136bef26a7fSKalle Valo ath6kl_bmi_execute(ar, ar->hw.app_start_override_addr, ¶m); 1137bdcd8170SKalle Valo 1138bdcd8170SKalle Valo return ret; 1139bdcd8170SKalle Valo } 1140bdcd8170SKalle Valo 1141bdcd8170SKalle Valo static int ath6kl_upload_firmware(struct ath6kl *ar) 1142bdcd8170SKalle Valo { 1143bdcd8170SKalle Valo u32 address; 1144bdcd8170SKalle Valo int ret; 1145bdcd8170SKalle Valo 1146772c31eeSKalle Valo if (WARN_ON(ar->fw == NULL)) 1147772c31eeSKalle Valo return -ENOENT; 1148bdcd8170SKalle Valo 1149a01ac414SKalle Valo address = ar->hw.app_load_addr; 1150bdcd8170SKalle Valo 1151ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n", 11526bc36431SKalle Valo address, ar->fw_len); 11536bc36431SKalle Valo 1154bdcd8170SKalle Valo ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); 1155bdcd8170SKalle Valo 1156bdcd8170SKalle Valo if (ret) { 1157bdcd8170SKalle Valo ath6kl_err("Failed to write firmware: %d\n", ret); 1158bdcd8170SKalle Valo return ret; 1159bdcd8170SKalle Valo } 1160bdcd8170SKalle Valo 116131024d99SKevin Fang /* 116231024d99SKevin Fang * Set starting address for firmware 116331024d99SKevin Fang * Don't need to setup app_start override addr on AR6004 116431024d99SKevin Fang */ 116531024d99SKevin Fang if (ar->target_type != TARGET_TYPE_AR6004) { 1166a01ac414SKalle Valo address = ar->hw.app_start_override_addr; 1167bdcd8170SKalle Valo ath6kl_bmi_set_app_start(ar, address); 116831024d99SKevin Fang } 1169bdcd8170SKalle Valo return ret; 1170bdcd8170SKalle Valo } 1171bdcd8170SKalle Valo 1172bdcd8170SKalle Valo static int ath6kl_upload_patch(struct ath6kl *ar) 1173bdcd8170SKalle Valo { 1174bdcd8170SKalle Valo u32 address, param; 1175bdcd8170SKalle Valo int ret; 1176bdcd8170SKalle Valo 1177772c31eeSKalle Valo if (WARN_ON(ar->fw_patch == NULL)) 1178772c31eeSKalle Valo return -ENOENT; 1179bdcd8170SKalle Valo 1180a01ac414SKalle Valo address = ar->hw.dataset_patch_addr; 1181bdcd8170SKalle Valo 1182ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n", 11836bc36431SKalle Valo address, ar->fw_patch_len); 11846bc36431SKalle Valo 1185bdcd8170SKalle Valo ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); 1186bdcd8170SKalle Valo if (ret) { 1187bdcd8170SKalle Valo ath6kl_err("Failed to write patch file: %d\n", ret); 1188bdcd8170SKalle Valo return ret; 1189bdcd8170SKalle Valo } 1190bdcd8170SKalle Valo 1191bdcd8170SKalle Valo param = address; 1192bdcd8170SKalle Valo ath6kl_bmi_write(ar, 1193bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1194bdcd8170SKalle Valo HI_ITEM(hi_dset_list_head)), 1195bdcd8170SKalle Valo (unsigned char *) ¶m, 4); 1196bdcd8170SKalle Valo 1197bdcd8170SKalle Valo return 0; 1198bdcd8170SKalle Valo } 1199bdcd8170SKalle Valo 1200bdcd8170SKalle Valo static int ath6kl_init_upload(struct ath6kl *ar) 1201bdcd8170SKalle Valo { 1202bdcd8170SKalle Valo u32 param, options, sleep, address; 1203bdcd8170SKalle Valo int status = 0; 1204bdcd8170SKalle Valo 120531024d99SKevin Fang if (ar->target_type != TARGET_TYPE_AR6003 && 120631024d99SKevin Fang ar->target_type != TARGET_TYPE_AR6004) 1207bdcd8170SKalle Valo return -EINVAL; 1208bdcd8170SKalle Valo 1209bdcd8170SKalle Valo /* temporarily disable system sleep */ 1210bdcd8170SKalle Valo address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; 1211bdcd8170SKalle Valo status = ath6kl_bmi_reg_read(ar, address, ¶m); 1212bdcd8170SKalle Valo if (status) 1213bdcd8170SKalle Valo return status; 1214bdcd8170SKalle Valo 1215bdcd8170SKalle Valo options = param; 1216bdcd8170SKalle Valo 1217bdcd8170SKalle Valo param |= ATH6KL_OPTION_SLEEP_DISABLE; 1218bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1219bdcd8170SKalle Valo if (status) 1220bdcd8170SKalle Valo return status; 1221bdcd8170SKalle Valo 1222bdcd8170SKalle Valo address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; 1223bdcd8170SKalle Valo status = ath6kl_bmi_reg_read(ar, address, ¶m); 1224bdcd8170SKalle Valo if (status) 1225bdcd8170SKalle Valo return status; 1226bdcd8170SKalle Valo 1227bdcd8170SKalle Valo sleep = param; 1228bdcd8170SKalle Valo 1229bdcd8170SKalle Valo param |= SM(SYSTEM_SLEEP_DISABLE, 1); 1230bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1231bdcd8170SKalle Valo if (status) 1232bdcd8170SKalle Valo return status; 1233bdcd8170SKalle Valo 1234bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "old options: %d, old sleep: %d\n", 1235bdcd8170SKalle Valo options, sleep); 1236bdcd8170SKalle Valo 1237bdcd8170SKalle Valo /* program analog PLL register */ 123831024d99SKevin Fang /* no need to control 40/44MHz clock on AR6004 */ 123931024d99SKevin Fang if (ar->target_type != TARGET_TYPE_AR6004) { 1240bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, 1241bdcd8170SKalle Valo 0xF9104001); 124231024d99SKevin Fang 1243bdcd8170SKalle Valo if (status) 1244bdcd8170SKalle Valo return status; 1245bdcd8170SKalle Valo 1246bdcd8170SKalle Valo /* Run at 80/88MHz by default */ 1247bdcd8170SKalle Valo param = SM(CPU_CLOCK_STANDARD, 1); 1248bdcd8170SKalle Valo 1249bdcd8170SKalle Valo address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS; 1250bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1251bdcd8170SKalle Valo if (status) 1252bdcd8170SKalle Valo return status; 125331024d99SKevin Fang } 1254bdcd8170SKalle Valo 1255bdcd8170SKalle Valo param = 0; 1256bdcd8170SKalle Valo address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS; 1257bdcd8170SKalle Valo param = SM(LPO_CAL_ENABLE, 1); 1258bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1259bdcd8170SKalle Valo if (status) 1260bdcd8170SKalle Valo return status; 1261bdcd8170SKalle Valo 1262bdcd8170SKalle Valo /* WAR to avoid SDIO CRC err */ 1263bdcd8170SKalle Valo if (ar->version.target_ver == AR6003_REV2_VERSION) { 1264bdcd8170SKalle Valo ath6kl_err("temporary war to avoid sdio crc error\n"); 1265bdcd8170SKalle Valo 1266bdcd8170SKalle Valo param = 0x20; 1267bdcd8170SKalle Valo 1268bdcd8170SKalle Valo address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS; 1269bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1270bdcd8170SKalle Valo if (status) 1271bdcd8170SKalle Valo return status; 1272bdcd8170SKalle Valo 1273bdcd8170SKalle Valo address = GPIO_BASE_ADDRESS + GPIO_PIN11_ADDRESS; 1274bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1275bdcd8170SKalle Valo if (status) 1276bdcd8170SKalle Valo return status; 1277bdcd8170SKalle Valo 1278bdcd8170SKalle Valo address = GPIO_BASE_ADDRESS + GPIO_PIN12_ADDRESS; 1279bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1280bdcd8170SKalle Valo if (status) 1281bdcd8170SKalle Valo return status; 1282bdcd8170SKalle Valo 1283bdcd8170SKalle Valo address = GPIO_BASE_ADDRESS + GPIO_PIN13_ADDRESS; 1284bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1285bdcd8170SKalle Valo if (status) 1286bdcd8170SKalle Valo return status; 1287bdcd8170SKalle Valo } 1288bdcd8170SKalle Valo 1289bdcd8170SKalle Valo /* write EEPROM data to Target RAM */ 1290bdcd8170SKalle Valo status = ath6kl_upload_board_file(ar); 1291bdcd8170SKalle Valo if (status) 1292bdcd8170SKalle Valo return status; 1293bdcd8170SKalle Valo 1294bdcd8170SKalle Valo /* transfer One time Programmable data */ 1295bdcd8170SKalle Valo status = ath6kl_upload_otp(ar); 1296bdcd8170SKalle Valo if (status) 1297bdcd8170SKalle Valo return status; 1298bdcd8170SKalle Valo 1299bdcd8170SKalle Valo /* Download Target firmware */ 1300bdcd8170SKalle Valo status = ath6kl_upload_firmware(ar); 1301bdcd8170SKalle Valo if (status) 1302bdcd8170SKalle Valo return status; 1303bdcd8170SKalle Valo 1304bdcd8170SKalle Valo status = ath6kl_upload_patch(ar); 1305bdcd8170SKalle Valo if (status) 1306bdcd8170SKalle Valo return status; 1307bdcd8170SKalle Valo 1308bdcd8170SKalle Valo /* Restore system sleep */ 1309bdcd8170SKalle Valo address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; 1310bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, sleep); 1311bdcd8170SKalle Valo if (status) 1312bdcd8170SKalle Valo return status; 1313bdcd8170SKalle Valo 1314bdcd8170SKalle Valo address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; 1315bdcd8170SKalle Valo param = options | 0x20; 1316bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1317bdcd8170SKalle Valo if (status) 1318bdcd8170SKalle Valo return status; 1319bdcd8170SKalle Valo 1320bdcd8170SKalle Valo /* Configure GPIO AR6003 UART */ 1321bdcd8170SKalle Valo param = CONFIG_AR600x_DEBUG_UART_TX_PIN; 1322bdcd8170SKalle Valo status = ath6kl_bmi_write(ar, 1323bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1324bdcd8170SKalle Valo HI_ITEM(hi_dbg_uart_txpin)), 1325bdcd8170SKalle Valo (u8 *)¶m, 4); 1326bdcd8170SKalle Valo 1327bdcd8170SKalle Valo return status; 1328bdcd8170SKalle Valo } 1329bdcd8170SKalle Valo 1330a01ac414SKalle Valo static int ath6kl_init_hw_params(struct ath6kl *ar) 1331a01ac414SKalle Valo { 1332a01ac414SKalle Valo switch (ar->version.target_ver) { 1333a01ac414SKalle Valo case AR6003_REV2_VERSION: 1334a01ac414SKalle Valo ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; 1335a01ac414SKalle Valo ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS; 1336991b27eaSKalle Valo ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; 1337991b27eaSKalle Valo ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE; 1338bef26a7fSKalle Valo 1339bef26a7fSKalle Valo /* hw2.0 needs override address hardcoded */ 1340bef26a7fSKalle Valo ar->hw.app_start_override_addr = 0x944C00; 1341bef26a7fSKalle Valo 1342a01ac414SKalle Valo break; 1343a01ac414SKalle Valo case AR6003_REV3_VERSION: 1344a01ac414SKalle Valo ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS; 1345a01ac414SKalle Valo ar->hw.app_load_addr = 0x1234; 1346991b27eaSKalle Valo ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; 1347991b27eaSKalle Valo ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE; 1348a01ac414SKalle Valo break; 1349a01ac414SKalle Valo case AR6004_REV1_VERSION: 1350a01ac414SKalle Valo ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; 1351a01ac414SKalle Valo ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS; 1352991b27eaSKalle Valo ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS; 1353991b27eaSKalle Valo ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE; 1354a01ac414SKalle Valo break; 1355a01ac414SKalle Valo default: 1356a01ac414SKalle Valo ath6kl_err("Unsupported hardware version: 0x%x\n", 1357a01ac414SKalle Valo ar->version.target_ver); 1358a01ac414SKalle Valo return -EINVAL; 1359a01ac414SKalle Valo } 1360a01ac414SKalle Valo 13616bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 13626bc36431SKalle Valo "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n", 13636bc36431SKalle Valo ar->version.target_ver, ar->target_type, 13646bc36431SKalle Valo ar->hw.dataset_patch_addr, ar->hw.app_load_addr); 13656bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 13666bc36431SKalle Valo "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x", 13676bc36431SKalle Valo ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr, 13686bc36431SKalle Valo ar->hw.reserved_ram_size); 13696bc36431SKalle Valo 1370a01ac414SKalle Valo return 0; 1371a01ac414SKalle Valo } 1372a01ac414SKalle Valo 1373521dffccSVasanthakumar Thiagarajan static int ath6kl_init(struct ath6kl *ar) 1374bdcd8170SKalle Valo { 1375bdcd8170SKalle Valo int status = 0; 1376bdcd8170SKalle Valo s32 timeleft; 13778dafb70eSVasanthakumar Thiagarajan struct net_device *ndev; 1378bdcd8170SKalle Valo 1379bdcd8170SKalle Valo if (!ar) 1380bdcd8170SKalle Valo return -EIO; 1381bdcd8170SKalle Valo 1382bdcd8170SKalle Valo /* Do we need to finish the BMI phase */ 1383bdcd8170SKalle Valo if (ath6kl_bmi_done(ar)) { 1384bdcd8170SKalle Valo status = -EIO; 1385bdcd8170SKalle Valo goto ath6kl_init_done; 1386bdcd8170SKalle Valo } 1387bdcd8170SKalle Valo 1388bdcd8170SKalle Valo /* Indicate that WMI is enabled (although not ready yet) */ 1389bdcd8170SKalle Valo set_bit(WMI_ENABLED, &ar->flag); 13902865785eSVasanthakumar Thiagarajan ar->wmi = ath6kl_wmi_init(ar); 1391bdcd8170SKalle Valo if (!ar->wmi) { 1392bdcd8170SKalle Valo ath6kl_err("failed to initialize wmi\n"); 1393bdcd8170SKalle Valo status = -EIO; 1394bdcd8170SKalle Valo goto ath6kl_init_done; 1395bdcd8170SKalle Valo } 1396bdcd8170SKalle Valo 1397bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); 1398bdcd8170SKalle Valo 13998dafb70eSVasanthakumar Thiagarajan status = ath6kl_register_ieee80211_hw(ar); 14008dafb70eSVasanthakumar Thiagarajan if (status) 14018dafb70eSVasanthakumar Thiagarajan goto err_node_cleanup; 14028dafb70eSVasanthakumar Thiagarajan 14038dafb70eSVasanthakumar Thiagarajan status = ath6kl_debug_init(ar); 14048dafb70eSVasanthakumar Thiagarajan if (status) { 14058dafb70eSVasanthakumar Thiagarajan wiphy_unregister(ar->wiphy); 14068dafb70eSVasanthakumar Thiagarajan goto err_node_cleanup; 14078dafb70eSVasanthakumar Thiagarajan } 14088dafb70eSVasanthakumar Thiagarajan 14098dafb70eSVasanthakumar Thiagarajan /* Add an initial station interface */ 1410334234b5SVasanthakumar Thiagarajan ndev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0); 14118dafb70eSVasanthakumar Thiagarajan if (!ndev) { 14128dafb70eSVasanthakumar Thiagarajan ath6kl_err("Failed to instantiate a network device\n"); 14138dafb70eSVasanthakumar Thiagarajan status = -ENOMEM; 14148dafb70eSVasanthakumar Thiagarajan wiphy_unregister(ar->wiphy); 14158dafb70eSVasanthakumar Thiagarajan goto err_debug_init; 14168dafb70eSVasanthakumar Thiagarajan } 14178dafb70eSVasanthakumar Thiagarajan 14188dafb70eSVasanthakumar Thiagarajan 14198dafb70eSVasanthakumar Thiagarajan ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", 1420*28ae58ddSVasanthakumar Thiagarajan __func__, ndev->name, ndev, ar); 14218dafb70eSVasanthakumar Thiagarajan 1422bdcd8170SKalle Valo /* 1423bdcd8170SKalle Valo * The reason we have to wait for the target here is that the 1424bdcd8170SKalle Valo * driver layer has to init BMI in order to set the host block 1425bdcd8170SKalle Valo * size. 1426bdcd8170SKalle Valo */ 1427ad226ec2SKalle Valo if (ath6kl_htc_wait_target(ar->htc_target)) { 1428bdcd8170SKalle Valo status = -EIO; 14298dafb70eSVasanthakumar Thiagarajan goto err_if_deinit; 1430bdcd8170SKalle Valo } 1431bdcd8170SKalle Valo 1432bdcd8170SKalle Valo if (ath6kl_init_service_ep(ar)) { 1433bdcd8170SKalle Valo status = -EIO; 1434bdcd8170SKalle Valo goto err_cleanup_scatter; 1435bdcd8170SKalle Valo } 1436bdcd8170SKalle Valo 1437bdcd8170SKalle Valo /* setup access class priority mappings */ 1438bdcd8170SKalle Valo ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ 1439bdcd8170SKalle Valo ar->ac_stream_pri_map[WMM_AC_BE] = 1; 1440bdcd8170SKalle Valo ar->ac_stream_pri_map[WMM_AC_VI] = 2; 1441bdcd8170SKalle Valo ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ 1442bdcd8170SKalle Valo 1443bdcd8170SKalle Valo /* give our connected endpoints some buffers */ 1444bdcd8170SKalle Valo ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); 1445bdcd8170SKalle Valo ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); 1446bdcd8170SKalle Valo 1447bdcd8170SKalle Valo /* allocate some buffers that handle larger AMSDU frames */ 1448bdcd8170SKalle Valo ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); 1449bdcd8170SKalle Valo 1450bdcd8170SKalle Valo /* setup credit distribution */ 1451bdcd8170SKalle Valo ath6k_setup_credit_dist(ar->htc_target, &ar->credit_state_info); 1452bdcd8170SKalle Valo 1453bdcd8170SKalle Valo ath6kl_cookie_init(ar); 1454bdcd8170SKalle Valo 1455bdcd8170SKalle Valo /* start HTC */ 1456ad226ec2SKalle Valo status = ath6kl_htc_start(ar->htc_target); 1457bdcd8170SKalle Valo 1458bdcd8170SKalle Valo if (status) { 1459bdcd8170SKalle Valo ath6kl_cookie_cleanup(ar); 1460bdcd8170SKalle Valo goto err_rxbuf_cleanup; 1461bdcd8170SKalle Valo } 1462bdcd8170SKalle Valo 1463bdcd8170SKalle Valo /* Wait for Wmi event to be ready */ 1464bdcd8170SKalle Valo timeleft = wait_event_interruptible_timeout(ar->event_wq, 1465bdcd8170SKalle Valo test_bit(WMI_READY, 1466bdcd8170SKalle Valo &ar->flag), 1467bdcd8170SKalle Valo WMI_TIMEOUT); 1468bdcd8170SKalle Valo 14696bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); 14706bc36431SKalle Valo 1471bdcd8170SKalle Valo if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { 1472bdcd8170SKalle Valo ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", 1473bdcd8170SKalle Valo ATH6KL_ABI_VERSION, ar->version.abi_ver); 1474bdcd8170SKalle Valo status = -EIO; 1475bdcd8170SKalle Valo goto err_htc_stop; 1476bdcd8170SKalle Valo } 1477bdcd8170SKalle Valo 1478bdcd8170SKalle Valo if (!timeleft || signal_pending(current)) { 1479bdcd8170SKalle Valo ath6kl_err("wmi is not ready or wait was interrupted\n"); 1480bdcd8170SKalle Valo status = -EIO; 1481bdcd8170SKalle Valo goto err_htc_stop; 1482bdcd8170SKalle Valo } 1483bdcd8170SKalle Valo 1484bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__); 1485bdcd8170SKalle Valo 1486bdcd8170SKalle Valo /* communicate the wmi protocol verision to the target */ 1487bdcd8170SKalle Valo if ((ath6kl_set_host_app_area(ar)) != 0) 1488bdcd8170SKalle Valo ath6kl_err("unable to set the host app area\n"); 1489bdcd8170SKalle Valo 1490bdcd8170SKalle Valo ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | 1491bdcd8170SKalle Valo ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; 1492bdcd8170SKalle Valo 1493be98e3a4SVasanthakumar Thiagarajan ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | 1494562a7480SJohannes Berg WIPHY_FLAG_HAVE_AP_SME; 1495011a36e1SVivek Natarajan 1496bdcd8170SKalle Valo status = ath6kl_target_config_wlan_params(ar); 1497bdcd8170SKalle Valo if (!status) 1498bdcd8170SKalle Valo goto ath6kl_init_done; 1499bdcd8170SKalle Valo 1500bdcd8170SKalle Valo err_htc_stop: 1501ad226ec2SKalle Valo ath6kl_htc_stop(ar->htc_target); 1502bdcd8170SKalle Valo err_rxbuf_cleanup: 1503ad226ec2SKalle Valo ath6kl_htc_flush_rx_buf(ar->htc_target); 1504bdcd8170SKalle Valo ath6kl_cleanup_amsdu_rxbufs(ar); 1505bdcd8170SKalle Valo err_cleanup_scatter: 1506bdcd8170SKalle Valo ath6kl_hif_cleanup_scatter(ar); 15078dafb70eSVasanthakumar Thiagarajan err_if_deinit: 1508108438bcSVasanthakumar Thiagarajan ath6kl_deinit_if_data(netdev_priv(ndev)); 15098dafb70eSVasanthakumar Thiagarajan wiphy_unregister(ar->wiphy); 15108dafb70eSVasanthakumar Thiagarajan err_debug_init: 15118dafb70eSVasanthakumar Thiagarajan ath6kl_debug_cleanup(ar); 1512852bd9d9SVasanthakumar Thiagarajan err_node_cleanup: 1513bdcd8170SKalle Valo ath6kl_wmi_shutdown(ar->wmi); 1514bdcd8170SKalle Valo clear_bit(WMI_ENABLED, &ar->flag); 1515bdcd8170SKalle Valo ar->wmi = NULL; 1516bdcd8170SKalle Valo 1517bdcd8170SKalle Valo ath6kl_init_done: 1518bdcd8170SKalle Valo return status; 1519bdcd8170SKalle Valo } 1520bdcd8170SKalle Valo 1521bdcd8170SKalle Valo int ath6kl_core_init(struct ath6kl *ar) 1522bdcd8170SKalle Valo { 1523bdcd8170SKalle Valo int ret = 0; 1524bdcd8170SKalle Valo struct ath6kl_bmi_target_info targ_info; 1525bdcd8170SKalle Valo 1526bdcd8170SKalle Valo ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); 1527bdcd8170SKalle Valo if (!ar->ath6kl_wq) 1528bdcd8170SKalle Valo return -ENOMEM; 1529bdcd8170SKalle Valo 1530bdcd8170SKalle Valo ret = ath6kl_bmi_init(ar); 1531bdcd8170SKalle Valo if (ret) 1532bdcd8170SKalle Valo goto err_wq; 1533bdcd8170SKalle Valo 1534bdcd8170SKalle Valo ret = ath6kl_bmi_get_target_info(ar, &targ_info); 1535bdcd8170SKalle Valo if (ret) 1536bdcd8170SKalle Valo goto err_bmi_cleanup; 1537bdcd8170SKalle Valo 1538bdcd8170SKalle Valo ar->version.target_ver = le32_to_cpu(targ_info.version); 1539bdcd8170SKalle Valo ar->target_type = le32_to_cpu(targ_info.type); 1540be98e3a4SVasanthakumar Thiagarajan ar->wiphy->hw_version = le32_to_cpu(targ_info.version); 1541bdcd8170SKalle Valo 1542a01ac414SKalle Valo ret = ath6kl_init_hw_params(ar); 1543a01ac414SKalle Valo if (ret) 1544a01ac414SKalle Valo goto err_bmi_cleanup; 1545a01ac414SKalle Valo 1546bdcd8170SKalle Valo ret = ath6kl_configure_target(ar); 1547bdcd8170SKalle Valo if (ret) 1548bdcd8170SKalle Valo goto err_bmi_cleanup; 1549bdcd8170SKalle Valo 1550ad226ec2SKalle Valo ar->htc_target = ath6kl_htc_create(ar); 1551bdcd8170SKalle Valo 1552bdcd8170SKalle Valo if (!ar->htc_target) { 1553bdcd8170SKalle Valo ret = -ENOMEM; 1554bdcd8170SKalle Valo goto err_bmi_cleanup; 1555bdcd8170SKalle Valo } 1556bdcd8170SKalle Valo 1557772c31eeSKalle Valo ret = ath6kl_fetch_firmwares(ar); 1558772c31eeSKalle Valo if (ret) 1559772c31eeSKalle Valo goto err_htc_cleanup; 1560772c31eeSKalle Valo 1561bdcd8170SKalle Valo ret = ath6kl_init_upload(ar); 1562bdcd8170SKalle Valo if (ret) 1563bdcd8170SKalle Valo goto err_htc_cleanup; 1564bdcd8170SKalle Valo 1565521dffccSVasanthakumar Thiagarajan ret = ath6kl_init(ar); 1566bdcd8170SKalle Valo if (ret) 1567bdcd8170SKalle Valo goto err_htc_cleanup; 1568bdcd8170SKalle Valo 1569bdcd8170SKalle Valo return ret; 1570bdcd8170SKalle Valo 1571bdcd8170SKalle Valo err_htc_cleanup: 1572ad226ec2SKalle Valo ath6kl_htc_cleanup(ar->htc_target); 1573bdcd8170SKalle Valo err_bmi_cleanup: 1574bdcd8170SKalle Valo ath6kl_bmi_cleanup(ar); 1575bdcd8170SKalle Valo err_wq: 1576bdcd8170SKalle Valo destroy_workqueue(ar->ath6kl_wq); 15778dafb70eSVasanthakumar Thiagarajan 1578bdcd8170SKalle Valo return ret; 1579bdcd8170SKalle Valo } 1580bdcd8170SKalle Valo 1581bdcd8170SKalle Valo void ath6kl_stop_txrx(struct ath6kl *ar) 1582bdcd8170SKalle Valo { 158359c98449SVasanthakumar Thiagarajan struct ath6kl_vif *vif = ar->vif; 1584*28ae58ddSVasanthakumar Thiagarajan struct net_device *ndev = vif->ndev; 1585bdcd8170SKalle Valo 1586bdcd8170SKalle Valo if (!ndev) 1587bdcd8170SKalle Valo return; 1588bdcd8170SKalle Valo 1589bdcd8170SKalle Valo set_bit(DESTROY_IN_PROGRESS, &ar->flag); 1590bdcd8170SKalle Valo 1591bdcd8170SKalle Valo if (down_interruptible(&ar->sem)) { 1592bdcd8170SKalle Valo ath6kl_err("down_interruptible failed\n"); 1593bdcd8170SKalle Valo return; 1594bdcd8170SKalle Valo } 1595bdcd8170SKalle Valo 1596bdcd8170SKalle Valo if (ar->wlan_pwr_state != WLAN_POWER_STATE_CUT_PWR) 1597bdcd8170SKalle Valo ath6kl_stop_endpoint(ndev, false, true); 1598bdcd8170SKalle Valo 159959c98449SVasanthakumar Thiagarajan clear_bit(WLAN_ENABLED, &vif->flags); 1600bdcd8170SKalle Valo } 1601bdcd8170SKalle Valo 1602bdcd8170SKalle Valo /* 1603bdcd8170SKalle Valo * We need to differentiate between the surprise and planned removal of the 1604bdcd8170SKalle Valo * device because of the following consideration: 1605bdcd8170SKalle Valo * 1606bdcd8170SKalle Valo * - In case of surprise removal, the hcd already frees up the pending 1607bdcd8170SKalle Valo * for the device and hence there is no need to unregister the function 1608bdcd8170SKalle Valo * driver inorder to get these requests. For planned removal, the function 1609bdcd8170SKalle Valo * driver has to explicitly unregister itself to have the hcd return all the 1610bdcd8170SKalle Valo * pending requests before the data structures for the devices are freed up. 1611bdcd8170SKalle Valo * Note that as per the current implementation, the function driver will 1612bdcd8170SKalle Valo * end up releasing all the devices since there is no API to selectively 1613bdcd8170SKalle Valo * release a particular device. 1614bdcd8170SKalle Valo * 1615bdcd8170SKalle Valo * - Certain commands issued to the target can be skipped for surprise 1616bdcd8170SKalle Valo * removal since they will anyway not go through. 1617bdcd8170SKalle Valo */ 1618bdcd8170SKalle Valo void ath6kl_destroy(struct net_device *dev, unsigned int unregister) 1619bdcd8170SKalle Valo { 1620bdcd8170SKalle Valo struct ath6kl *ar; 1621bdcd8170SKalle Valo 1622bdcd8170SKalle Valo if (!dev || !ath6kl_priv(dev)) { 1623bdcd8170SKalle Valo ath6kl_err("failed to get device structure\n"); 1624bdcd8170SKalle Valo return; 1625bdcd8170SKalle Valo } 1626bdcd8170SKalle Valo 1627bdcd8170SKalle Valo ar = ath6kl_priv(dev); 1628bdcd8170SKalle Valo 1629bdcd8170SKalle Valo destroy_workqueue(ar->ath6kl_wq); 1630bdcd8170SKalle Valo 1631bdcd8170SKalle Valo if (ar->htc_target) 1632ad226ec2SKalle Valo ath6kl_htc_cleanup(ar->htc_target); 1633bdcd8170SKalle Valo 1634bdcd8170SKalle Valo ath6kl_cookie_cleanup(ar); 1635bdcd8170SKalle Valo 1636bdcd8170SKalle Valo ath6kl_cleanup_amsdu_rxbufs(ar); 1637bdcd8170SKalle Valo 1638bdcd8170SKalle Valo ath6kl_bmi_cleanup(ar); 1639bdcd8170SKalle Valo 1640bdf5396bSKalle Valo ath6kl_debug_cleanup(ar); 1641bdf5396bSKalle Valo 1642108438bcSVasanthakumar Thiagarajan ath6kl_deinit_if_data(netdev_priv(dev)); 1643bdcd8170SKalle Valo 164419703573SRaja Mani kfree(ar->fw_board); 164519703573SRaja Mani kfree(ar->fw_otp); 164619703573SRaja Mani kfree(ar->fw); 164719703573SRaja Mani kfree(ar->fw_patch); 164819703573SRaja Mani 16498dafb70eSVasanthakumar Thiagarajan ath6kl_deinit_ieee80211_hw(ar); 1650bdcd8170SKalle Valo } 1651