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> 19f7830202SSangwook Lee #include <linux/errno.h> 2092ecbff4SSam Leffler #include <linux/of.h> 21bdcd8170SKalle Valo #include <linux/mmc/sdio_func.h> 22bdcd8170SKalle Valo #include "core.h" 23bdcd8170SKalle Valo #include "cfg80211.h" 24bdcd8170SKalle Valo #include "target.h" 25bdcd8170SKalle Valo #include "debug.h" 26bdcd8170SKalle Valo #include "hif-ops.h" 27bdcd8170SKalle Valo 28bdcd8170SKalle Valo unsigned int debug_mask; 29003353b0SKalle Valo static unsigned int testmode; 30bdcd8170SKalle Valo 31bdcd8170SKalle Valo module_param(debug_mask, uint, 0644); 32003353b0SKalle Valo module_param(testmode, uint, 0644); 33bdcd8170SKalle Valo 34bdcd8170SKalle Valo /* 35bdcd8170SKalle Valo * Include definitions here that can be used to tune the WLAN module 36bdcd8170SKalle Valo * behavior. Different customers can tune the behavior as per their needs, 37bdcd8170SKalle Valo * here. 38bdcd8170SKalle Valo */ 39bdcd8170SKalle Valo 40bdcd8170SKalle Valo /* 41bdcd8170SKalle Valo * This configuration item enable/disable keepalive support. 42bdcd8170SKalle Valo * Keepalive support: In the absence of any data traffic to AP, null 43bdcd8170SKalle Valo * frames will be sent to the AP at periodic interval, to keep the association 44bdcd8170SKalle Valo * active. This configuration item defines the periodic interval. 45bdcd8170SKalle Valo * Use value of zero to disable keepalive support 46bdcd8170SKalle Valo * Default: 60 seconds 47bdcd8170SKalle Valo */ 48bdcd8170SKalle Valo #define WLAN_CONFIG_KEEP_ALIVE_INTERVAL 60 49bdcd8170SKalle Valo 50bdcd8170SKalle Valo /* 51bdcd8170SKalle Valo * This configuration item sets the value of disconnect timeout 52bdcd8170SKalle Valo * Firmware delays sending the disconnec event to the host for this 53bdcd8170SKalle Valo * timeout after is gets disconnected from the current AP. 54bdcd8170SKalle Valo * If the firmware successly roams within the disconnect timeout 55bdcd8170SKalle Valo * it sends a new connect event 56bdcd8170SKalle Valo */ 57bdcd8170SKalle Valo #define WLAN_CONFIG_DISCONNECT_TIMEOUT 10 58bdcd8170SKalle Valo 59bdcd8170SKalle Valo #define CONFIG_AR600x_DEBUG_UART_TX_PIN 8 60bdcd8170SKalle Valo 61bdcd8170SKalle Valo #define ATH6KL_DATA_OFFSET 64 62bdcd8170SKalle Valo struct sk_buff *ath6kl_buf_alloc(int size) 63bdcd8170SKalle Valo { 64bdcd8170SKalle Valo struct sk_buff *skb; 65bdcd8170SKalle Valo u16 reserved; 66bdcd8170SKalle Valo 67bdcd8170SKalle Valo /* Add chacheline space at front and back of buffer */ 68bdcd8170SKalle Valo reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + 691df94a85SVasanthakumar Thiagarajan sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES; 70bdcd8170SKalle Valo skb = dev_alloc_skb(size + reserved); 71bdcd8170SKalle Valo 72bdcd8170SKalle Valo if (skb) 73bdcd8170SKalle Valo skb_reserve(skb, reserved - L1_CACHE_BYTES); 74bdcd8170SKalle Valo return skb; 75bdcd8170SKalle Valo } 76bdcd8170SKalle Valo 77e29f25f5SVasanthakumar Thiagarajan void ath6kl_init_profile_info(struct ath6kl_vif *vif) 78bdcd8170SKalle Valo { 793450334fSVasanthakumar Thiagarajan vif->ssid_len = 0; 803450334fSVasanthakumar Thiagarajan memset(vif->ssid, 0, sizeof(vif->ssid)); 813450334fSVasanthakumar Thiagarajan 823450334fSVasanthakumar Thiagarajan vif->dot11_auth_mode = OPEN_AUTH; 833450334fSVasanthakumar Thiagarajan vif->auth_mode = NONE_AUTH; 843450334fSVasanthakumar Thiagarajan vif->prwise_crypto = NONE_CRYPT; 853450334fSVasanthakumar Thiagarajan vif->prwise_crypto_len = 0; 863450334fSVasanthakumar Thiagarajan vif->grp_crypto = NONE_CRYPT; 873450334fSVasanthakumar Thiagarajan vif->grp_crypto_len = 0; 886f2a73f9SVasanthakumar Thiagarajan memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); 898c8b65e3SVasanthakumar Thiagarajan memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); 908c8b65e3SVasanthakumar Thiagarajan memset(vif->bssid, 0, sizeof(vif->bssid)); 91f74bac54SVasanthakumar Thiagarajan vif->bss_ch = 0; 92bdcd8170SKalle Valo } 93bdcd8170SKalle Valo 94bdcd8170SKalle Valo static int ath6kl_set_host_app_area(struct ath6kl *ar) 95bdcd8170SKalle Valo { 96bdcd8170SKalle Valo u32 address, data; 97bdcd8170SKalle Valo struct host_app_area host_app_area; 98bdcd8170SKalle Valo 99bdcd8170SKalle Valo /* Fetch the address of the host_app_area_s 100bdcd8170SKalle Valo * instance in the host interest area */ 101bdcd8170SKalle Valo address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); 10231024d99SKevin Fang address = TARG_VTOP(ar->target_type, address); 103bdcd8170SKalle Valo 104addb44beSKalle Valo if (ath6kl_diag_read32(ar, address, &data)) 105bdcd8170SKalle Valo return -EIO; 106bdcd8170SKalle Valo 10731024d99SKevin Fang address = TARG_VTOP(ar->target_type, data); 108cbf49a6fSKalle Valo host_app_area.wmi_protocol_ver = cpu_to_le32(WMI_PROTOCOL_VERSION); 109addb44beSKalle Valo if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area, 110addb44beSKalle Valo sizeof(struct host_app_area))) 111bdcd8170SKalle Valo return -EIO; 112bdcd8170SKalle Valo 113bdcd8170SKalle Valo return 0; 114bdcd8170SKalle Valo } 115bdcd8170SKalle Valo 116bdcd8170SKalle Valo static inline void set_ac2_ep_map(struct ath6kl *ar, 117bdcd8170SKalle Valo u8 ac, 118bdcd8170SKalle Valo enum htc_endpoint_id ep) 119bdcd8170SKalle Valo { 120bdcd8170SKalle Valo ar->ac2ep_map[ac] = ep; 121bdcd8170SKalle Valo ar->ep2ac_map[ep] = ac; 122bdcd8170SKalle Valo } 123bdcd8170SKalle Valo 124bdcd8170SKalle Valo /* connect to a service */ 125bdcd8170SKalle Valo static int ath6kl_connectservice(struct ath6kl *ar, 126bdcd8170SKalle Valo struct htc_service_connect_req *con_req, 127bdcd8170SKalle Valo char *desc) 128bdcd8170SKalle Valo { 129bdcd8170SKalle Valo int status; 130bdcd8170SKalle Valo struct htc_service_connect_resp response; 131bdcd8170SKalle Valo 132bdcd8170SKalle Valo memset(&response, 0, sizeof(response)); 133bdcd8170SKalle Valo 134ad226ec2SKalle Valo status = ath6kl_htc_conn_service(ar->htc_target, con_req, &response); 135bdcd8170SKalle Valo if (status) { 136bdcd8170SKalle Valo ath6kl_err("failed to connect to %s service status:%d\n", 137bdcd8170SKalle Valo desc, status); 138bdcd8170SKalle Valo return status; 139bdcd8170SKalle Valo } 140bdcd8170SKalle Valo 141bdcd8170SKalle Valo switch (con_req->svc_id) { 142bdcd8170SKalle Valo case WMI_CONTROL_SVC: 143bdcd8170SKalle Valo if (test_bit(WMI_ENABLED, &ar->flag)) 144bdcd8170SKalle Valo ath6kl_wmi_set_control_ep(ar->wmi, response.endpoint); 145bdcd8170SKalle Valo ar->ctrl_ep = response.endpoint; 146bdcd8170SKalle Valo break; 147bdcd8170SKalle Valo case WMI_DATA_BE_SVC: 148bdcd8170SKalle Valo set_ac2_ep_map(ar, WMM_AC_BE, response.endpoint); 149bdcd8170SKalle Valo break; 150bdcd8170SKalle Valo case WMI_DATA_BK_SVC: 151bdcd8170SKalle Valo set_ac2_ep_map(ar, WMM_AC_BK, response.endpoint); 152bdcd8170SKalle Valo break; 153bdcd8170SKalle Valo case WMI_DATA_VI_SVC: 154bdcd8170SKalle Valo set_ac2_ep_map(ar, WMM_AC_VI, response.endpoint); 155bdcd8170SKalle Valo break; 156bdcd8170SKalle Valo case WMI_DATA_VO_SVC: 157bdcd8170SKalle Valo set_ac2_ep_map(ar, WMM_AC_VO, response.endpoint); 158bdcd8170SKalle Valo break; 159bdcd8170SKalle Valo default: 160bdcd8170SKalle Valo ath6kl_err("service id is not mapped %d\n", con_req->svc_id); 161bdcd8170SKalle Valo return -EINVAL; 162bdcd8170SKalle Valo } 163bdcd8170SKalle Valo 164bdcd8170SKalle Valo return 0; 165bdcd8170SKalle Valo } 166bdcd8170SKalle Valo 167bdcd8170SKalle Valo static int ath6kl_init_service_ep(struct ath6kl *ar) 168bdcd8170SKalle Valo { 169bdcd8170SKalle Valo struct htc_service_connect_req connect; 170bdcd8170SKalle Valo 171bdcd8170SKalle Valo memset(&connect, 0, sizeof(connect)); 172bdcd8170SKalle Valo 173bdcd8170SKalle Valo /* these fields are the same for all service endpoints */ 174bdcd8170SKalle Valo connect.ep_cb.rx = ath6kl_rx; 175bdcd8170SKalle Valo connect.ep_cb.rx_refill = ath6kl_rx_refill; 176bdcd8170SKalle Valo connect.ep_cb.tx_full = ath6kl_tx_queue_full; 177bdcd8170SKalle Valo 178bdcd8170SKalle Valo /* 179bdcd8170SKalle Valo * Set the max queue depth so that our ath6kl_tx_queue_full handler 180bdcd8170SKalle Valo * gets called. 181bdcd8170SKalle Valo */ 182bdcd8170SKalle Valo connect.max_txq_depth = MAX_DEFAULT_SEND_QUEUE_DEPTH; 183bdcd8170SKalle Valo connect.ep_cb.rx_refill_thresh = ATH6KL_MAX_RX_BUFFERS / 4; 184bdcd8170SKalle Valo if (!connect.ep_cb.rx_refill_thresh) 185bdcd8170SKalle Valo connect.ep_cb.rx_refill_thresh++; 186bdcd8170SKalle Valo 187bdcd8170SKalle Valo /* connect to control service */ 188bdcd8170SKalle Valo connect.svc_id = WMI_CONTROL_SVC; 189bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI CONTROL")) 190bdcd8170SKalle Valo return -EIO; 191bdcd8170SKalle Valo 192bdcd8170SKalle Valo connect.flags |= HTC_FLGS_TX_BNDL_PAD_EN; 193bdcd8170SKalle Valo 194bdcd8170SKalle Valo /* 195bdcd8170SKalle Valo * Limit the HTC message size on the send path, although e can 196bdcd8170SKalle Valo * receive A-MSDU frames of 4K, we will only send ethernet-sized 197bdcd8170SKalle Valo * (802.3) frames on the send path. 198bdcd8170SKalle Valo */ 199bdcd8170SKalle Valo connect.max_rxmsg_sz = WMI_MAX_TX_DATA_FRAME_LENGTH; 200bdcd8170SKalle Valo 201bdcd8170SKalle Valo /* 202bdcd8170SKalle Valo * To reduce the amount of committed memory for larger A_MSDU 203bdcd8170SKalle Valo * frames, use the recv-alloc threshold mechanism for larger 204bdcd8170SKalle Valo * packets. 205bdcd8170SKalle Valo */ 206bdcd8170SKalle Valo connect.ep_cb.rx_alloc_thresh = ATH6KL_BUFFER_SIZE; 207bdcd8170SKalle Valo connect.ep_cb.rx_allocthresh = ath6kl_alloc_amsdu_rxbuf; 208bdcd8170SKalle Valo 209bdcd8170SKalle Valo /* 210bdcd8170SKalle Valo * For the remaining data services set the connection flag to 211bdcd8170SKalle Valo * reduce dribbling, if configured to do so. 212bdcd8170SKalle Valo */ 213bdcd8170SKalle Valo connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB; 214bdcd8170SKalle Valo connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK; 215bdcd8170SKalle Valo connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF; 216bdcd8170SKalle Valo 217bdcd8170SKalle Valo connect.svc_id = WMI_DATA_BE_SVC; 218bdcd8170SKalle Valo 219bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI DATA BE")) 220bdcd8170SKalle Valo return -EIO; 221bdcd8170SKalle Valo 222bdcd8170SKalle Valo /* connect to back-ground map this to WMI LOW_PRI */ 223bdcd8170SKalle Valo connect.svc_id = WMI_DATA_BK_SVC; 224bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI DATA BK")) 225bdcd8170SKalle Valo return -EIO; 226bdcd8170SKalle Valo 227bdcd8170SKalle Valo /* connect to Video service, map this to to HI PRI */ 228bdcd8170SKalle Valo connect.svc_id = WMI_DATA_VI_SVC; 229bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI DATA VI")) 230bdcd8170SKalle Valo return -EIO; 231bdcd8170SKalle Valo 232bdcd8170SKalle Valo /* 233bdcd8170SKalle Valo * Connect to VO service, this is currently not mapped to a WMI 234bdcd8170SKalle Valo * priority stream due to historical reasons. WMI originally 235bdcd8170SKalle Valo * defined 3 priorities over 3 mailboxes We can change this when 236bdcd8170SKalle Valo * WMI is reworked so that priorities are not dependent on 237bdcd8170SKalle Valo * mailboxes. 238bdcd8170SKalle Valo */ 239bdcd8170SKalle Valo connect.svc_id = WMI_DATA_VO_SVC; 240bdcd8170SKalle Valo if (ath6kl_connectservice(ar, &connect, "WMI DATA VO")) 241bdcd8170SKalle Valo return -EIO; 242bdcd8170SKalle Valo 243bdcd8170SKalle Valo return 0; 244bdcd8170SKalle Valo } 245bdcd8170SKalle Valo 246e29f25f5SVasanthakumar Thiagarajan void ath6kl_init_control_info(struct ath6kl_vif *vif) 247bdcd8170SKalle Valo { 248e29f25f5SVasanthakumar Thiagarajan ath6kl_init_profile_info(vif); 2493450334fSVasanthakumar Thiagarajan vif->def_txkey_index = 0; 2506f2a73f9SVasanthakumar Thiagarajan memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); 251f74bac54SVasanthakumar Thiagarajan vif->ch_hint = 0; 252bdcd8170SKalle Valo } 253bdcd8170SKalle Valo 254bdcd8170SKalle Valo /* 255bdcd8170SKalle Valo * Set HTC/Mbox operational parameters, this can only be called when the 256bdcd8170SKalle Valo * target is in the BMI phase. 257bdcd8170SKalle Valo */ 258bdcd8170SKalle Valo static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, 259bdcd8170SKalle Valo u8 htc_ctrl_buf) 260bdcd8170SKalle Valo { 261bdcd8170SKalle Valo int status; 262bdcd8170SKalle Valo u32 blk_size; 263bdcd8170SKalle Valo 264bdcd8170SKalle Valo blk_size = ar->mbox_info.block_size; 265bdcd8170SKalle Valo 266bdcd8170SKalle Valo if (htc_ctrl_buf) 267bdcd8170SKalle Valo blk_size |= ((u32)htc_ctrl_buf) << 16; 268bdcd8170SKalle Valo 269bdcd8170SKalle Valo /* set the host interest area for the block size */ 270bdcd8170SKalle Valo status = ath6kl_bmi_write(ar, 271bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 272bdcd8170SKalle Valo HI_ITEM(hi_mbox_io_block_sz)), 273bdcd8170SKalle Valo (u8 *)&blk_size, 274bdcd8170SKalle Valo 4); 275bdcd8170SKalle Valo if (status) { 276bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for IO block size failed\n"); 277bdcd8170SKalle Valo goto out; 278bdcd8170SKalle Valo } 279bdcd8170SKalle Valo 280bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "block size set: %d (target addr:0x%X)\n", 281bdcd8170SKalle Valo blk_size, 282bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz))); 283bdcd8170SKalle Valo 284bdcd8170SKalle Valo if (mbox_isr_yield_val) { 285bdcd8170SKalle Valo /* set the host interest area for the mbox ISR yield limit */ 286bdcd8170SKalle Valo status = ath6kl_bmi_write(ar, 287bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 288bdcd8170SKalle Valo HI_ITEM(hi_mbox_isr_yield_limit)), 289bdcd8170SKalle Valo (u8 *)&mbox_isr_yield_val, 290bdcd8170SKalle Valo 4); 291bdcd8170SKalle Valo if (status) { 292bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for yield limit failed\n"); 293bdcd8170SKalle Valo goto out; 294bdcd8170SKalle Valo } 295bdcd8170SKalle Valo } 296bdcd8170SKalle Valo 297bdcd8170SKalle Valo out: 298bdcd8170SKalle Valo return status; 299bdcd8170SKalle Valo } 300bdcd8170SKalle Valo 301bdcd8170SKalle Valo #define REG_DUMP_COUNT_AR6003 60 302bdcd8170SKalle Valo #define REGISTER_DUMP_LEN_MAX 60 303bdcd8170SKalle Valo 304bdcd8170SKalle Valo static void ath6kl_dump_target_assert_info(struct ath6kl *ar) 305bdcd8170SKalle Valo { 306bdcd8170SKalle Valo u32 address; 307bdcd8170SKalle Valo u32 regdump_loc = 0; 308bdcd8170SKalle Valo int status; 309bdcd8170SKalle Valo u32 regdump_val[REGISTER_DUMP_LEN_MAX]; 310bdcd8170SKalle Valo u32 i; 311bdcd8170SKalle Valo 312bdcd8170SKalle Valo if (ar->target_type != TARGET_TYPE_AR6003) 313bdcd8170SKalle Valo return; 314bdcd8170SKalle Valo 315bdcd8170SKalle Valo /* the reg dump pointer is copied to the host interest area */ 316bdcd8170SKalle Valo address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); 31731024d99SKevin Fang address = TARG_VTOP(ar->target_type, address); 318bdcd8170SKalle Valo 319bdcd8170SKalle Valo /* read RAM location through diagnostic window */ 320addb44beSKalle Valo status = ath6kl_diag_read32(ar, address, ®dump_loc); 321bdcd8170SKalle Valo 322bdcd8170SKalle Valo if (status || !regdump_loc) { 323bdcd8170SKalle Valo ath6kl_err("failed to get ptr to register dump area\n"); 324bdcd8170SKalle Valo return; 325bdcd8170SKalle Valo } 326bdcd8170SKalle Valo 327bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n", 328bdcd8170SKalle Valo regdump_loc); 32931024d99SKevin Fang regdump_loc = TARG_VTOP(ar->target_type, regdump_loc); 330bdcd8170SKalle Valo 331bdcd8170SKalle Valo /* fetch register dump data */ 332addb44beSKalle Valo status = ath6kl_diag_read(ar, regdump_loc, (u8 *)®dump_val[0], 333addb44beSKalle Valo REG_DUMP_COUNT_AR6003 * (sizeof(u32))); 334bdcd8170SKalle Valo 335bdcd8170SKalle Valo if (status) { 336bdcd8170SKalle Valo ath6kl_err("failed to get register dump\n"); 337bdcd8170SKalle Valo return; 338bdcd8170SKalle Valo } 339bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "Register Dump:\n"); 340bdcd8170SKalle Valo 341bdcd8170SKalle Valo for (i = 0; i < REG_DUMP_COUNT_AR6003; i++) 342bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, " %d : 0x%8.8X\n", 343bdcd8170SKalle Valo i, regdump_val[i]); 344bdcd8170SKalle Valo 345bdcd8170SKalle Valo } 346bdcd8170SKalle Valo 347bdcd8170SKalle Valo void ath6kl_target_failure(struct ath6kl *ar) 348bdcd8170SKalle Valo { 349bdcd8170SKalle Valo ath6kl_err("target asserted\n"); 350bdcd8170SKalle Valo 351bdcd8170SKalle Valo /* try dumping target assertion information (if any) */ 352bdcd8170SKalle Valo ath6kl_dump_target_assert_info(ar); 353bdcd8170SKalle Valo 354bdcd8170SKalle Valo } 355bdcd8170SKalle Valo 3560ce59445SVasanthakumar Thiagarajan static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) 357bdcd8170SKalle Valo { 358bdcd8170SKalle Valo int status = 0; 3594dea08e0SJouni Malinen int ret; 360bdcd8170SKalle Valo 361bdcd8170SKalle Valo /* 362bdcd8170SKalle Valo * Configure the device for rx dot11 header rules. "0,0" are the 363bdcd8170SKalle Valo * default values. Required if checksum offload is needed. Set 364bdcd8170SKalle Valo * RxMetaVersion to 2. 365bdcd8170SKalle Valo */ 3660ce59445SVasanthakumar Thiagarajan if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx, 367bdcd8170SKalle Valo ar->rx_meta_ver, 0, 0)) { 368bdcd8170SKalle Valo ath6kl_err("unable to set the rx frame format\n"); 369bdcd8170SKalle Valo status = -EIO; 370bdcd8170SKalle Valo } 371bdcd8170SKalle Valo 372bdcd8170SKalle Valo if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) 3730ce59445SVasanthakumar Thiagarajan if ((ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1, 374bdcd8170SKalle Valo IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN)) != 0) { 375bdcd8170SKalle Valo ath6kl_err("unable to set power save fail event policy\n"); 376bdcd8170SKalle Valo status = -EIO; 377bdcd8170SKalle Valo } 378bdcd8170SKalle Valo 379bdcd8170SKalle Valo if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) 3800ce59445SVasanthakumar Thiagarajan if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0, 381bdcd8170SKalle Valo WMI_DONOT_IGNORE_BARKER_IN_ERP)) != 0) { 382bdcd8170SKalle Valo ath6kl_err("unable to set barker preamble policy\n"); 383bdcd8170SKalle Valo status = -EIO; 384bdcd8170SKalle Valo } 385bdcd8170SKalle Valo 3860ce59445SVasanthakumar Thiagarajan if (ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx, 387bdcd8170SKalle Valo WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) { 388bdcd8170SKalle Valo ath6kl_err("unable to set keep alive interval\n"); 389bdcd8170SKalle Valo status = -EIO; 390bdcd8170SKalle Valo } 391bdcd8170SKalle Valo 3920ce59445SVasanthakumar Thiagarajan if (ath6kl_wmi_disctimeout_cmd(ar->wmi, idx, 393bdcd8170SKalle Valo WLAN_CONFIG_DISCONNECT_TIMEOUT)) { 394bdcd8170SKalle Valo ath6kl_err("unable to set disconnect timeout\n"); 395bdcd8170SKalle Valo status = -EIO; 396bdcd8170SKalle Valo } 397bdcd8170SKalle Valo 398bdcd8170SKalle Valo if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) 3990ce59445SVasanthakumar Thiagarajan if (ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED)) { 400bdcd8170SKalle Valo ath6kl_err("unable to set txop bursting\n"); 401bdcd8170SKalle Valo status = -EIO; 402bdcd8170SKalle Valo } 403bdcd8170SKalle Valo 4040ce59445SVasanthakumar Thiagarajan /* 4050ce59445SVasanthakumar Thiagarajan * FIXME: Make sure p2p configurations are not applied to 4060ce59445SVasanthakumar Thiagarajan * non-p2p capable interfaces when multivif support is enabled. 4070ce59445SVasanthakumar Thiagarajan */ 4086bbc7c35SJouni Malinen if (ar->p2p) { 4090ce59445SVasanthakumar Thiagarajan ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx, 4106bbc7c35SJouni Malinen P2P_FLAG_CAPABILITIES_REQ | 4114dea08e0SJouni Malinen P2P_FLAG_MACADDR_REQ | 4124dea08e0SJouni Malinen P2P_FLAG_HMODEL_REQ); 4134dea08e0SJouni Malinen if (ret) { 4144dea08e0SJouni Malinen ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P " 4156bbc7c35SJouni Malinen "capabilities (%d) - assuming P2P not " 4166bbc7c35SJouni Malinen "supported\n", ret); 4176bbc7c35SJouni Malinen ar->p2p = 0; 4186bbc7c35SJouni Malinen } 4196bbc7c35SJouni Malinen } 4206bbc7c35SJouni Malinen 4210ce59445SVasanthakumar Thiagarajan /* 4220ce59445SVasanthakumar Thiagarajan * FIXME: Make sure p2p configurations are not applied to 4230ce59445SVasanthakumar Thiagarajan * non-p2p capable interfaces when multivif support is enabled. 4240ce59445SVasanthakumar Thiagarajan */ 4256bbc7c35SJouni Malinen if (ar->p2p) { 4266bbc7c35SJouni Malinen /* Enable Probe Request reporting for P2P */ 4270ce59445SVasanthakumar Thiagarajan ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true); 4286bbc7c35SJouni Malinen if (ret) { 4296bbc7c35SJouni Malinen ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe " 4306bbc7c35SJouni Malinen "Request reporting (%d)\n", ret); 4316bbc7c35SJouni Malinen } 4324dea08e0SJouni Malinen } 4334dea08e0SJouni Malinen 434bdcd8170SKalle Valo return status; 435bdcd8170SKalle Valo } 436bdcd8170SKalle Valo 437bdcd8170SKalle Valo int ath6kl_configure_target(struct ath6kl *ar) 438bdcd8170SKalle Valo { 439bdcd8170SKalle Valo u32 param, ram_reserved_size; 4403226f68aSVasanthakumar Thiagarajan u8 fw_iftype, fw_mode = 0, fw_submode = 0; 4417b85832dSVasanthakumar Thiagarajan int i; 442bdcd8170SKalle Valo 4437b85832dSVasanthakumar Thiagarajan /* 4447b85832dSVasanthakumar Thiagarajan * Note: Even though the firmware interface type is 4457b85832dSVasanthakumar Thiagarajan * chosen as BSS_STA for all three interfaces, can 4467b85832dSVasanthakumar Thiagarajan * be configured to IBSS/AP as long as the fw submode 4477b85832dSVasanthakumar Thiagarajan * remains normal mode (0 - AP, STA and IBSS). But 4487b85832dSVasanthakumar Thiagarajan * due to an target assert in firmware only one interface is 4497b85832dSVasanthakumar Thiagarajan * configured for now. 4507b85832dSVasanthakumar Thiagarajan */ 451dd3751f7SVasanthakumar Thiagarajan fw_iftype = HI_OPTION_FW_MODE_BSS_STA; 452bdcd8170SKalle Valo 4537b85832dSVasanthakumar Thiagarajan for (i = 0; i < MAX_NUM_VIF; i++) 4547b85832dSVasanthakumar Thiagarajan fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); 4557b85832dSVasanthakumar Thiagarajan 4567b85832dSVasanthakumar Thiagarajan /* 4573226f68aSVasanthakumar Thiagarajan * By default, submodes : 4583226f68aSVasanthakumar Thiagarajan * vif[0] - AP/STA/IBSS 4597b85832dSVasanthakumar Thiagarajan * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" 4607b85832dSVasanthakumar Thiagarajan * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" 4617b85832dSVasanthakumar Thiagarajan */ 4623226f68aSVasanthakumar Thiagarajan 4633226f68aSVasanthakumar Thiagarajan for (i = 0; i < ar->max_norm_iface; i++) 4643226f68aSVasanthakumar Thiagarajan fw_submode |= HI_OPTION_FW_SUBMODE_NONE << 4653226f68aSVasanthakumar Thiagarajan (i * HI_OPTION_FW_SUBMODE_BITS); 4663226f68aSVasanthakumar Thiagarajan 4673226f68aSVasanthakumar Thiagarajan for (i = ar->max_norm_iface; i < MAX_NUM_VIF; i++) 4683226f68aSVasanthakumar Thiagarajan fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << 4693226f68aSVasanthakumar Thiagarajan (i * HI_OPTION_FW_SUBMODE_BITS); 4707b85832dSVasanthakumar Thiagarajan 4717b85832dSVasanthakumar Thiagarajan /* 4727b85832dSVasanthakumar Thiagarajan * FIXME: This needs to be removed once the multivif 4737b85832dSVasanthakumar Thiagarajan * support is enabled. 4747b85832dSVasanthakumar Thiagarajan */ 4757b85832dSVasanthakumar Thiagarajan if (ar->p2p) 4767b85832dSVasanthakumar Thiagarajan fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; 4777b85832dSVasanthakumar Thiagarajan 478bdcd8170SKalle Valo param = HTC_PROTOCOL_VERSION; 479bdcd8170SKalle Valo if (ath6kl_bmi_write(ar, 480bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 481bdcd8170SKalle Valo HI_ITEM(hi_app_host_interest)), 482bdcd8170SKalle Valo (u8 *)¶m, 4) != 0) { 483bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for htc version failed\n"); 484bdcd8170SKalle Valo return -EIO; 485bdcd8170SKalle Valo } 486bdcd8170SKalle Valo 487bdcd8170SKalle Valo /* set the firmware mode to STA/IBSS/AP */ 488bdcd8170SKalle Valo param = 0; 489bdcd8170SKalle Valo 490bdcd8170SKalle Valo if (ath6kl_bmi_read(ar, 491bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 492bdcd8170SKalle Valo HI_ITEM(hi_option_flag)), 493bdcd8170SKalle Valo (u8 *)¶m, 4) != 0) { 494bdcd8170SKalle Valo ath6kl_err("bmi_read_memory for setting fwmode failed\n"); 495bdcd8170SKalle Valo return -EIO; 496bdcd8170SKalle Valo } 497bdcd8170SKalle Valo 4987b85832dSVasanthakumar Thiagarajan param |= (MAX_NUM_VIF << HI_OPTION_NUM_DEV_SHIFT); 4997b85832dSVasanthakumar Thiagarajan param |= fw_mode << HI_OPTION_FW_MODE_SHIFT; 5007b85832dSVasanthakumar Thiagarajan param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT; 5017b85832dSVasanthakumar Thiagarajan 502bdcd8170SKalle Valo param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); 503bdcd8170SKalle Valo param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); 504bdcd8170SKalle Valo 505bdcd8170SKalle Valo if (ath6kl_bmi_write(ar, 506bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 507bdcd8170SKalle Valo HI_ITEM(hi_option_flag)), 508bdcd8170SKalle Valo (u8 *)¶m, 509bdcd8170SKalle Valo 4) != 0) { 510bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for setting fwmode failed\n"); 511bdcd8170SKalle Valo return -EIO; 512bdcd8170SKalle Valo } 513bdcd8170SKalle Valo 514bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "firmware mode set\n"); 515bdcd8170SKalle Valo 516bdcd8170SKalle Valo /* 517bdcd8170SKalle Valo * Hardcode the address use for the extended board data 518bdcd8170SKalle Valo * Ideally this should be pre-allocate by the OS at boot time 519bdcd8170SKalle Valo * But since it is a new feature and board data is loaded 520bdcd8170SKalle Valo * at init time, we have to workaround this from host. 521bdcd8170SKalle Valo * It is difficult to patch the firmware boot code, 522bdcd8170SKalle Valo * but possible in theory. 523bdcd8170SKalle Valo */ 524bdcd8170SKalle Valo 525991b27eaSKalle Valo param = ar->hw.board_ext_data_addr; 526991b27eaSKalle Valo ram_reserved_size = ar->hw.reserved_ram_size; 527bdcd8170SKalle Valo 528991b27eaSKalle Valo if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, 529bdcd8170SKalle Valo HI_ITEM(hi_board_ext_data)), 530bdcd8170SKalle Valo (u8 *)¶m, 4) != 0) { 531bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); 532bdcd8170SKalle Valo return -EIO; 533bdcd8170SKalle Valo } 534991b27eaSKalle Valo 535991b27eaSKalle Valo if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, 536bdcd8170SKalle Valo HI_ITEM(hi_end_ram_reserve_sz)), 537bdcd8170SKalle Valo (u8 *)&ram_reserved_size, 4) != 0) { 538bdcd8170SKalle Valo ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); 539bdcd8170SKalle Valo return -EIO; 540bdcd8170SKalle Valo } 541bdcd8170SKalle Valo 542bdcd8170SKalle Valo /* set the block size for the target */ 543bdcd8170SKalle Valo if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0)) 544bdcd8170SKalle Valo /* use default number of control buffers */ 545bdcd8170SKalle Valo return -EIO; 546bdcd8170SKalle Valo 547bdcd8170SKalle Valo return 0; 548bdcd8170SKalle Valo } 549bdcd8170SKalle Valo 5508dafb70eSVasanthakumar Thiagarajan void ath6kl_core_free(struct ath6kl *ar) 551bdcd8170SKalle Valo { 5528dafb70eSVasanthakumar Thiagarajan wiphy_free(ar->wiphy); 553bdcd8170SKalle Valo } 554bdcd8170SKalle Valo 5556db8fa53SVasanthakumar Thiagarajan void ath6kl_core_cleanup(struct ath6kl *ar) 556bdcd8170SKalle Valo { 557b2e75698SKalle Valo ath6kl_hif_power_off(ar); 558b2e75698SKalle Valo 5596db8fa53SVasanthakumar Thiagarajan destroy_workqueue(ar->ath6kl_wq); 560bdcd8170SKalle Valo 5616db8fa53SVasanthakumar Thiagarajan if (ar->htc_target) 5626db8fa53SVasanthakumar Thiagarajan ath6kl_htc_cleanup(ar->htc_target); 5636db8fa53SVasanthakumar Thiagarajan 5646db8fa53SVasanthakumar Thiagarajan ath6kl_cookie_cleanup(ar); 5656db8fa53SVasanthakumar Thiagarajan 5666db8fa53SVasanthakumar Thiagarajan ath6kl_cleanup_amsdu_rxbufs(ar); 5676db8fa53SVasanthakumar Thiagarajan 5686db8fa53SVasanthakumar Thiagarajan ath6kl_bmi_cleanup(ar); 5696db8fa53SVasanthakumar Thiagarajan 5706db8fa53SVasanthakumar Thiagarajan ath6kl_debug_cleanup(ar); 5716db8fa53SVasanthakumar Thiagarajan 5726db8fa53SVasanthakumar Thiagarajan kfree(ar->fw_board); 5736db8fa53SVasanthakumar Thiagarajan kfree(ar->fw_otp); 5746db8fa53SVasanthakumar Thiagarajan kfree(ar->fw); 5756db8fa53SVasanthakumar Thiagarajan kfree(ar->fw_patch); 5766db8fa53SVasanthakumar Thiagarajan 5776db8fa53SVasanthakumar Thiagarajan ath6kl_deinit_ieee80211_hw(ar); 578bdcd8170SKalle Valo } 579bdcd8170SKalle Valo 580bdcd8170SKalle Valo /* firmware upload */ 581bdcd8170SKalle Valo static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, 582bdcd8170SKalle Valo u8 **fw, size_t *fw_len) 583bdcd8170SKalle Valo { 584bdcd8170SKalle Valo const struct firmware *fw_entry; 585bdcd8170SKalle Valo int ret; 586bdcd8170SKalle Valo 587bdcd8170SKalle Valo ret = request_firmware(&fw_entry, filename, ar->dev); 588bdcd8170SKalle Valo if (ret) 589bdcd8170SKalle Valo return ret; 590bdcd8170SKalle Valo 591bdcd8170SKalle Valo *fw_len = fw_entry->size; 592bdcd8170SKalle Valo *fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); 593bdcd8170SKalle Valo 594bdcd8170SKalle Valo if (*fw == NULL) 595bdcd8170SKalle Valo ret = -ENOMEM; 596bdcd8170SKalle Valo 597bdcd8170SKalle Valo release_firmware(fw_entry); 598bdcd8170SKalle Valo 599bdcd8170SKalle Valo return ret; 600bdcd8170SKalle Valo } 601bdcd8170SKalle Valo 60292ecbff4SSam Leffler #ifdef CONFIG_OF 60392ecbff4SSam Leffler static const char *get_target_ver_dir(const struct ath6kl *ar) 60492ecbff4SSam Leffler { 60592ecbff4SSam Leffler switch (ar->version.target_ver) { 60692ecbff4SSam Leffler case AR6003_REV1_VERSION: 60792ecbff4SSam Leffler return "ath6k/AR6003/hw1.0"; 60892ecbff4SSam Leffler case AR6003_REV2_VERSION: 60992ecbff4SSam Leffler return "ath6k/AR6003/hw2.0"; 61092ecbff4SSam Leffler case AR6003_REV3_VERSION: 61192ecbff4SSam Leffler return "ath6k/AR6003/hw2.1.1"; 61292ecbff4SSam Leffler } 61392ecbff4SSam Leffler ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__, 61492ecbff4SSam Leffler ar->version.target_ver); 61592ecbff4SSam Leffler return NULL; 61692ecbff4SSam Leffler } 61792ecbff4SSam Leffler 61892ecbff4SSam Leffler /* 61992ecbff4SSam Leffler * Check the device tree for a board-id and use it to construct 62092ecbff4SSam Leffler * the pathname to the firmware file. Used (for now) to find a 62192ecbff4SSam Leffler * fallback to the "bdata.bin" file--typically a symlink to the 62292ecbff4SSam Leffler * appropriate board-specific file. 62392ecbff4SSam Leffler */ 62492ecbff4SSam Leffler static bool check_device_tree(struct ath6kl *ar) 62592ecbff4SSam Leffler { 62692ecbff4SSam Leffler static const char *board_id_prop = "atheros,board-id"; 62792ecbff4SSam Leffler struct device_node *node; 62892ecbff4SSam Leffler char board_filename[64]; 62992ecbff4SSam Leffler const char *board_id; 63092ecbff4SSam Leffler int ret; 63192ecbff4SSam Leffler 63292ecbff4SSam Leffler for_each_compatible_node(node, NULL, "atheros,ath6kl") { 63392ecbff4SSam Leffler board_id = of_get_property(node, board_id_prop, NULL); 63492ecbff4SSam Leffler if (board_id == NULL) { 63592ecbff4SSam Leffler ath6kl_warn("No \"%s\" property on %s node.\n", 63692ecbff4SSam Leffler board_id_prop, node->name); 63792ecbff4SSam Leffler continue; 63892ecbff4SSam Leffler } 63992ecbff4SSam Leffler snprintf(board_filename, sizeof(board_filename), 64092ecbff4SSam Leffler "%s/bdata.%s.bin", get_target_ver_dir(ar), board_id); 64192ecbff4SSam Leffler 64292ecbff4SSam Leffler ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board, 64392ecbff4SSam Leffler &ar->fw_board_len); 64492ecbff4SSam Leffler if (ret) { 64592ecbff4SSam Leffler ath6kl_err("Failed to get DT board file %s: %d\n", 64692ecbff4SSam Leffler board_filename, ret); 64792ecbff4SSam Leffler continue; 64892ecbff4SSam Leffler } 64992ecbff4SSam Leffler return true; 65092ecbff4SSam Leffler } 65192ecbff4SSam Leffler return false; 65292ecbff4SSam Leffler } 65392ecbff4SSam Leffler #else 65492ecbff4SSam Leffler static bool check_device_tree(struct ath6kl *ar) 65592ecbff4SSam Leffler { 65692ecbff4SSam Leffler return false; 65792ecbff4SSam Leffler } 65892ecbff4SSam Leffler #endif /* CONFIG_OF */ 65992ecbff4SSam Leffler 660bdcd8170SKalle Valo static int ath6kl_fetch_board_file(struct ath6kl *ar) 661bdcd8170SKalle Valo { 662bdcd8170SKalle Valo const char *filename; 663bdcd8170SKalle Valo int ret; 664bdcd8170SKalle Valo 665772c31eeSKalle Valo if (ar->fw_board != NULL) 666772c31eeSKalle Valo return 0; 667772c31eeSKalle Valo 668bdcd8170SKalle Valo switch (ar->version.target_ver) { 669bdcd8170SKalle Valo case AR6003_REV2_VERSION: 670bdcd8170SKalle Valo filename = AR6003_REV2_BOARD_DATA_FILE; 671bdcd8170SKalle Valo break; 67231024d99SKevin Fang case AR6004_REV1_VERSION: 67331024d99SKevin Fang filename = AR6004_REV1_BOARD_DATA_FILE; 67431024d99SKevin Fang break; 675bdcd8170SKalle Valo default: 676bdcd8170SKalle Valo filename = AR6003_REV3_BOARD_DATA_FILE; 677bdcd8170SKalle Valo break; 678bdcd8170SKalle Valo } 679bdcd8170SKalle Valo 680bdcd8170SKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw_board, 681bdcd8170SKalle Valo &ar->fw_board_len); 682bdcd8170SKalle Valo if (ret == 0) { 683bdcd8170SKalle Valo /* managed to get proper board file */ 684bdcd8170SKalle Valo return 0; 685bdcd8170SKalle Valo } 686bdcd8170SKalle Valo 68792ecbff4SSam Leffler if (check_device_tree(ar)) { 68892ecbff4SSam Leffler /* got board file from device tree */ 68992ecbff4SSam Leffler return 0; 69092ecbff4SSam Leffler } 69192ecbff4SSam Leffler 692bdcd8170SKalle Valo /* there was no proper board file, try to use default instead */ 693bdcd8170SKalle Valo ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n", 694bdcd8170SKalle Valo filename, ret); 695bdcd8170SKalle Valo 696bdcd8170SKalle Valo switch (ar->version.target_ver) { 697bdcd8170SKalle Valo case AR6003_REV2_VERSION: 698bdcd8170SKalle Valo filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE; 699bdcd8170SKalle Valo break; 70031024d99SKevin Fang case AR6004_REV1_VERSION: 70131024d99SKevin Fang filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE; 70231024d99SKevin Fang break; 703bdcd8170SKalle Valo default: 704bdcd8170SKalle Valo filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE; 705bdcd8170SKalle Valo break; 706bdcd8170SKalle Valo } 707bdcd8170SKalle Valo 708bdcd8170SKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw_board, 709bdcd8170SKalle Valo &ar->fw_board_len); 710bdcd8170SKalle Valo if (ret) { 711bdcd8170SKalle Valo ath6kl_err("Failed to get default board file %s: %d\n", 712bdcd8170SKalle Valo filename, ret); 713bdcd8170SKalle Valo return ret; 714bdcd8170SKalle Valo } 715bdcd8170SKalle Valo 716bdcd8170SKalle Valo ath6kl_warn("WARNING! No proper board file was not found, instead using a default board file.\n"); 717bdcd8170SKalle Valo ath6kl_warn("Most likely your hardware won't work as specified. Install correct board file!\n"); 718bdcd8170SKalle Valo 719bdcd8170SKalle Valo return 0; 720bdcd8170SKalle Valo } 721bdcd8170SKalle Valo 722772c31eeSKalle Valo static int ath6kl_fetch_otp_file(struct ath6kl *ar) 723772c31eeSKalle Valo { 724772c31eeSKalle Valo const char *filename; 725772c31eeSKalle Valo int ret; 726772c31eeSKalle Valo 727772c31eeSKalle Valo if (ar->fw_otp != NULL) 728772c31eeSKalle Valo return 0; 729772c31eeSKalle Valo 730772c31eeSKalle Valo switch (ar->version.target_ver) { 731772c31eeSKalle Valo case AR6003_REV2_VERSION: 732772c31eeSKalle Valo filename = AR6003_REV2_OTP_FILE; 733772c31eeSKalle Valo break; 734772c31eeSKalle Valo case AR6004_REV1_VERSION: 735772c31eeSKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n"); 736772c31eeSKalle Valo return 0; 737772c31eeSKalle Valo break; 738772c31eeSKalle Valo default: 739772c31eeSKalle Valo filename = AR6003_REV3_OTP_FILE; 740772c31eeSKalle Valo break; 741772c31eeSKalle Valo } 742772c31eeSKalle Valo 743772c31eeSKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, 744772c31eeSKalle Valo &ar->fw_otp_len); 745772c31eeSKalle Valo if (ret) { 746772c31eeSKalle Valo ath6kl_err("Failed to get OTP file %s: %d\n", 747772c31eeSKalle Valo filename, ret); 748772c31eeSKalle Valo return ret; 749772c31eeSKalle Valo } 750772c31eeSKalle Valo 751772c31eeSKalle Valo return 0; 752772c31eeSKalle Valo } 753772c31eeSKalle Valo 754772c31eeSKalle Valo static int ath6kl_fetch_fw_file(struct ath6kl *ar) 755772c31eeSKalle Valo { 756772c31eeSKalle Valo const char *filename; 757772c31eeSKalle Valo int ret; 758772c31eeSKalle Valo 759772c31eeSKalle Valo if (ar->fw != NULL) 760772c31eeSKalle Valo return 0; 761772c31eeSKalle Valo 762772c31eeSKalle Valo if (testmode) { 763772c31eeSKalle Valo switch (ar->version.target_ver) { 764772c31eeSKalle Valo case AR6003_REV2_VERSION: 765772c31eeSKalle Valo filename = AR6003_REV2_TCMD_FIRMWARE_FILE; 766772c31eeSKalle Valo break; 767772c31eeSKalle Valo case AR6003_REV3_VERSION: 768772c31eeSKalle Valo filename = AR6003_REV3_TCMD_FIRMWARE_FILE; 769772c31eeSKalle Valo break; 770772c31eeSKalle Valo case AR6004_REV1_VERSION: 771772c31eeSKalle Valo ath6kl_warn("testmode not supported with ar6004\n"); 772772c31eeSKalle Valo return -EOPNOTSUPP; 773772c31eeSKalle Valo default: 774772c31eeSKalle Valo ath6kl_warn("unknown target version: 0x%x\n", 775772c31eeSKalle Valo ar->version.target_ver); 776772c31eeSKalle Valo return -EINVAL; 777772c31eeSKalle Valo } 778772c31eeSKalle Valo 779772c31eeSKalle Valo set_bit(TESTMODE, &ar->flag); 780772c31eeSKalle Valo 781772c31eeSKalle Valo goto get_fw; 782772c31eeSKalle Valo } 783772c31eeSKalle Valo 784772c31eeSKalle Valo switch (ar->version.target_ver) { 785772c31eeSKalle Valo case AR6003_REV2_VERSION: 786772c31eeSKalle Valo filename = AR6003_REV2_FIRMWARE_FILE; 787772c31eeSKalle Valo break; 788772c31eeSKalle Valo case AR6004_REV1_VERSION: 789772c31eeSKalle Valo filename = AR6004_REV1_FIRMWARE_FILE; 790772c31eeSKalle Valo break; 791772c31eeSKalle Valo default: 792772c31eeSKalle Valo filename = AR6003_REV3_FIRMWARE_FILE; 793772c31eeSKalle Valo break; 794772c31eeSKalle Valo } 795772c31eeSKalle Valo 796772c31eeSKalle Valo get_fw: 797772c31eeSKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); 798772c31eeSKalle Valo if (ret) { 799772c31eeSKalle Valo ath6kl_err("Failed to get firmware file %s: %d\n", 800772c31eeSKalle Valo filename, ret); 801772c31eeSKalle Valo return ret; 802772c31eeSKalle Valo } 803772c31eeSKalle Valo 804772c31eeSKalle Valo return 0; 805772c31eeSKalle Valo } 806772c31eeSKalle Valo 807772c31eeSKalle Valo static int ath6kl_fetch_patch_file(struct ath6kl *ar) 808772c31eeSKalle Valo { 809772c31eeSKalle Valo const char *filename; 810772c31eeSKalle Valo int ret; 811772c31eeSKalle Valo 812772c31eeSKalle Valo switch (ar->version.target_ver) { 813772c31eeSKalle Valo case AR6003_REV2_VERSION: 814772c31eeSKalle Valo filename = AR6003_REV2_PATCH_FILE; 815772c31eeSKalle Valo break; 816772c31eeSKalle Valo case AR6004_REV1_VERSION: 817772c31eeSKalle Valo /* FIXME: implement for AR6004 */ 818772c31eeSKalle Valo return 0; 819772c31eeSKalle Valo break; 820772c31eeSKalle Valo default: 821772c31eeSKalle Valo filename = AR6003_REV3_PATCH_FILE; 822772c31eeSKalle Valo break; 823772c31eeSKalle Valo } 824772c31eeSKalle Valo 825772c31eeSKalle Valo if (ar->fw_patch == NULL) { 826772c31eeSKalle Valo ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, 827772c31eeSKalle Valo &ar->fw_patch_len); 828772c31eeSKalle Valo if (ret) { 829772c31eeSKalle Valo ath6kl_err("Failed to get patch file %s: %d\n", 830772c31eeSKalle Valo filename, ret); 831772c31eeSKalle Valo return ret; 832772c31eeSKalle Valo } 833772c31eeSKalle Valo } 834772c31eeSKalle Valo 835772c31eeSKalle Valo return 0; 836772c31eeSKalle Valo } 837772c31eeSKalle Valo 83850d41234SKalle Valo static int ath6kl_fetch_fw_api1(struct ath6kl *ar) 839772c31eeSKalle Valo { 840772c31eeSKalle Valo int ret; 841772c31eeSKalle Valo 842772c31eeSKalle Valo ret = ath6kl_fetch_otp_file(ar); 843772c31eeSKalle Valo if (ret) 844772c31eeSKalle Valo return ret; 845772c31eeSKalle Valo 846772c31eeSKalle Valo ret = ath6kl_fetch_fw_file(ar); 847772c31eeSKalle Valo if (ret) 848772c31eeSKalle Valo return ret; 849772c31eeSKalle Valo 850772c31eeSKalle Valo ret = ath6kl_fetch_patch_file(ar); 851772c31eeSKalle Valo if (ret) 852772c31eeSKalle Valo return ret; 853772c31eeSKalle Valo 854772c31eeSKalle Valo return 0; 855772c31eeSKalle Valo } 856bdcd8170SKalle Valo 85750d41234SKalle Valo static int ath6kl_fetch_fw_api2(struct ath6kl *ar) 85850d41234SKalle Valo { 85950d41234SKalle Valo size_t magic_len, len, ie_len; 86050d41234SKalle Valo const struct firmware *fw; 86150d41234SKalle Valo struct ath6kl_fw_ie *hdr; 86250d41234SKalle Valo const char *filename; 86350d41234SKalle Valo const u8 *data; 86497e0496dSKalle Valo int ret, ie_id, i, index, bit; 8658a137480SKalle Valo __le32 *val; 86650d41234SKalle Valo 86750d41234SKalle Valo switch (ar->version.target_ver) { 86850d41234SKalle Valo case AR6003_REV2_VERSION: 86950d41234SKalle Valo filename = AR6003_REV2_FIRMWARE_2_FILE; 87050d41234SKalle Valo break; 87150d41234SKalle Valo case AR6003_REV3_VERSION: 87250d41234SKalle Valo filename = AR6003_REV3_FIRMWARE_2_FILE; 87350d41234SKalle Valo break; 87450d41234SKalle Valo case AR6004_REV1_VERSION: 87550d41234SKalle Valo filename = AR6004_REV1_FIRMWARE_2_FILE; 87650d41234SKalle Valo break; 87750d41234SKalle Valo default: 87850d41234SKalle Valo return -EOPNOTSUPP; 87950d41234SKalle Valo } 88050d41234SKalle Valo 88150d41234SKalle Valo ret = request_firmware(&fw, filename, ar->dev); 88250d41234SKalle Valo if (ret) 88350d41234SKalle Valo return ret; 88450d41234SKalle Valo 88550d41234SKalle Valo data = fw->data; 88650d41234SKalle Valo len = fw->size; 88750d41234SKalle Valo 88850d41234SKalle Valo /* magic also includes the null byte, check that as well */ 88950d41234SKalle Valo magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1; 89050d41234SKalle Valo 89150d41234SKalle Valo if (len < magic_len) { 89250d41234SKalle Valo ret = -EINVAL; 89350d41234SKalle Valo goto out; 89450d41234SKalle Valo } 89550d41234SKalle Valo 89650d41234SKalle Valo if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) { 89750d41234SKalle Valo ret = -EINVAL; 89850d41234SKalle Valo goto out; 89950d41234SKalle Valo } 90050d41234SKalle Valo 90150d41234SKalle Valo len -= magic_len; 90250d41234SKalle Valo data += magic_len; 90350d41234SKalle Valo 90450d41234SKalle Valo /* loop elements */ 90550d41234SKalle Valo while (len > sizeof(struct ath6kl_fw_ie)) { 90650d41234SKalle Valo /* hdr is unaligned! */ 90750d41234SKalle Valo hdr = (struct ath6kl_fw_ie *) data; 90850d41234SKalle Valo 90950d41234SKalle Valo ie_id = le32_to_cpup(&hdr->id); 91050d41234SKalle Valo ie_len = le32_to_cpup(&hdr->len); 91150d41234SKalle Valo 91250d41234SKalle Valo len -= sizeof(*hdr); 91350d41234SKalle Valo data += sizeof(*hdr); 91450d41234SKalle Valo 91550d41234SKalle Valo if (len < ie_len) { 91650d41234SKalle Valo ret = -EINVAL; 91750d41234SKalle Valo goto out; 91850d41234SKalle Valo } 91950d41234SKalle Valo 92050d41234SKalle Valo switch (ie_id) { 92150d41234SKalle Valo case ATH6KL_FW_IE_OTP_IMAGE: 922ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n", 9236bc36431SKalle Valo ie_len); 9246bc36431SKalle Valo 92550d41234SKalle Valo ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); 92650d41234SKalle Valo 92750d41234SKalle Valo if (ar->fw_otp == NULL) { 92850d41234SKalle Valo ret = -ENOMEM; 92950d41234SKalle Valo goto out; 93050d41234SKalle Valo } 93150d41234SKalle Valo 93250d41234SKalle Valo ar->fw_otp_len = ie_len; 93350d41234SKalle Valo break; 93450d41234SKalle Valo case ATH6KL_FW_IE_FW_IMAGE: 935ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n", 9366bc36431SKalle Valo ie_len); 9376bc36431SKalle Valo 93850d41234SKalle Valo ar->fw = kmemdup(data, ie_len, GFP_KERNEL); 93950d41234SKalle Valo 94050d41234SKalle Valo if (ar->fw == NULL) { 94150d41234SKalle Valo ret = -ENOMEM; 94250d41234SKalle Valo goto out; 94350d41234SKalle Valo } 94450d41234SKalle Valo 94550d41234SKalle Valo ar->fw_len = ie_len; 94650d41234SKalle Valo break; 94750d41234SKalle Valo case ATH6KL_FW_IE_PATCH_IMAGE: 948ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n", 9496bc36431SKalle Valo ie_len); 9506bc36431SKalle Valo 95150d41234SKalle Valo ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); 95250d41234SKalle Valo 95350d41234SKalle Valo if (ar->fw_patch == NULL) { 95450d41234SKalle Valo ret = -ENOMEM; 95550d41234SKalle Valo goto out; 95650d41234SKalle Valo } 95750d41234SKalle Valo 95850d41234SKalle Valo ar->fw_patch_len = ie_len; 95950d41234SKalle Valo break; 9608a137480SKalle Valo case ATH6KL_FW_IE_RESERVED_RAM_SIZE: 9618a137480SKalle Valo val = (__le32 *) data; 9628a137480SKalle Valo ar->hw.reserved_ram_size = le32_to_cpup(val); 9636bc36431SKalle Valo 9646bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 9656bc36431SKalle Valo "found reserved ram size ie 0x%d\n", 9666bc36431SKalle Valo ar->hw.reserved_ram_size); 9678a137480SKalle Valo break; 96897e0496dSKalle Valo case ATH6KL_FW_IE_CAPABILITIES: 9696bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 970ef548626SKalle Valo "found firmware capabilities ie (%zd B)\n", 9716bc36431SKalle Valo ie_len); 9726bc36431SKalle Valo 97397e0496dSKalle Valo for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { 97497e0496dSKalle Valo index = ALIGN(i, 8) / 8; 97597e0496dSKalle Valo bit = i % 8; 97697e0496dSKalle Valo 97797e0496dSKalle Valo if (data[index] & (1 << bit)) 97897e0496dSKalle Valo __set_bit(i, ar->fw_capabilities); 97997e0496dSKalle Valo } 9806bc36431SKalle Valo 9816bc36431SKalle Valo ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "", 9826bc36431SKalle Valo ar->fw_capabilities, 9836bc36431SKalle Valo sizeof(ar->fw_capabilities)); 98497e0496dSKalle Valo break; 9851b4304daSKalle Valo case ATH6KL_FW_IE_PATCH_ADDR: 9861b4304daSKalle Valo if (ie_len != sizeof(*val)) 9871b4304daSKalle Valo break; 9881b4304daSKalle Valo 9891b4304daSKalle Valo val = (__le32 *) data; 9901b4304daSKalle Valo ar->hw.dataset_patch_addr = le32_to_cpup(val); 9916bc36431SKalle Valo 9926bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 9936bc36431SKalle Valo "found patch address ie 0x%d\n", 9946bc36431SKalle Valo ar->hw.dataset_patch_addr); 9951b4304daSKalle Valo break; 99650d41234SKalle Valo default: 9976bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n", 99850d41234SKalle Valo le32_to_cpup(&hdr->id)); 99950d41234SKalle Valo break; 100050d41234SKalle Valo } 100150d41234SKalle Valo 100250d41234SKalle Valo len -= ie_len; 100350d41234SKalle Valo data += ie_len; 100450d41234SKalle Valo }; 100550d41234SKalle Valo 100650d41234SKalle Valo ret = 0; 100750d41234SKalle Valo out: 100850d41234SKalle Valo release_firmware(fw); 100950d41234SKalle Valo 101050d41234SKalle Valo return ret; 101150d41234SKalle Valo } 101250d41234SKalle Valo 101350d41234SKalle Valo static int ath6kl_fetch_firmwares(struct ath6kl *ar) 101450d41234SKalle Valo { 101550d41234SKalle Valo int ret; 101650d41234SKalle Valo 101750d41234SKalle Valo ret = ath6kl_fetch_board_file(ar); 101850d41234SKalle Valo if (ret) 101950d41234SKalle Valo return ret; 102050d41234SKalle Valo 102150d41234SKalle Valo ret = ath6kl_fetch_fw_api2(ar); 10226bc36431SKalle Valo if (ret == 0) { 10236bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n"); 102450d41234SKalle Valo return 0; 10256bc36431SKalle Valo } 102650d41234SKalle Valo 102750d41234SKalle Valo ret = ath6kl_fetch_fw_api1(ar); 102850d41234SKalle Valo if (ret) 102950d41234SKalle Valo return ret; 103050d41234SKalle Valo 10316bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n"); 10326bc36431SKalle Valo 103350d41234SKalle Valo return 0; 103450d41234SKalle Valo } 103550d41234SKalle Valo 1036bdcd8170SKalle Valo static int ath6kl_upload_board_file(struct ath6kl *ar) 1037bdcd8170SKalle Valo { 1038bdcd8170SKalle Valo u32 board_address, board_ext_address, param; 103931024d99SKevin Fang u32 board_data_size, board_ext_data_size; 1040bdcd8170SKalle Valo int ret; 1041bdcd8170SKalle Valo 1042772c31eeSKalle Valo if (WARN_ON(ar->fw_board == NULL)) 1043772c31eeSKalle Valo return -ENOENT; 1044bdcd8170SKalle Valo 104531024d99SKevin Fang /* 104631024d99SKevin Fang * Determine where in Target RAM to write Board Data. 104731024d99SKevin Fang * For AR6004, host determine Target RAM address for 104831024d99SKevin Fang * writing board data. 104931024d99SKevin Fang */ 105031024d99SKevin Fang if (ar->target_type == TARGET_TYPE_AR6004) { 105131024d99SKevin Fang board_address = AR6004_REV1_BOARD_DATA_ADDRESS; 105231024d99SKevin Fang ath6kl_bmi_write(ar, 105331024d99SKevin Fang ath6kl_get_hi_item_addr(ar, 105431024d99SKevin Fang HI_ITEM(hi_board_data)), 105531024d99SKevin Fang (u8 *) &board_address, 4); 105631024d99SKevin Fang } else { 1057bdcd8170SKalle Valo ath6kl_bmi_read(ar, 1058bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1059bdcd8170SKalle Valo HI_ITEM(hi_board_data)), 1060bdcd8170SKalle Valo (u8 *) &board_address, 4); 106131024d99SKevin Fang } 106231024d99SKevin Fang 1063bdcd8170SKalle Valo /* determine where in target ram to write extended board data */ 1064bdcd8170SKalle Valo ath6kl_bmi_read(ar, 1065bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1066bdcd8170SKalle Valo HI_ITEM(hi_board_ext_data)), 1067bdcd8170SKalle Valo (u8 *) &board_ext_address, 4); 1068bdcd8170SKalle Valo 1069bdcd8170SKalle Valo if (board_ext_address == 0) { 1070bdcd8170SKalle Valo ath6kl_err("Failed to get board file target address.\n"); 1071bdcd8170SKalle Valo return -EINVAL; 1072bdcd8170SKalle Valo } 1073bdcd8170SKalle Valo 107431024d99SKevin Fang switch (ar->target_type) { 107531024d99SKevin Fang case TARGET_TYPE_AR6003: 107631024d99SKevin Fang board_data_size = AR6003_BOARD_DATA_SZ; 107731024d99SKevin Fang board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ; 107831024d99SKevin Fang break; 107931024d99SKevin Fang case TARGET_TYPE_AR6004: 108031024d99SKevin Fang board_data_size = AR6004_BOARD_DATA_SZ; 108131024d99SKevin Fang board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ; 108231024d99SKevin Fang break; 108331024d99SKevin Fang default: 108431024d99SKevin Fang WARN_ON(1); 108531024d99SKevin Fang return -EINVAL; 108631024d99SKevin Fang break; 108731024d99SKevin Fang } 108831024d99SKevin Fang 108931024d99SKevin Fang if (ar->fw_board_len == (board_data_size + 109031024d99SKevin Fang board_ext_data_size)) { 109131024d99SKevin Fang 1092bdcd8170SKalle Valo /* write extended board data */ 10936bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 10946bc36431SKalle Valo "writing extended board data to 0x%x (%d B)\n", 10956bc36431SKalle Valo board_ext_address, board_ext_data_size); 10966bc36431SKalle Valo 1097bdcd8170SKalle Valo ret = ath6kl_bmi_write(ar, board_ext_address, 109831024d99SKevin Fang ar->fw_board + board_data_size, 109931024d99SKevin Fang board_ext_data_size); 1100bdcd8170SKalle Valo if (ret) { 1101bdcd8170SKalle Valo ath6kl_err("Failed to write extended board data: %d\n", 1102bdcd8170SKalle Valo ret); 1103bdcd8170SKalle Valo return ret; 1104bdcd8170SKalle Valo } 1105bdcd8170SKalle Valo 1106bdcd8170SKalle Valo /* record that extended board data is initialized */ 110731024d99SKevin Fang param = (board_ext_data_size << 16) | 1; 110831024d99SKevin Fang 1109bdcd8170SKalle Valo ath6kl_bmi_write(ar, 1110bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1111bdcd8170SKalle Valo HI_ITEM(hi_board_ext_data_config)), 1112bdcd8170SKalle Valo (unsigned char *) ¶m, 4); 1113bdcd8170SKalle Valo } 1114bdcd8170SKalle Valo 111531024d99SKevin Fang if (ar->fw_board_len < board_data_size) { 1116bdcd8170SKalle Valo ath6kl_err("Too small board file: %zu\n", ar->fw_board_len); 1117bdcd8170SKalle Valo ret = -EINVAL; 1118bdcd8170SKalle Valo return ret; 1119bdcd8170SKalle Valo } 1120bdcd8170SKalle Valo 11216bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n", 11226bc36431SKalle Valo board_address, board_data_size); 11236bc36431SKalle Valo 1124bdcd8170SKalle Valo ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, 112531024d99SKevin Fang board_data_size); 1126bdcd8170SKalle Valo 1127bdcd8170SKalle Valo if (ret) { 1128bdcd8170SKalle Valo ath6kl_err("Board file bmi write failed: %d\n", ret); 1129bdcd8170SKalle Valo return ret; 1130bdcd8170SKalle Valo } 1131bdcd8170SKalle Valo 1132bdcd8170SKalle Valo /* record the fact that Board Data IS initialized */ 1133bdcd8170SKalle Valo param = 1; 1134bdcd8170SKalle Valo ath6kl_bmi_write(ar, 1135bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1136bdcd8170SKalle Valo HI_ITEM(hi_board_data_initialized)), 1137bdcd8170SKalle Valo (u8 *)¶m, 4); 1138bdcd8170SKalle Valo 1139bdcd8170SKalle Valo return ret; 1140bdcd8170SKalle Valo } 1141bdcd8170SKalle Valo 1142bdcd8170SKalle Valo static int ath6kl_upload_otp(struct ath6kl *ar) 1143bdcd8170SKalle Valo { 1144bdcd8170SKalle Valo u32 address, param; 1145bef26a7fSKalle Valo bool from_hw = false; 1146bdcd8170SKalle Valo int ret; 1147bdcd8170SKalle Valo 1148772c31eeSKalle Valo if (WARN_ON(ar->fw_otp == NULL)) 1149772c31eeSKalle Valo return -ENOENT; 1150bdcd8170SKalle Valo 1151a01ac414SKalle Valo address = ar->hw.app_load_addr; 1152bdcd8170SKalle Valo 1153ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address, 11546bc36431SKalle Valo ar->fw_otp_len); 11556bc36431SKalle Valo 1156bdcd8170SKalle Valo ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, 1157bdcd8170SKalle Valo ar->fw_otp_len); 1158bdcd8170SKalle Valo if (ret) { 1159bdcd8170SKalle Valo ath6kl_err("Failed to upload OTP file: %d\n", ret); 1160bdcd8170SKalle Valo return ret; 1161bdcd8170SKalle Valo } 1162bdcd8170SKalle Valo 1163639d0b89SKalle Valo /* read firmware start address */ 1164639d0b89SKalle Valo ret = ath6kl_bmi_read(ar, 1165639d0b89SKalle Valo ath6kl_get_hi_item_addr(ar, 1166639d0b89SKalle Valo HI_ITEM(hi_app_start)), 1167639d0b89SKalle Valo (u8 *) &address, sizeof(address)); 1168639d0b89SKalle Valo 1169639d0b89SKalle Valo if (ret) { 1170639d0b89SKalle Valo ath6kl_err("Failed to read hi_app_start: %d\n", ret); 1171639d0b89SKalle Valo return ret; 1172639d0b89SKalle Valo } 1173639d0b89SKalle Valo 1174bef26a7fSKalle Valo if (ar->hw.app_start_override_addr == 0) { 1175639d0b89SKalle Valo ar->hw.app_start_override_addr = address; 1176bef26a7fSKalle Valo from_hw = true; 1177bef26a7fSKalle Valo } 1178639d0b89SKalle Valo 1179bef26a7fSKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr%s 0x%x\n", 1180bef26a7fSKalle Valo from_hw ? " (from hw)" : "", 11816bc36431SKalle Valo ar->hw.app_start_override_addr); 11826bc36431SKalle Valo 1183bdcd8170SKalle Valo /* execute the OTP code */ 1184bef26a7fSKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", 1185bef26a7fSKalle Valo ar->hw.app_start_override_addr); 1186bdcd8170SKalle Valo param = 0; 1187bef26a7fSKalle Valo ath6kl_bmi_execute(ar, ar->hw.app_start_override_addr, ¶m); 1188bdcd8170SKalle Valo 1189bdcd8170SKalle Valo return ret; 1190bdcd8170SKalle Valo } 1191bdcd8170SKalle Valo 1192bdcd8170SKalle Valo static int ath6kl_upload_firmware(struct ath6kl *ar) 1193bdcd8170SKalle Valo { 1194bdcd8170SKalle Valo u32 address; 1195bdcd8170SKalle Valo int ret; 1196bdcd8170SKalle Valo 1197772c31eeSKalle Valo if (WARN_ON(ar->fw == NULL)) 1198772c31eeSKalle Valo return -ENOENT; 1199bdcd8170SKalle Valo 1200a01ac414SKalle Valo address = ar->hw.app_load_addr; 1201bdcd8170SKalle Valo 1202ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n", 12036bc36431SKalle Valo address, ar->fw_len); 12046bc36431SKalle Valo 1205bdcd8170SKalle Valo ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); 1206bdcd8170SKalle Valo 1207bdcd8170SKalle Valo if (ret) { 1208bdcd8170SKalle Valo ath6kl_err("Failed to write firmware: %d\n", ret); 1209bdcd8170SKalle Valo return ret; 1210bdcd8170SKalle Valo } 1211bdcd8170SKalle Valo 121231024d99SKevin Fang /* 121331024d99SKevin Fang * Set starting address for firmware 121431024d99SKevin Fang * Don't need to setup app_start override addr on AR6004 121531024d99SKevin Fang */ 121631024d99SKevin Fang if (ar->target_type != TARGET_TYPE_AR6004) { 1217a01ac414SKalle Valo address = ar->hw.app_start_override_addr; 1218bdcd8170SKalle Valo ath6kl_bmi_set_app_start(ar, address); 121931024d99SKevin Fang } 1220bdcd8170SKalle Valo return ret; 1221bdcd8170SKalle Valo } 1222bdcd8170SKalle Valo 1223bdcd8170SKalle Valo static int ath6kl_upload_patch(struct ath6kl *ar) 1224bdcd8170SKalle Valo { 1225bdcd8170SKalle Valo u32 address, param; 1226bdcd8170SKalle Valo int ret; 1227bdcd8170SKalle Valo 1228772c31eeSKalle Valo if (WARN_ON(ar->fw_patch == NULL)) 1229772c31eeSKalle Valo return -ENOENT; 1230bdcd8170SKalle Valo 1231a01ac414SKalle Valo address = ar->hw.dataset_patch_addr; 1232bdcd8170SKalle Valo 1233ef548626SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n", 12346bc36431SKalle Valo address, ar->fw_patch_len); 12356bc36431SKalle Valo 1236bdcd8170SKalle Valo ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); 1237bdcd8170SKalle Valo if (ret) { 1238bdcd8170SKalle Valo ath6kl_err("Failed to write patch file: %d\n", ret); 1239bdcd8170SKalle Valo return ret; 1240bdcd8170SKalle Valo } 1241bdcd8170SKalle Valo 1242bdcd8170SKalle Valo param = address; 1243bdcd8170SKalle Valo ath6kl_bmi_write(ar, 1244bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1245bdcd8170SKalle Valo HI_ITEM(hi_dset_list_head)), 1246bdcd8170SKalle Valo (unsigned char *) ¶m, 4); 1247bdcd8170SKalle Valo 1248bdcd8170SKalle Valo return 0; 1249bdcd8170SKalle Valo } 1250bdcd8170SKalle Valo 1251bdcd8170SKalle Valo static int ath6kl_init_upload(struct ath6kl *ar) 1252bdcd8170SKalle Valo { 1253bdcd8170SKalle Valo u32 param, options, sleep, address; 1254bdcd8170SKalle Valo int status = 0; 1255bdcd8170SKalle Valo 125631024d99SKevin Fang if (ar->target_type != TARGET_TYPE_AR6003 && 125731024d99SKevin Fang ar->target_type != TARGET_TYPE_AR6004) 1258bdcd8170SKalle Valo return -EINVAL; 1259bdcd8170SKalle Valo 1260bdcd8170SKalle Valo /* temporarily disable system sleep */ 1261bdcd8170SKalle Valo address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; 1262bdcd8170SKalle Valo status = ath6kl_bmi_reg_read(ar, address, ¶m); 1263bdcd8170SKalle Valo if (status) 1264bdcd8170SKalle Valo return status; 1265bdcd8170SKalle Valo 1266bdcd8170SKalle Valo options = param; 1267bdcd8170SKalle Valo 1268bdcd8170SKalle Valo param |= ATH6KL_OPTION_SLEEP_DISABLE; 1269bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1270bdcd8170SKalle Valo if (status) 1271bdcd8170SKalle Valo return status; 1272bdcd8170SKalle Valo 1273bdcd8170SKalle Valo address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; 1274bdcd8170SKalle Valo status = ath6kl_bmi_reg_read(ar, address, ¶m); 1275bdcd8170SKalle Valo if (status) 1276bdcd8170SKalle Valo return status; 1277bdcd8170SKalle Valo 1278bdcd8170SKalle Valo sleep = param; 1279bdcd8170SKalle Valo 1280bdcd8170SKalle Valo param |= SM(SYSTEM_SLEEP_DISABLE, 1); 1281bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1282bdcd8170SKalle Valo if (status) 1283bdcd8170SKalle Valo return status; 1284bdcd8170SKalle Valo 1285bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "old options: %d, old sleep: %d\n", 1286bdcd8170SKalle Valo options, sleep); 1287bdcd8170SKalle Valo 1288bdcd8170SKalle Valo /* program analog PLL register */ 128931024d99SKevin Fang /* no need to control 40/44MHz clock on AR6004 */ 129031024d99SKevin Fang if (ar->target_type != TARGET_TYPE_AR6004) { 1291bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, 1292bdcd8170SKalle Valo 0xF9104001); 129331024d99SKevin Fang 1294bdcd8170SKalle Valo if (status) 1295bdcd8170SKalle Valo return status; 1296bdcd8170SKalle Valo 1297bdcd8170SKalle Valo /* Run at 80/88MHz by default */ 1298bdcd8170SKalle Valo param = SM(CPU_CLOCK_STANDARD, 1); 1299bdcd8170SKalle Valo 1300bdcd8170SKalle Valo address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS; 1301bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1302bdcd8170SKalle Valo if (status) 1303bdcd8170SKalle Valo return status; 130431024d99SKevin Fang } 1305bdcd8170SKalle Valo 1306bdcd8170SKalle Valo param = 0; 1307bdcd8170SKalle Valo address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS; 1308bdcd8170SKalle Valo param = SM(LPO_CAL_ENABLE, 1); 1309bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1310bdcd8170SKalle Valo if (status) 1311bdcd8170SKalle Valo return status; 1312bdcd8170SKalle Valo 1313bdcd8170SKalle Valo /* WAR to avoid SDIO CRC err */ 1314bdcd8170SKalle Valo if (ar->version.target_ver == AR6003_REV2_VERSION) { 1315bdcd8170SKalle Valo ath6kl_err("temporary war to avoid sdio crc error\n"); 1316bdcd8170SKalle Valo 1317bdcd8170SKalle Valo param = 0x20; 1318bdcd8170SKalle Valo 1319bdcd8170SKalle Valo address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS; 1320bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1321bdcd8170SKalle Valo if (status) 1322bdcd8170SKalle Valo return status; 1323bdcd8170SKalle Valo 1324bdcd8170SKalle Valo address = GPIO_BASE_ADDRESS + GPIO_PIN11_ADDRESS; 1325bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1326bdcd8170SKalle Valo if (status) 1327bdcd8170SKalle Valo return status; 1328bdcd8170SKalle Valo 1329bdcd8170SKalle Valo address = GPIO_BASE_ADDRESS + GPIO_PIN12_ADDRESS; 1330bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1331bdcd8170SKalle Valo if (status) 1332bdcd8170SKalle Valo return status; 1333bdcd8170SKalle Valo 1334bdcd8170SKalle Valo address = GPIO_BASE_ADDRESS + GPIO_PIN13_ADDRESS; 1335bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1336bdcd8170SKalle Valo if (status) 1337bdcd8170SKalle Valo return status; 1338bdcd8170SKalle Valo } 1339bdcd8170SKalle Valo 1340bdcd8170SKalle Valo /* write EEPROM data to Target RAM */ 1341bdcd8170SKalle Valo status = ath6kl_upload_board_file(ar); 1342bdcd8170SKalle Valo if (status) 1343bdcd8170SKalle Valo return status; 1344bdcd8170SKalle Valo 1345bdcd8170SKalle Valo /* transfer One time Programmable data */ 1346bdcd8170SKalle Valo status = ath6kl_upload_otp(ar); 1347bdcd8170SKalle Valo if (status) 1348bdcd8170SKalle Valo return status; 1349bdcd8170SKalle Valo 1350bdcd8170SKalle Valo /* Download Target firmware */ 1351bdcd8170SKalle Valo status = ath6kl_upload_firmware(ar); 1352bdcd8170SKalle Valo if (status) 1353bdcd8170SKalle Valo return status; 1354bdcd8170SKalle Valo 1355bdcd8170SKalle Valo status = ath6kl_upload_patch(ar); 1356bdcd8170SKalle Valo if (status) 1357bdcd8170SKalle Valo return status; 1358bdcd8170SKalle Valo 1359bdcd8170SKalle Valo /* Restore system sleep */ 1360bdcd8170SKalle Valo address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; 1361bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, sleep); 1362bdcd8170SKalle Valo if (status) 1363bdcd8170SKalle Valo return status; 1364bdcd8170SKalle Valo 1365bdcd8170SKalle Valo address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; 1366bdcd8170SKalle Valo param = options | 0x20; 1367bdcd8170SKalle Valo status = ath6kl_bmi_reg_write(ar, address, param); 1368bdcd8170SKalle Valo if (status) 1369bdcd8170SKalle Valo return status; 1370bdcd8170SKalle Valo 1371bdcd8170SKalle Valo /* Configure GPIO AR6003 UART */ 1372bdcd8170SKalle Valo param = CONFIG_AR600x_DEBUG_UART_TX_PIN; 1373bdcd8170SKalle Valo status = ath6kl_bmi_write(ar, 1374bdcd8170SKalle Valo ath6kl_get_hi_item_addr(ar, 1375bdcd8170SKalle Valo HI_ITEM(hi_dbg_uart_txpin)), 1376bdcd8170SKalle Valo (u8 *)¶m, 4); 1377bdcd8170SKalle Valo 1378bdcd8170SKalle Valo return status; 1379bdcd8170SKalle Valo } 1380bdcd8170SKalle Valo 1381a01ac414SKalle Valo static int ath6kl_init_hw_params(struct ath6kl *ar) 1382a01ac414SKalle Valo { 1383a01ac414SKalle Valo switch (ar->version.target_ver) { 1384a01ac414SKalle Valo case AR6003_REV2_VERSION: 1385a01ac414SKalle Valo ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; 1386a01ac414SKalle Valo ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS; 1387991b27eaSKalle Valo ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; 1388991b27eaSKalle Valo ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE; 1389bef26a7fSKalle Valo 1390bef26a7fSKalle Valo /* hw2.0 needs override address hardcoded */ 1391bef26a7fSKalle Valo ar->hw.app_start_override_addr = 0x944C00; 1392bef26a7fSKalle Valo 1393a01ac414SKalle Valo break; 1394a01ac414SKalle Valo case AR6003_REV3_VERSION: 1395a01ac414SKalle Valo ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS; 1396a01ac414SKalle Valo ar->hw.app_load_addr = 0x1234; 1397991b27eaSKalle Valo ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; 1398991b27eaSKalle Valo ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE; 1399a01ac414SKalle Valo break; 1400a01ac414SKalle Valo case AR6004_REV1_VERSION: 1401a01ac414SKalle Valo ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; 1402a01ac414SKalle Valo ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS; 1403991b27eaSKalle Valo ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS; 1404991b27eaSKalle Valo ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE; 1405a01ac414SKalle Valo break; 1406a01ac414SKalle Valo default: 1407a01ac414SKalle Valo ath6kl_err("Unsupported hardware version: 0x%x\n", 1408a01ac414SKalle Valo ar->version.target_ver); 1409a01ac414SKalle Valo return -EINVAL; 1410a01ac414SKalle Valo } 1411a01ac414SKalle Valo 14126bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 14136bc36431SKalle Valo "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n", 14146bc36431SKalle Valo ar->version.target_ver, ar->target_type, 14156bc36431SKalle Valo ar->hw.dataset_patch_addr, ar->hw.app_load_addr); 14166bc36431SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, 14176bc36431SKalle Valo "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x", 14186bc36431SKalle Valo ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr, 14196bc36431SKalle Valo ar->hw.reserved_ram_size); 14206bc36431SKalle Valo 1421a01ac414SKalle Valo return 0; 1422a01ac414SKalle Valo } 1423a01ac414SKalle Valo 1424*20459ee2SKalle Valo static int ath6kl_hw_start(struct ath6kl *ar) 1425*20459ee2SKalle Valo { 1426*20459ee2SKalle Valo long timeleft; 1427*20459ee2SKalle Valo int ret, i; 1428*20459ee2SKalle Valo 1429*20459ee2SKalle Valo ret = ath6kl_hif_power_on(ar); 1430*20459ee2SKalle Valo if (ret) 1431*20459ee2SKalle Valo return ret; 1432*20459ee2SKalle Valo 1433*20459ee2SKalle Valo ret = ath6kl_configure_target(ar); 1434*20459ee2SKalle Valo if (ret) 1435*20459ee2SKalle Valo goto err_power_off; 1436*20459ee2SKalle Valo 1437*20459ee2SKalle Valo ret = ath6kl_init_upload(ar); 1438*20459ee2SKalle Valo if (ret) 1439*20459ee2SKalle Valo goto err_power_off; 1440*20459ee2SKalle Valo 1441*20459ee2SKalle Valo /* Do we need to finish the BMI phase */ 1442*20459ee2SKalle Valo /* FIXME: return error from ath6kl_bmi_done() */ 1443*20459ee2SKalle Valo if (ath6kl_bmi_done(ar)) { 1444*20459ee2SKalle Valo ret = -EIO; 1445*20459ee2SKalle Valo goto err_power_off; 1446*20459ee2SKalle Valo } 1447*20459ee2SKalle Valo 1448*20459ee2SKalle Valo /* 1449*20459ee2SKalle Valo * The reason we have to wait for the target here is that the 1450*20459ee2SKalle Valo * driver layer has to init BMI in order to set the host block 1451*20459ee2SKalle Valo * size. 1452*20459ee2SKalle Valo */ 1453*20459ee2SKalle Valo if (ath6kl_htc_wait_target(ar->htc_target)) { 1454*20459ee2SKalle Valo ret = -EIO; 1455*20459ee2SKalle Valo goto err_power_off; 1456*20459ee2SKalle Valo } 1457*20459ee2SKalle Valo 1458*20459ee2SKalle Valo if (ath6kl_init_service_ep(ar)) { 1459*20459ee2SKalle Valo ret = -EIO; 1460*20459ee2SKalle Valo goto err_cleanup_scatter; 1461*20459ee2SKalle Valo } 1462*20459ee2SKalle Valo 1463*20459ee2SKalle Valo /* setup credit distribution */ 1464*20459ee2SKalle Valo ath6kl_credit_setup(ar->htc_target, &ar->credit_state_info); 1465*20459ee2SKalle Valo 1466*20459ee2SKalle Valo /* start HTC */ 1467*20459ee2SKalle Valo ret = ath6kl_htc_start(ar->htc_target); 1468*20459ee2SKalle Valo if (ret) { 1469*20459ee2SKalle Valo /* FIXME: call this */ 1470*20459ee2SKalle Valo ath6kl_cookie_cleanup(ar); 1471*20459ee2SKalle Valo goto err_cleanup_scatter; 1472*20459ee2SKalle Valo } 1473*20459ee2SKalle Valo 1474*20459ee2SKalle Valo /* Wait for Wmi event to be ready */ 1475*20459ee2SKalle Valo timeleft = wait_event_interruptible_timeout(ar->event_wq, 1476*20459ee2SKalle Valo test_bit(WMI_READY, 1477*20459ee2SKalle Valo &ar->flag), 1478*20459ee2SKalle Valo WMI_TIMEOUT); 1479*20459ee2SKalle Valo 1480*20459ee2SKalle Valo ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); 1481*20459ee2SKalle Valo 1482*20459ee2SKalle Valo if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { 1483*20459ee2SKalle Valo ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", 1484*20459ee2SKalle Valo ATH6KL_ABI_VERSION, ar->version.abi_ver); 1485*20459ee2SKalle Valo ret = -EIO; 1486*20459ee2SKalle Valo goto err_htc_stop; 1487*20459ee2SKalle Valo } 1488*20459ee2SKalle Valo 1489*20459ee2SKalle Valo if (!timeleft || signal_pending(current)) { 1490*20459ee2SKalle Valo ath6kl_err("wmi is not ready or wait was interrupted\n"); 1491*20459ee2SKalle Valo ret = -EIO; 1492*20459ee2SKalle Valo goto err_htc_stop; 1493*20459ee2SKalle Valo } 1494*20459ee2SKalle Valo 1495*20459ee2SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__); 1496*20459ee2SKalle Valo 1497*20459ee2SKalle Valo /* communicate the wmi protocol verision to the target */ 1498*20459ee2SKalle Valo /* FIXME: return error */ 1499*20459ee2SKalle Valo if ((ath6kl_set_host_app_area(ar)) != 0) 1500*20459ee2SKalle Valo ath6kl_err("unable to set the host app area\n"); 1501*20459ee2SKalle Valo 1502*20459ee2SKalle Valo for (i = 0; i < MAX_NUM_VIF; i++) { 1503*20459ee2SKalle Valo ret = ath6kl_target_config_wlan_params(ar, i); 1504*20459ee2SKalle Valo if (ret) 1505*20459ee2SKalle Valo goto err_htc_stop; 1506*20459ee2SKalle Valo } 1507*20459ee2SKalle Valo 1508*20459ee2SKalle Valo return 0; 1509*20459ee2SKalle Valo 1510*20459ee2SKalle Valo err_htc_stop: 1511*20459ee2SKalle Valo ath6kl_htc_stop(ar->htc_target); 1512*20459ee2SKalle Valo err_cleanup_scatter: 1513*20459ee2SKalle Valo ath6kl_hif_cleanup_scatter(ar); 1514*20459ee2SKalle Valo err_power_off: 1515*20459ee2SKalle Valo ath6kl_hif_power_off(ar); 1516*20459ee2SKalle Valo 1517*20459ee2SKalle Valo return ret; 1518*20459ee2SKalle Valo } 1519*20459ee2SKalle Valo 1520bdcd8170SKalle Valo int ath6kl_core_init(struct ath6kl *ar) 1521bdcd8170SKalle Valo { 1522bdcd8170SKalle Valo struct ath6kl_bmi_target_info targ_info; 152361448a93SKalle Valo struct net_device *ndev; 1524*20459ee2SKalle Valo int ret = 0, i; 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 1534*20459ee2SKalle Valo /* 1535*20459ee2SKalle Valo * Turn on power to get hardware (target) version and leave power 1536*20459ee2SKalle Valo * on delibrately as we will boot the hardware anyway within few 1537*20459ee2SKalle Valo * seconds. 1538*20459ee2SKalle Valo */ 1539b2e75698SKalle Valo ret = ath6kl_hif_power_on(ar); 1540bdcd8170SKalle Valo if (ret) 1541bdcd8170SKalle Valo goto err_bmi_cleanup; 1542bdcd8170SKalle Valo 1543b2e75698SKalle Valo ret = ath6kl_bmi_get_target_info(ar, &targ_info); 1544b2e75698SKalle Valo if (ret) 1545b2e75698SKalle Valo goto err_power_off; 1546b2e75698SKalle Valo 1547bdcd8170SKalle Valo ar->version.target_ver = le32_to_cpu(targ_info.version); 1548bdcd8170SKalle Valo ar->target_type = le32_to_cpu(targ_info.type); 1549be98e3a4SVasanthakumar Thiagarajan ar->wiphy->hw_version = le32_to_cpu(targ_info.version); 1550bdcd8170SKalle Valo 1551a01ac414SKalle Valo ret = ath6kl_init_hw_params(ar); 1552a01ac414SKalle Valo if (ret) 1553b2e75698SKalle Valo goto err_power_off; 1554a01ac414SKalle Valo 1555ad226ec2SKalle Valo ar->htc_target = ath6kl_htc_create(ar); 1556bdcd8170SKalle Valo 1557bdcd8170SKalle Valo if (!ar->htc_target) { 1558bdcd8170SKalle Valo ret = -ENOMEM; 1559b2e75698SKalle Valo goto err_power_off; 1560bdcd8170SKalle Valo } 1561bdcd8170SKalle Valo 1562772c31eeSKalle Valo ret = ath6kl_fetch_firmwares(ar); 1563772c31eeSKalle Valo if (ret) 1564772c31eeSKalle Valo goto err_htc_cleanup; 1565772c31eeSKalle Valo 156661448a93SKalle Valo /* FIXME: we should free all firmwares in the error cases below */ 156761448a93SKalle Valo 156861448a93SKalle Valo /* Indicate that WMI is enabled (although not ready yet) */ 156961448a93SKalle Valo set_bit(WMI_ENABLED, &ar->flag); 157061448a93SKalle Valo ar->wmi = ath6kl_wmi_init(ar); 157161448a93SKalle Valo if (!ar->wmi) { 157261448a93SKalle Valo ath6kl_err("failed to initialize wmi\n"); 157361448a93SKalle Valo ret = -EIO; 157461448a93SKalle Valo goto err_htc_cleanup; 157561448a93SKalle Valo } 157661448a93SKalle Valo 157761448a93SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); 157861448a93SKalle Valo 157961448a93SKalle Valo ret = ath6kl_register_ieee80211_hw(ar); 158061448a93SKalle Valo if (ret) 158161448a93SKalle Valo goto err_node_cleanup; 158261448a93SKalle Valo 158361448a93SKalle Valo ret = ath6kl_debug_init(ar); 158461448a93SKalle Valo if (ret) { 158561448a93SKalle Valo wiphy_unregister(ar->wiphy); 158661448a93SKalle Valo goto err_node_cleanup; 158761448a93SKalle Valo } 158861448a93SKalle Valo 158961448a93SKalle Valo for (i = 0; i < MAX_NUM_VIF; i++) 159061448a93SKalle Valo ar->avail_idx_map |= BIT(i); 159161448a93SKalle Valo 159261448a93SKalle Valo rtnl_lock(); 159361448a93SKalle Valo 159461448a93SKalle Valo /* Add an initial station interface */ 159561448a93SKalle Valo ndev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0, 159661448a93SKalle Valo INFRA_NETWORK); 159761448a93SKalle Valo 159861448a93SKalle Valo rtnl_unlock(); 159961448a93SKalle Valo 160061448a93SKalle Valo if (!ndev) { 160161448a93SKalle Valo ath6kl_err("Failed to instantiate a network device\n"); 160261448a93SKalle Valo ret = -ENOMEM; 160361448a93SKalle Valo wiphy_unregister(ar->wiphy); 160461448a93SKalle Valo goto err_debug_init; 160561448a93SKalle Valo } 160661448a93SKalle Valo 160761448a93SKalle Valo 160861448a93SKalle Valo ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", 160961448a93SKalle Valo __func__, ndev->name, ndev, ar); 161061448a93SKalle Valo 161161448a93SKalle Valo /* setup access class priority mappings */ 161261448a93SKalle Valo ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ 161361448a93SKalle Valo ar->ac_stream_pri_map[WMM_AC_BE] = 1; 161461448a93SKalle Valo ar->ac_stream_pri_map[WMM_AC_VI] = 2; 161561448a93SKalle Valo ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ 161661448a93SKalle Valo 161761448a93SKalle Valo /* give our connected endpoints some buffers */ 161861448a93SKalle Valo ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); 161961448a93SKalle Valo ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); 162061448a93SKalle Valo 162161448a93SKalle Valo /* allocate some buffers that handle larger AMSDU frames */ 162261448a93SKalle Valo ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); 162361448a93SKalle Valo 162461448a93SKalle Valo ath6kl_cookie_init(ar); 162561448a93SKalle Valo 162661448a93SKalle Valo ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | 162761448a93SKalle Valo ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; 162861448a93SKalle Valo 162961448a93SKalle Valo ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | 163061448a93SKalle Valo WIPHY_FLAG_HAVE_AP_SME; 163161448a93SKalle Valo 1632*20459ee2SKalle Valo ret = ath6kl_hw_start(ar); 1633*20459ee2SKalle Valo if (ret) { 1634*20459ee2SKalle Valo ath6kl_err("Failed to boot hardware: %d\n", ret); 1635*20459ee2SKalle Valo goto err_rxbuf_cleanup; 163661448a93SKalle Valo } 163761448a93SKalle Valo 163861448a93SKalle Valo /* 163961448a93SKalle Valo * Set mac address which is received in ready event 164061448a93SKalle Valo * FIXME: Move to ath6kl_interface_add() 164161448a93SKalle Valo */ 164261448a93SKalle Valo memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); 1643bdcd8170SKalle Valo 1644bdcd8170SKalle Valo return ret; 1645bdcd8170SKalle Valo 164661448a93SKalle Valo err_rxbuf_cleanup: 164761448a93SKalle Valo ath6kl_htc_flush_rx_buf(ar->htc_target); 164861448a93SKalle Valo ath6kl_cleanup_amsdu_rxbufs(ar); 164961448a93SKalle Valo rtnl_lock(); 165061448a93SKalle Valo ath6kl_deinit_if_data(netdev_priv(ndev)); 165161448a93SKalle Valo rtnl_unlock(); 165261448a93SKalle Valo wiphy_unregister(ar->wiphy); 165361448a93SKalle Valo err_debug_init: 165461448a93SKalle Valo ath6kl_debug_cleanup(ar); 165561448a93SKalle Valo err_node_cleanup: 165661448a93SKalle Valo ath6kl_wmi_shutdown(ar->wmi); 165761448a93SKalle Valo clear_bit(WMI_ENABLED, &ar->flag); 165861448a93SKalle Valo ar->wmi = NULL; 1659bdcd8170SKalle Valo err_htc_cleanup: 1660ad226ec2SKalle Valo ath6kl_htc_cleanup(ar->htc_target); 1661b2e75698SKalle Valo err_power_off: 1662b2e75698SKalle Valo ath6kl_hif_power_off(ar); 1663bdcd8170SKalle Valo err_bmi_cleanup: 1664bdcd8170SKalle Valo ath6kl_bmi_cleanup(ar); 1665bdcd8170SKalle Valo err_wq: 1666bdcd8170SKalle Valo destroy_workqueue(ar->ath6kl_wq); 16678dafb70eSVasanthakumar Thiagarajan 1668bdcd8170SKalle Valo return ret; 1669bdcd8170SKalle Valo } 1670bdcd8170SKalle Valo 167155055976SVasanthakumar Thiagarajan void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) 16726db8fa53SVasanthakumar Thiagarajan { 16736db8fa53SVasanthakumar Thiagarajan static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 16746db8fa53SVasanthakumar Thiagarajan bool discon_issued; 16756db8fa53SVasanthakumar Thiagarajan 16766db8fa53SVasanthakumar Thiagarajan netif_stop_queue(vif->ndev); 16776db8fa53SVasanthakumar Thiagarajan 16786db8fa53SVasanthakumar Thiagarajan clear_bit(WLAN_ENABLED, &vif->flags); 16796db8fa53SVasanthakumar Thiagarajan 16806db8fa53SVasanthakumar Thiagarajan if (wmi_ready) { 16816db8fa53SVasanthakumar Thiagarajan discon_issued = test_bit(CONNECTED, &vif->flags) || 16826db8fa53SVasanthakumar Thiagarajan test_bit(CONNECT_PEND, &vif->flags); 16836db8fa53SVasanthakumar Thiagarajan ath6kl_disconnect(vif); 16846db8fa53SVasanthakumar Thiagarajan del_timer(&vif->disconnect_timer); 16856db8fa53SVasanthakumar Thiagarajan 16866db8fa53SVasanthakumar Thiagarajan if (discon_issued) 16876db8fa53SVasanthakumar Thiagarajan ath6kl_disconnect_event(vif, DISCONNECT_CMD, 16886db8fa53SVasanthakumar Thiagarajan (vif->nw_type & AP_NETWORK) ? 16896db8fa53SVasanthakumar Thiagarajan bcast_mac : vif->bssid, 16906db8fa53SVasanthakumar Thiagarajan 0, NULL, 0); 16916db8fa53SVasanthakumar Thiagarajan } 16926db8fa53SVasanthakumar Thiagarajan 16936db8fa53SVasanthakumar Thiagarajan if (vif->scan_req) { 16946db8fa53SVasanthakumar Thiagarajan cfg80211_scan_done(vif->scan_req, true); 16956db8fa53SVasanthakumar Thiagarajan vif->scan_req = NULL; 16966db8fa53SVasanthakumar Thiagarajan } 16976db8fa53SVasanthakumar Thiagarajan } 16986db8fa53SVasanthakumar Thiagarajan 1699bdcd8170SKalle Valo void ath6kl_stop_txrx(struct ath6kl *ar) 1700bdcd8170SKalle Valo { 1701990bd915SVasanthakumar Thiagarajan struct ath6kl_vif *vif, *tmp_vif; 1702bdcd8170SKalle Valo 1703bdcd8170SKalle Valo set_bit(DESTROY_IN_PROGRESS, &ar->flag); 1704bdcd8170SKalle Valo 1705bdcd8170SKalle Valo if (down_interruptible(&ar->sem)) { 1706bdcd8170SKalle Valo ath6kl_err("down_interruptible failed\n"); 1707bdcd8170SKalle Valo return; 1708bdcd8170SKalle Valo } 1709bdcd8170SKalle Valo 1710990bd915SVasanthakumar Thiagarajan spin_lock(&ar->list_lock); 1711990bd915SVasanthakumar Thiagarajan list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) { 1712990bd915SVasanthakumar Thiagarajan list_del(&vif->list); 1713990bd915SVasanthakumar Thiagarajan spin_unlock(&ar->list_lock); 1714990bd915SVasanthakumar Thiagarajan ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); 171527929723SVasanthakumar Thiagarajan rtnl_lock(); 171627929723SVasanthakumar Thiagarajan ath6kl_deinit_if_data(vif); 171727929723SVasanthakumar Thiagarajan rtnl_unlock(); 1718990bd915SVasanthakumar Thiagarajan spin_lock(&ar->list_lock); 1719990bd915SVasanthakumar Thiagarajan } 1720990bd915SVasanthakumar Thiagarajan spin_unlock(&ar->list_lock); 1721bdcd8170SKalle Valo 17226db8fa53SVasanthakumar Thiagarajan clear_bit(WMI_READY, &ar->flag); 17236db8fa53SVasanthakumar Thiagarajan 17246db8fa53SVasanthakumar Thiagarajan /* 17256db8fa53SVasanthakumar Thiagarajan * After wmi_shudown all WMI events will be dropped. We 17266db8fa53SVasanthakumar Thiagarajan * need to cleanup the buffers allocated in AP mode and 17276db8fa53SVasanthakumar Thiagarajan * give disconnect notification to stack, which usually 17286db8fa53SVasanthakumar Thiagarajan * happens in the disconnect_event. Simulate the disconnect 17296db8fa53SVasanthakumar Thiagarajan * event by calling the function directly. Sometimes 17306db8fa53SVasanthakumar Thiagarajan * disconnect_event will be received when the debug logs 17316db8fa53SVasanthakumar Thiagarajan * are collected. 17326db8fa53SVasanthakumar Thiagarajan */ 17336db8fa53SVasanthakumar Thiagarajan ath6kl_wmi_shutdown(ar->wmi); 17346db8fa53SVasanthakumar Thiagarajan 17356db8fa53SVasanthakumar Thiagarajan clear_bit(WMI_ENABLED, &ar->flag); 17366db8fa53SVasanthakumar Thiagarajan if (ar->htc_target) { 17376db8fa53SVasanthakumar Thiagarajan ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__); 17386db8fa53SVasanthakumar Thiagarajan ath6kl_htc_stop(ar->htc_target); 1739bdcd8170SKalle Valo } 1740bdcd8170SKalle Valo 1741bdcd8170SKalle Valo /* 17426db8fa53SVasanthakumar Thiagarajan * Try to reset the device if we can. The driver may have been 17436db8fa53SVasanthakumar Thiagarajan * configure NOT to reset the target during a debug session. 1744bdcd8170SKalle Valo */ 17456db8fa53SVasanthakumar Thiagarajan ath6kl_dbg(ATH6KL_DBG_TRC, 17466db8fa53SVasanthakumar Thiagarajan "attempting to reset target on instance destroy\n"); 17476db8fa53SVasanthakumar Thiagarajan ath6kl_reset_device(ar, ar->target_type, true, true); 1748bdcd8170SKalle Valo 17496db8fa53SVasanthakumar Thiagarajan clear_bit(WLAN_ENABLED, &ar->flag); 1750bdcd8170SKalle Valo } 1751