19463fd55SLee Jones /* 2dad0d04fSFariya Fatima * Copyright (c) 2014 Redpine Signals Inc. 3dad0d04fSFariya Fatima * 4dad0d04fSFariya Fatima * Permission to use, copy, modify, and/or distribute this software for any 5dad0d04fSFariya Fatima * purpose with or without fee is hereby granted, provided that the above 6dad0d04fSFariya Fatima * copyright notice and this permission notice appear in all copies. 7dad0d04fSFariya Fatima * 8dad0d04fSFariya Fatima * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9dad0d04fSFariya Fatima * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10dad0d04fSFariya Fatima * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11dad0d04fSFariya Fatima * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12dad0d04fSFariya Fatima * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13dad0d04fSFariya Fatima * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14dad0d04fSFariya Fatima * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15dad0d04fSFariya Fatima */ 16dad0d04fSFariya Fatima 17b78e91bcSPrameela Rani Garnepudi #include <linux/firmware.h> 18716b840cSSiva Rebbagondla #include <net/bluetooth/bluetooth.h> 19dad0d04fSFariya Fatima #include "rsi_mgmt.h" 20b78e91bcSPrameela Rani Garnepudi #include "rsi_hal.h" 21b78e91bcSPrameela Rani Garnepudi #include "rsi_sdio.h" 2219844c0aSPrameela Rani Garnepudi #include "rsi_common.h" 23b78e91bcSPrameela Rani Garnepudi 24b78e91bcSPrameela Rani Garnepudi /* FLASH Firmware */ 25b78e91bcSPrameela Rani Garnepudi static struct ta_metadata metadata_flash_content[] = { 26b78e91bcSPrameela Rani Garnepudi {"flash_content", 0x00010000}, 27219569adSamit karwar {"rsi/rs9113_wlan_qspi.rps", 0x00010000}, 28716b840cSSiva Rebbagondla {"rsi/rs9113_wlan_bt_dual_mode.rps", 0x00010000}, 29f5fbce65SSiva Rebbagondla {"flash_content", 0x00010000}, 30f5fbce65SSiva Rebbagondla {"rsi/rs9113_ap_bt_dual_mode.rps", 0x00010000}, 31f5fbce65SSiva Rebbagondla 32b78e91bcSPrameela Rani Garnepudi }; 33dad0d04fSFariya Fatima 34e5a1ecc9SSiva Rebbagondla static struct ta_metadata metadata[] = {{"pmemdata_dummy", 0x00000000}, 35e5a1ecc9SSiva Rebbagondla {"rsi/rs9116_wlan.rps", 0x00000000}, 36e5a1ecc9SSiva Rebbagondla {"rsi/rs9116_wlan_bt_classic.rps", 0x00000000}, 37e5a1ecc9SSiva Rebbagondla {"rsi/pmemdata_dummy", 0x00000000}, 38e5a1ecc9SSiva Rebbagondla {"rsi/rs9116_wlan_bt_classic.rps", 0x00000000} 39e5a1ecc9SSiva Rebbagondla }; 40e5a1ecc9SSiva Rebbagondla 41d26a9559SPrameela Rani Garnepudi int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb) 42d26a9559SPrameela Rani Garnepudi { 43d26a9559SPrameela Rani Garnepudi struct rsi_hw *adapter = common->priv; 44d26a9559SPrameela Rani Garnepudi int status; 45d26a9559SPrameela Rani Garnepudi 462108df3cSPrameela Rani Garnepudi if (common->coex_mode > 1) 472108df3cSPrameela Rani Garnepudi mutex_lock(&common->tx_bus_mutex); 482108df3cSPrameela Rani Garnepudi 49d26a9559SPrameela Rani Garnepudi status = adapter->host_intf_ops->write_pkt(common->priv, 50d26a9559SPrameela Rani Garnepudi skb->data, skb->len); 512108df3cSPrameela Rani Garnepudi 522108df3cSPrameela Rani Garnepudi if (common->coex_mode > 1) 532108df3cSPrameela Rani Garnepudi mutex_unlock(&common->tx_bus_mutex); 542108df3cSPrameela Rani Garnepudi 55d26a9559SPrameela Rani Garnepudi return status; 56d26a9559SPrameela Rani Garnepudi } 576507de6dSPrameela Rani Garnepudi 581be05eb5SPrameela Rani Garnepudi int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) 596507de6dSPrameela Rani Garnepudi { 606507de6dSPrameela Rani Garnepudi struct rsi_hw *adapter = common->priv; 616507de6dSPrameela Rani Garnepudi struct ieee80211_hdr *wh = NULL; 626507de6dSPrameela Rani Garnepudi struct ieee80211_tx_info *info; 636507de6dSPrameela Rani Garnepudi struct ieee80211_conf *conf = &adapter->hw->conf; 644671c209SPrameela Rani Garnepudi struct ieee80211_vif *vif; 656507de6dSPrameela Rani Garnepudi struct rsi_mgmt_desc *mgmt_desc; 666507de6dSPrameela Rani Garnepudi struct skb_info *tx_params; 675dc36387SPrameela Rani Garnepudi struct rsi_xtended_desc *xtend_desc = NULL; 686507de6dSPrameela Rani Garnepudi u8 header_size; 696507de6dSPrameela Rani Garnepudi u32 dword_align_bytes = 0; 706507de6dSPrameela Rani Garnepudi 7119844c0aSPrameela Rani Garnepudi if (skb->len > MAX_MGMT_PKT_SIZE) { 7219844c0aSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); 7319844c0aSPrameela Rani Garnepudi return -EINVAL; 7419844c0aSPrameela Rani Garnepudi } 7519844c0aSPrameela Rani Garnepudi 766507de6dSPrameela Rani Garnepudi info = IEEE80211_SKB_CB(skb); 776507de6dSPrameela Rani Garnepudi tx_params = (struct skb_info *)info->driver_data; 784671c209SPrameela Rani Garnepudi vif = tx_params->vif; 796507de6dSPrameela Rani Garnepudi 806507de6dSPrameela Rani Garnepudi /* Update header size */ 815dc36387SPrameela Rani Garnepudi header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc); 826507de6dSPrameela Rani Garnepudi if (header_size > skb_headroom(skb)) { 836507de6dSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 846507de6dSPrameela Rani Garnepudi "%s: Failed to add extended descriptor\n", 856507de6dSPrameela Rani Garnepudi __func__); 866507de6dSPrameela Rani Garnepudi return -ENOSPC; 876507de6dSPrameela Rani Garnepudi } 886507de6dSPrameela Rani Garnepudi skb_push(skb, header_size); 896507de6dSPrameela Rani Garnepudi dword_align_bytes = ((unsigned long)skb->data & 0x3f); 906507de6dSPrameela Rani Garnepudi if (dword_align_bytes > skb_headroom(skb)) { 916507de6dSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 926507de6dSPrameela Rani Garnepudi "%s: Failed to add dword align\n", __func__); 936507de6dSPrameela Rani Garnepudi return -ENOSPC; 946507de6dSPrameela Rani Garnepudi } 956507de6dSPrameela Rani Garnepudi skb_push(skb, dword_align_bytes); 966507de6dSPrameela Rani Garnepudi header_size += dword_align_bytes; 976507de6dSPrameela Rani Garnepudi 986507de6dSPrameela Rani Garnepudi tx_params->internal_hdr_size = header_size; 996507de6dSPrameela Rani Garnepudi memset(&skb->data[0], 0, header_size); 1006507de6dSPrameela Rani Garnepudi wh = (struct ieee80211_hdr *)&skb->data[header_size]; 1016507de6dSPrameela Rani Garnepudi 1026507de6dSPrameela Rani Garnepudi mgmt_desc = (struct rsi_mgmt_desc *)skb->data; 1035dc36387SPrameela Rani Garnepudi xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 1046507de6dSPrameela Rani Garnepudi 1056507de6dSPrameela Rani Garnepudi rsi_set_len_qno(&mgmt_desc->len_qno, (skb->len - FRAME_DESC_SZ), 1066507de6dSPrameela Rani Garnepudi RSI_WIFI_MGMT_Q); 1076507de6dSPrameela Rani Garnepudi mgmt_desc->frame_type = TX_DOT11_MGMT; 1086507de6dSPrameela Rani Garnepudi mgmt_desc->header_len = MIN_802_11_HDR_LEN; 1096507de6dSPrameela Rani Garnepudi mgmt_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; 1107fdcb8e1SPrameela Rani Garnepudi 1117fdcb8e1SPrameela Rani Garnepudi if (ieee80211_is_probe_req(wh->frame_control)) 1127fdcb8e1SPrameela Rani Garnepudi mgmt_desc->frame_info = cpu_to_le16(RSI_INSERT_SEQ_IN_FW); 1136507de6dSPrameela Rani Garnepudi mgmt_desc->frame_info |= cpu_to_le16(RATE_INFO_ENABLE); 1146507de6dSPrameela Rani Garnepudi if (is_broadcast_ether_addr(wh->addr1)) 1156507de6dSPrameela Rani Garnepudi mgmt_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); 1166507de6dSPrameela Rani Garnepudi 1176507de6dSPrameela Rani Garnepudi mgmt_desc->seq_ctrl = 1186507de6dSPrameela Rani Garnepudi cpu_to_le16(IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl))); 1194671c209SPrameela Rani Garnepudi if ((common->band == NL80211_BAND_2GHZ) && !common->p2p_enabled) 1204671c209SPrameela Rani Garnepudi mgmt_desc->rate_info = cpu_to_le16(RSI_RATE_1); 1216507de6dSPrameela Rani Garnepudi else 1224671c209SPrameela Rani Garnepudi mgmt_desc->rate_info = cpu_to_le16(RSI_RATE_6); 1236507de6dSPrameela Rani Garnepudi 1246507de6dSPrameela Rani Garnepudi if (conf_is_ht40(conf)) 1256507de6dSPrameela Rani Garnepudi mgmt_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); 1266507de6dSPrameela Rani Garnepudi 12719844c0aSPrameela Rani Garnepudi if (ieee80211_is_probe_resp(wh->frame_control)) { 12819844c0aSPrameela Rani Garnepudi mgmt_desc->misc_flags |= (RSI_ADD_DELTA_TSF_VAP_ID | 12919844c0aSPrameela Rani Garnepudi RSI_FETCH_RETRY_CNT_FRM_HST); 13019844c0aSPrameela Rani Garnepudi #define PROBE_RESP_RETRY_CNT 3 13119844c0aSPrameela Rani Garnepudi xtend_desc->retry_cnt = PROBE_RESP_RETRY_CNT; 13219844c0aSPrameela Rani Garnepudi } 13319844c0aSPrameela Rani Garnepudi 1344671c209SPrameela Rani Garnepudi if (((vif->type == NL80211_IFTYPE_AP) || 1354671c209SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_GO)) && 13619844c0aSPrameela Rani Garnepudi (ieee80211_is_action(wh->frame_control))) { 13719844c0aSPrameela Rani Garnepudi struct rsi_sta *rsta = rsi_find_sta(common, wh->addr1); 13819844c0aSPrameela Rani Garnepudi 13919844c0aSPrameela Rani Garnepudi if (rsta) 14019844c0aSPrameela Rani Garnepudi mgmt_desc->sta_id = tx_params->sta_id; 14119844c0aSPrameela Rani Garnepudi else 14219844c0aSPrameela Rani Garnepudi return -EINVAL; 14319844c0aSPrameela Rani Garnepudi } 1444671c209SPrameela Rani Garnepudi mgmt_desc->rate_info |= 1454671c209SPrameela Rani Garnepudi cpu_to_le16((tx_params->vap_id << RSI_DESC_VAP_ID_OFST) & 1464671c209SPrameela Rani Garnepudi RSI_DESC_VAP_ID_MASK); 1474671c209SPrameela Rani Garnepudi 1486507de6dSPrameela Rani Garnepudi return 0; 1496507de6dSPrameela Rani Garnepudi } 1506507de6dSPrameela Rani Garnepudi 151ceb2e4eaSPavani Muthyala /* This function prepares descriptor for given data packet */ 1521be05eb5SPrameela Rani Garnepudi int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) 153dad0d04fSFariya Fatima { 154ce86893fSKarun Eagalapati struct rsi_hw *adapter = common->priv; 155ce86893fSKarun Eagalapati struct ieee80211_vif *vif; 1560eb42586SPavani Muthyala struct ieee80211_hdr *wh = NULL; 157dad0d04fSFariya Fatima struct ieee80211_tx_info *info; 158dad0d04fSFariya Fatima struct skb_info *tx_params; 159af193097SPavani Muthyala struct rsi_data_desc *data_desc; 1605dc36387SPrameela Rani Garnepudi struct rsi_xtended_desc *xtend_desc; 161dad0d04fSFariya Fatima u8 ieee80211_size = MIN_802_11_HDR_LEN; 1620eb42586SPavani Muthyala u8 header_size; 1630eb42586SPavani Muthyala u8 vap_id = 0; 1640eb42586SPavani Muthyala u8 dword_align_bytes; 165ab2ef1d6SMarkus Elfring u16 seq_num; 166dad0d04fSFariya Fatima 167dad0d04fSFariya Fatima info = IEEE80211_SKB_CB(skb); 168eac4eed3SPrameela Rani Garnepudi vif = info->control.vif; 169dad0d04fSFariya Fatima tx_params = (struct skb_info *)info->driver_data; 170dad0d04fSFariya Fatima 1715dc36387SPrameela Rani Garnepudi header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc); 1720eb42586SPavani Muthyala if (header_size > skb_headroom(skb)) { 173dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); 174ceb2e4eaSPavani Muthyala return -ENOSPC; 175dad0d04fSFariya Fatima } 1760eb42586SPavani Muthyala skb_push(skb, header_size); 1770eb42586SPavani Muthyala dword_align_bytes = ((unsigned long)skb->data & 0x3f); 1780eb42586SPavani Muthyala if (header_size > skb_headroom(skb)) { 1790eb42586SPavani Muthyala rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__); 180ceb2e4eaSPavani Muthyala return -ENOSPC; 1810eb42586SPavani Muthyala } 1820eb42586SPavani Muthyala skb_push(skb, dword_align_bytes); 1830eb42586SPavani Muthyala header_size += dword_align_bytes; 184dad0d04fSFariya Fatima 1850eb42586SPavani Muthyala tx_params->internal_hdr_size = header_size; 186af193097SPavani Muthyala data_desc = (struct rsi_data_desc *)skb->data; 1870eb42586SPavani Muthyala memset(data_desc, 0, header_size); 188dad0d04fSFariya Fatima 1895dc36387SPrameela Rani Garnepudi xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 1900eb42586SPavani Muthyala wh = (struct ieee80211_hdr *)&skb->data[header_size]; 19119844c0aSPrameela Rani Garnepudi seq_num = IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl)); 1920eb42586SPavani Muthyala 1930eb42586SPavani Muthyala data_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; 1940eb42586SPavani Muthyala 1950eb42586SPavani Muthyala if (ieee80211_is_data_qos(wh->frame_control)) { 196dad0d04fSFariya Fatima ieee80211_size += 2; 197af193097SPavani Muthyala data_desc->mac_flags |= cpu_to_le16(RSI_QOS_ENABLE); 198dad0d04fSFariya Fatima } 199dad0d04fSFariya Fatima 200eac4eed3SPrameela Rani Garnepudi if (((vif->type == NL80211_IFTYPE_STATION) || 201eac4eed3SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_CLIENT)) && 202ce86893fSKarun Eagalapati (adapter->ps_state == PS_ENABLED)) 203ce86893fSKarun Eagalapati wh->frame_control |= cpu_to_le16(RSI_SET_PS_ENABLE); 204ce86893fSKarun Eagalapati 205dad0d04fSFariya Fatima if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) && 206*31453804SMartin Fuzzey info->control.hw_key) { 207dad0d04fSFariya Fatima if (rsi_is_cipher_wep(common)) 208dad0d04fSFariya Fatima ieee80211_size += 4; 209dad0d04fSFariya Fatima else 210dad0d04fSFariya Fatima ieee80211_size += 8; 211af193097SPavani Muthyala data_desc->mac_flags |= cpu_to_le16(RSI_ENCRYPT_PKT); 212dad0d04fSFariya Fatima } 213af193097SPavani Muthyala rsi_set_len_qno(&data_desc->len_qno, (skb->len - FRAME_DESC_SZ), 214af193097SPavani Muthyala RSI_WIFI_DATA_Q); 215af193097SPavani Muthyala data_desc->header_len = ieee80211_size; 216dad0d04fSFariya Fatima 217af193097SPavani Muthyala if (common->min_rate != RSI_RATE_AUTO) { 218dad0d04fSFariya Fatima /* Send fixed rate */ 219af193097SPavani Muthyala data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 220af193097SPavani Muthyala data_desc->rate_info = cpu_to_le16(common->min_rate); 221e8c58e7aSJahnavi Meher 222e8c58e7aSJahnavi Meher if (conf_is_ht40(&common->priv->hw->conf)) 223af193097SPavani Muthyala data_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); 224e8c58e7aSJahnavi Meher 22519844c0aSPrameela Rani Garnepudi if ((common->vif_info[0].sgi) && (common->min_rate & 0x100)) { 22619844c0aSPrameela Rani Garnepudi /* Only MCS rates */ 227af193097SPavani Muthyala data_desc->rate_info |= 2282bfa6969SJahnavi Meher cpu_to_le16(ENABLE_SHORTGI_RATE); 2292bfa6969SJahnavi Meher } 2300eb42586SPavani Muthyala } 2310eb42586SPavani Muthyala 2320eb42586SPavani Muthyala if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { 2330eb42586SPavani Muthyala rsi_dbg(INFO_ZONE, "*** Tx EAPOL ***\n"); 2340eb42586SPavani Muthyala 2350eb42586SPavani Muthyala data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 2360eb42586SPavani Muthyala if (common->band == NL80211_BAND_5GHZ) 2370eb42586SPavani Muthyala data_desc->rate_info = cpu_to_le16(RSI_RATE_6); 2380eb42586SPavani Muthyala else 2390eb42586SPavani Muthyala data_desc->rate_info = cpu_to_le16(RSI_RATE_1); 2400eb42586SPavani Muthyala data_desc->mac_flags |= cpu_to_le16(RSI_REKEY_PURPOSE); 2410eb42586SPavani Muthyala data_desc->misc_flags |= RSI_FETCH_RETRY_CNT_FRM_HST; 2420eb42586SPavani Muthyala #define EAPOL_RETRY_CNT 15 2430eb42586SPavani Muthyala xtend_desc->retry_cnt = EAPOL_RETRY_CNT; 2444fd6c476SPrameela Rani Garnepudi 2454fd6c476SPrameela Rani Garnepudi if (common->eapol4_confirm) 2464fd6c476SPrameela Rani Garnepudi skb->priority = VO_Q; 2474fd6c476SPrameela Rani Garnepudi else 2484fd6c476SPrameela Rani Garnepudi rsi_set_len_qno(&data_desc->len_qno, 2494fd6c476SPrameela Rani Garnepudi (skb->len - FRAME_DESC_SZ), 2504fd6c476SPrameela Rani Garnepudi RSI_WIFI_MGMT_Q); 25165277100SMarek Vasut if (((skb->len - header_size) == EAPOL4_PACKET_LEN) || 25265277100SMarek Vasut ((skb->len - header_size) == EAPOL4_PACKET_LEN - 2)) { 2534fd6c476SPrameela Rani Garnepudi data_desc->misc_flags |= 2544fd6c476SPrameela Rani Garnepudi RSI_DESC_REQUIRE_CFM_TO_HOST; 2554fd6c476SPrameela Rani Garnepudi xtend_desc->confirm_frame_type = EAPOL4_CONFIRM; 2564fd6c476SPrameela Rani Garnepudi } 257dad0d04fSFariya Fatima } 258dad0d04fSFariya Fatima 25992e97123SSiva Rebbagondla data_desc->mac_flags |= cpu_to_le16(seq_num & 0xfff); 260af193097SPavani Muthyala data_desc->qid_tid = ((skb->priority & 0xf) | 261af193097SPavani Muthyala ((tx_params->tid & 0xf) << 4)); 262af193097SPavani Muthyala data_desc->sta_id = tx_params->sta_id; 263dad0d04fSFariya Fatima 2640eb42586SPavani Muthyala if ((is_broadcast_ether_addr(wh->addr1)) || 2650eb42586SPavani Muthyala (is_multicast_ether_addr(wh->addr1))) { 2660eb42586SPavani Muthyala data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 2670eb42586SPavani Muthyala data_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); 2680eb42586SPavani Muthyala data_desc->sta_id = vap_id; 26919844c0aSPrameela Rani Garnepudi 270eac4eed3SPrameela Rani Garnepudi if ((vif->type == NL80211_IFTYPE_AP) || 271eac4eed3SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_GO)) { 27219844c0aSPrameela Rani Garnepudi if (common->band == NL80211_BAND_5GHZ) 27319844c0aSPrameela Rani Garnepudi data_desc->rate_info = cpu_to_le16(RSI_RATE_6); 27419844c0aSPrameela Rani Garnepudi else 27519844c0aSPrameela Rani Garnepudi data_desc->rate_info = cpu_to_le16(RSI_RATE_1); 2760eb42586SPavani Muthyala } 27719844c0aSPrameela Rani Garnepudi } 278eac4eed3SPrameela Rani Garnepudi if (((vif->type == NL80211_IFTYPE_AP) || 279eac4eed3SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_GO)) && 28019844c0aSPrameela Rani Garnepudi (ieee80211_has_moredata(wh->frame_control))) 28119844c0aSPrameela Rani Garnepudi data_desc->frame_info |= cpu_to_le16(MORE_DATA_PRESENT); 2820eb42586SPavani Muthyala 283eac4eed3SPrameela Rani Garnepudi data_desc->rate_info |= 284eac4eed3SPrameela Rani Garnepudi cpu_to_le16((tx_params->vap_id << RSI_DESC_VAP_ID_OFST) & 285eac4eed3SPrameela Rani Garnepudi RSI_DESC_VAP_ID_MASK); 286eac4eed3SPrameela Rani Garnepudi 287ceb2e4eaSPavani Muthyala return 0; 288ceb2e4eaSPavani Muthyala } 289ceb2e4eaSPavani Muthyala 290ceb2e4eaSPavani Muthyala /* This function sends received data packet from driver to device */ 291ceb2e4eaSPavani Muthyala int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) 292ceb2e4eaSPavani Muthyala { 293ceb2e4eaSPavani Muthyala struct rsi_hw *adapter = common->priv; 294eac4eed3SPrameela Rani Garnepudi struct ieee80211_vif *vif; 295ceb2e4eaSPavani Muthyala struct ieee80211_tx_info *info; 296ceb2e4eaSPavani Muthyala struct ieee80211_bss_conf *bss; 29719844c0aSPrameela Rani Garnepudi int status = -EINVAL; 29819844c0aSPrameela Rani Garnepudi 29919844c0aSPrameela Rani Garnepudi if (!skb) 30019844c0aSPrameela Rani Garnepudi return 0; 30119844c0aSPrameela Rani Garnepudi if (common->iface_down) 30219844c0aSPrameela Rani Garnepudi goto err; 303ceb2e4eaSPavani Muthyala 304ceb2e4eaSPavani Muthyala info = IEEE80211_SKB_CB(skb); 30519844c0aSPrameela Rani Garnepudi if (!info->control.vif) 30619844c0aSPrameela Rani Garnepudi goto err; 307eac4eed3SPrameela Rani Garnepudi vif = info->control.vif; 308eac4eed3SPrameela Rani Garnepudi bss = &vif->bss_conf; 309ceb2e4eaSPavani Muthyala 310eac4eed3SPrameela Rani Garnepudi if (((vif->type == NL80211_IFTYPE_STATION) || 311eac4eed3SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_CLIENT)) && 312eac4eed3SPrameela Rani Garnepudi (!bss->assoc)) 313ceb2e4eaSPavani Muthyala goto err; 314ceb2e4eaSPavani Muthyala 3152108df3cSPrameela Rani Garnepudi status = rsi_send_pkt_to_bus(common, skb); 316dad0d04fSFariya Fatima if (status) 317ceb2e4eaSPavani Muthyala rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", __func__); 318dad0d04fSFariya Fatima 319dad0d04fSFariya Fatima err: 320dad0d04fSFariya Fatima ++common->tx_stats.total_tx_pkt_freed[skb->priority]; 321af193097SPavani Muthyala rsi_indicate_tx_status(adapter, skb, status); 322dad0d04fSFariya Fatima return status; 323dad0d04fSFariya Fatima } 324dad0d04fSFariya Fatima 325dad0d04fSFariya Fatima /** 326dad0d04fSFariya Fatima * rsi_send_mgmt_pkt() - This functions sends the received management packet 327dad0d04fSFariya Fatima * from driver to device. 328dad0d04fSFariya Fatima * @common: Pointer to the driver private structure. 329dad0d04fSFariya Fatima * @skb: Pointer to the socket buffer structure. 330dad0d04fSFariya Fatima * 331dad0d04fSFariya Fatima * Return: status: 0 on success, -1 on failure. 332dad0d04fSFariya Fatima */ 333dad0d04fSFariya Fatima int rsi_send_mgmt_pkt(struct rsi_common *common, 334dad0d04fSFariya Fatima struct sk_buff *skb) 335dad0d04fSFariya Fatima { 336dad0d04fSFariya Fatima struct rsi_hw *adapter = common->priv; 3371be05eb5SPrameela Rani Garnepudi struct ieee80211_bss_conf *bss; 3381be05eb5SPrameela Rani Garnepudi struct ieee80211_hdr *wh; 339dad0d04fSFariya Fatima struct ieee80211_tx_info *info; 340dad0d04fSFariya Fatima struct skb_info *tx_params; 3411be05eb5SPrameela Rani Garnepudi struct rsi_mgmt_desc *mgmt_desc; 3421be05eb5SPrameela Rani Garnepudi struct rsi_xtended_desc *xtend_desc; 343dad0d04fSFariya Fatima int status = -E2BIG; 3441be05eb5SPrameela Rani Garnepudi u8 header_size; 345dad0d04fSFariya Fatima 346dad0d04fSFariya Fatima info = IEEE80211_SKB_CB(skb); 347dad0d04fSFariya Fatima tx_params = (struct skb_info *)info->driver_data; 3481be05eb5SPrameela Rani Garnepudi header_size = tx_params->internal_hdr_size; 349dad0d04fSFariya Fatima 350dad0d04fSFariya Fatima if (tx_params->flags & INTERNAL_MGMT_PKT) { 351a2ce952cSPrameela Rani Garnepudi status = adapter->host_intf_ops->write_pkt(common->priv, 352dad0d04fSFariya Fatima (u8 *)skb->data, 353dad0d04fSFariya Fatima skb->len); 354dad0d04fSFariya Fatima if (status) { 355dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, 356dad0d04fSFariya Fatima "%s: Failed to write the packet\n", __func__); 357dad0d04fSFariya Fatima } 358dad0d04fSFariya Fatima dev_kfree_skb(skb); 359dad0d04fSFariya Fatima return status; 360dad0d04fSFariya Fatima } 361dad0d04fSFariya Fatima 3621be05eb5SPrameela Rani Garnepudi bss = &info->control.vif->bss_conf; 3631be05eb5SPrameela Rani Garnepudi wh = (struct ieee80211_hdr *)&skb->data[header_size]; 3641be05eb5SPrameela Rani Garnepudi mgmt_desc = (struct rsi_mgmt_desc *)skb->data; 3651be05eb5SPrameela Rani Garnepudi xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 366dad0d04fSFariya Fatima 3671be05eb5SPrameela Rani Garnepudi /* Indicate to firmware to give cfm for probe */ 3681be05eb5SPrameela Rani Garnepudi if (ieee80211_is_probe_req(wh->frame_control) && !bss->assoc) { 3691be05eb5SPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 3701be05eb5SPrameela Rani Garnepudi "%s: blocking mgmt queue\n", __func__); 3711be05eb5SPrameela Rani Garnepudi mgmt_desc->misc_flags = RSI_DESC_REQUIRE_CFM_TO_HOST; 3721be05eb5SPrameela Rani Garnepudi xtend_desc->confirm_frame_type = PROBEREQ_CONFIRM; 3731be05eb5SPrameela Rani Garnepudi common->mgmt_q_block = true; 3741be05eb5SPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Mgmt queue blocked\n"); 3751be05eb5SPrameela Rani Garnepudi } 3761be05eb5SPrameela Rani Garnepudi 3772108df3cSPrameela Rani Garnepudi status = rsi_send_pkt_to_bus(common, skb); 378dad0d04fSFariya Fatima if (status) 379dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__); 380dad0d04fSFariya Fatima 381dad0d04fSFariya Fatima rsi_indicate_tx_status(common->priv, skb, status); 382dad0d04fSFariya Fatima return status; 383dad0d04fSFariya Fatima } 384b78e91bcSPrameela Rani Garnepudi 385716b840cSSiva Rebbagondla int rsi_send_bt_pkt(struct rsi_common *common, struct sk_buff *skb) 386716b840cSSiva Rebbagondla { 387716b840cSSiva Rebbagondla int status = -EINVAL; 388716b840cSSiva Rebbagondla u8 header_size = 0; 389716b840cSSiva Rebbagondla struct rsi_bt_desc *bt_desc; 390716b840cSSiva Rebbagondla u8 queueno = ((skb->data[1] >> 4) & 0xf); 391716b840cSSiva Rebbagondla 392716b840cSSiva Rebbagondla if (queueno == RSI_BT_MGMT_Q) { 393716b840cSSiva Rebbagondla status = rsi_send_pkt_to_bus(common, skb); 394716b840cSSiva Rebbagondla if (status) 395716b840cSSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Failed to write bt mgmt pkt\n", 396716b840cSSiva Rebbagondla __func__); 397716b840cSSiva Rebbagondla goto out; 398716b840cSSiva Rebbagondla } 399716b840cSSiva Rebbagondla header_size = FRAME_DESC_SZ; 400716b840cSSiva Rebbagondla if (header_size > skb_headroom(skb)) { 401716b840cSSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__); 402716b840cSSiva Rebbagondla status = -ENOSPC; 403716b840cSSiva Rebbagondla goto out; 404716b840cSSiva Rebbagondla } 405716b840cSSiva Rebbagondla skb_push(skb, header_size); 406716b840cSSiva Rebbagondla memset(skb->data, 0, header_size); 407716b840cSSiva Rebbagondla bt_desc = (struct rsi_bt_desc *)skb->data; 408716b840cSSiva Rebbagondla 409716b840cSSiva Rebbagondla rsi_set_len_qno(&bt_desc->len_qno, (skb->len - FRAME_DESC_SZ), 410716b840cSSiva Rebbagondla RSI_BT_DATA_Q); 411716b840cSSiva Rebbagondla bt_desc->bt_pkt_type = cpu_to_le16(bt_cb(skb)->pkt_type); 412716b840cSSiva Rebbagondla 413716b840cSSiva Rebbagondla status = rsi_send_pkt_to_bus(common, skb); 414716b840cSSiva Rebbagondla if (status) 415716b840cSSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Failed to write bt pkt\n", __func__); 416716b840cSSiva Rebbagondla 417716b840cSSiva Rebbagondla out: 418716b840cSSiva Rebbagondla dev_kfree_skb(skb); 419716b840cSSiva Rebbagondla return status; 420716b840cSSiva Rebbagondla } 421716b840cSSiva Rebbagondla 422d26a9559SPrameela Rani Garnepudi int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb) 423d26a9559SPrameela Rani Garnepudi { 424d26a9559SPrameela Rani Garnepudi struct rsi_hw *adapter = (struct rsi_hw *)common->priv; 425d26a9559SPrameela Rani Garnepudi struct rsi_data_desc *bcn_frm; 426d26a9559SPrameela Rani Garnepudi struct ieee80211_hw *hw = common->priv->hw; 427d26a9559SPrameela Rani Garnepudi struct ieee80211_conf *conf = &hw->conf; 4284671c209SPrameela Rani Garnepudi struct ieee80211_vif *vif; 429d26a9559SPrameela Rani Garnepudi struct sk_buff *mac_bcn; 4304671c209SPrameela Rani Garnepudi u8 vap_id = 0, i; 4314671c209SPrameela Rani Garnepudi u16 tim_offset = 0; 432d26a9559SPrameela Rani Garnepudi 4334671c209SPrameela Rani Garnepudi for (i = 0; i < RSI_MAX_VIFS; i++) { 4344671c209SPrameela Rani Garnepudi vif = adapter->vifs[i]; 4354671c209SPrameela Rani Garnepudi if (!vif) 4364671c209SPrameela Rani Garnepudi continue; 4374671c209SPrameela Rani Garnepudi if ((vif->type == NL80211_IFTYPE_AP) || 4384671c209SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_GO)) 4394671c209SPrameela Rani Garnepudi break; 4404671c209SPrameela Rani Garnepudi } 4414671c209SPrameela Rani Garnepudi if (!vif) 4424671c209SPrameela Rani Garnepudi return -EINVAL; 443d26a9559SPrameela Rani Garnepudi mac_bcn = ieee80211_beacon_get_tim(adapter->hw, 4444671c209SPrameela Rani Garnepudi vif, 445d26a9559SPrameela Rani Garnepudi &tim_offset, NULL); 446d26a9559SPrameela Rani Garnepudi if (!mac_bcn) { 447d26a9559SPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Failed to get beacon from mac80211\n"); 448d26a9559SPrameela Rani Garnepudi return -EINVAL; 449d26a9559SPrameela Rani Garnepudi } 450d26a9559SPrameela Rani Garnepudi 451d26a9559SPrameela Rani Garnepudi common->beacon_cnt++; 452d26a9559SPrameela Rani Garnepudi bcn_frm = (struct rsi_data_desc *)skb->data; 453d26a9559SPrameela Rani Garnepudi rsi_set_len_qno(&bcn_frm->len_qno, mac_bcn->len, RSI_WIFI_DATA_Q); 454d26a9559SPrameela Rani Garnepudi bcn_frm->header_len = MIN_802_11_HDR_LEN; 455d26a9559SPrameela Rani Garnepudi bcn_frm->frame_info = cpu_to_le16(RSI_DATA_DESC_MAC_BBP_INFO | 456d26a9559SPrameela Rani Garnepudi RSI_DATA_DESC_NO_ACK_IND | 457d26a9559SPrameela Rani Garnepudi RSI_DATA_DESC_BEACON_FRAME | 458d26a9559SPrameela Rani Garnepudi RSI_DATA_DESC_INSERT_TSF | 459d26a9559SPrameela Rani Garnepudi RSI_DATA_DESC_INSERT_SEQ_NO | 460d26a9559SPrameela Rani Garnepudi RATE_INFO_ENABLE); 461d26a9559SPrameela Rani Garnepudi bcn_frm->rate_info = cpu_to_le16(vap_id << 14); 462d26a9559SPrameela Rani Garnepudi bcn_frm->qid_tid = BEACON_HW_Q; 463d26a9559SPrameela Rani Garnepudi 464d26a9559SPrameela Rani Garnepudi if (conf_is_ht40_plus(conf)) { 465d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info = cpu_to_le16(LOWER_20_ENABLE); 466d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info |= cpu_to_le16(LOWER_20_ENABLE >> 12); 467d26a9559SPrameela Rani Garnepudi } else if (conf_is_ht40_minus(conf)) { 468d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info = cpu_to_le16(UPPER_20_ENABLE); 469d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info |= cpu_to_le16(UPPER_20_ENABLE >> 12); 470d26a9559SPrameela Rani Garnepudi } 471d26a9559SPrameela Rani Garnepudi 472d26a9559SPrameela Rani Garnepudi if (common->band == NL80211_BAND_2GHZ) 473b1c3a248SMarek Vasut bcn_frm->rate_info |= cpu_to_le16(RSI_RATE_1); 474d26a9559SPrameela Rani Garnepudi else 475b1c3a248SMarek Vasut bcn_frm->rate_info |= cpu_to_le16(RSI_RATE_6); 476d26a9559SPrameela Rani Garnepudi 477d26a9559SPrameela Rani Garnepudi if (mac_bcn->data[tim_offset + 2] == 0) 478d26a9559SPrameela Rani Garnepudi bcn_frm->frame_info |= cpu_to_le16(RSI_DATA_DESC_DTIM_BEACON); 479d26a9559SPrameela Rani Garnepudi 480d26a9559SPrameela Rani Garnepudi memcpy(&skb->data[FRAME_DESC_SZ], mac_bcn->data, mac_bcn->len); 481d26a9559SPrameela Rani Garnepudi skb_put(skb, mac_bcn->len + FRAME_DESC_SZ); 482d26a9559SPrameela Rani Garnepudi 483d26a9559SPrameela Rani Garnepudi dev_kfree_skb(mac_bcn); 484d26a9559SPrameela Rani Garnepudi 485d26a9559SPrameela Rani Garnepudi return 0; 486d26a9559SPrameela Rani Garnepudi } 487d26a9559SPrameela Rani Garnepudi 488dfefb9f8SKees Cook static void bl_cmd_timeout(struct timer_list *t) 489b78e91bcSPrameela Rani Garnepudi { 490dfefb9f8SKees Cook struct rsi_hw *adapter = from_timer(adapter, t, bl_cmd_timer); 491b78e91bcSPrameela Rani Garnepudi 492b78e91bcSPrameela Rani Garnepudi adapter->blcmd_timer_expired = true; 493b78e91bcSPrameela Rani Garnepudi del_timer(&adapter->bl_cmd_timer); 494b78e91bcSPrameela Rani Garnepudi } 495b78e91bcSPrameela Rani Garnepudi 496b78e91bcSPrameela Rani Garnepudi static int bl_start_cmd_timer(struct rsi_hw *adapter, u32 timeout) 497b78e91bcSPrameela Rani Garnepudi { 498dfefb9f8SKees Cook timer_setup(&adapter->bl_cmd_timer, bl_cmd_timeout, 0); 499b78e91bcSPrameela Rani Garnepudi adapter->bl_cmd_timer.expires = (msecs_to_jiffies(timeout) + jiffies); 500b78e91bcSPrameela Rani Garnepudi 501b78e91bcSPrameela Rani Garnepudi adapter->blcmd_timer_expired = false; 502b78e91bcSPrameela Rani Garnepudi add_timer(&adapter->bl_cmd_timer); 503b78e91bcSPrameela Rani Garnepudi 504b78e91bcSPrameela Rani Garnepudi return 0; 505b78e91bcSPrameela Rani Garnepudi } 506b78e91bcSPrameela Rani Garnepudi 507b78e91bcSPrameela Rani Garnepudi static int bl_stop_cmd_timer(struct rsi_hw *adapter) 508b78e91bcSPrameela Rani Garnepudi { 509b78e91bcSPrameela Rani Garnepudi adapter->blcmd_timer_expired = false; 510b78e91bcSPrameela Rani Garnepudi if (timer_pending(&adapter->bl_cmd_timer)) 511b78e91bcSPrameela Rani Garnepudi del_timer(&adapter->bl_cmd_timer); 512b78e91bcSPrameela Rani Garnepudi 513b78e91bcSPrameela Rani Garnepudi return 0; 514b78e91bcSPrameela Rani Garnepudi } 515b78e91bcSPrameela Rani Garnepudi 516b78e91bcSPrameela Rani Garnepudi static int bl_write_cmd(struct rsi_hw *adapter, u8 cmd, u8 exp_resp, 517b78e91bcSPrameela Rani Garnepudi u16 *cmd_resp) 518b78e91bcSPrameela Rani Garnepudi { 519b78e91bcSPrameela Rani Garnepudi struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 520b78e91bcSPrameela Rani Garnepudi u32 regin_val = 0, regout_val = 0; 521b78e91bcSPrameela Rani Garnepudi u32 regin_input = 0; 522b78e91bcSPrameela Rani Garnepudi u8 output = 0; 523b78e91bcSPrameela Rani Garnepudi int status; 524b78e91bcSPrameela Rani Garnepudi 525b78e91bcSPrameela Rani Garnepudi regin_input = (REGIN_INPUT | adapter->priv->coex_mode); 526b78e91bcSPrameela Rani Garnepudi 527b78e91bcSPrameela Rani Garnepudi while (!adapter->blcmd_timer_expired) { 528b78e91bcSPrameela Rani Garnepudi regin_val = 0; 529b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_read(adapter, SWBL_REGIN, 530b78e91bcSPrameela Rani Garnepudi ®in_val, 2); 531b78e91bcSPrameela Rani Garnepudi if (status < 0) { 532b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 533b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGIN reading failed..\n", 534b78e91bcSPrameela Rani Garnepudi __func__, cmd); 535b78e91bcSPrameela Rani Garnepudi return status; 536b78e91bcSPrameela Rani Garnepudi } 537b78e91bcSPrameela Rani Garnepudi mdelay(1); 538b78e91bcSPrameela Rani Garnepudi if ((regin_val >> 12) != REGIN_VALID) 539b78e91bcSPrameela Rani Garnepudi break; 540b78e91bcSPrameela Rani Garnepudi } 541b78e91bcSPrameela Rani Garnepudi if (adapter->blcmd_timer_expired) { 542b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 543b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGIN reading timed out..\n", 544b78e91bcSPrameela Rani Garnepudi __func__, cmd); 545b78e91bcSPrameela Rani Garnepudi return -ETIMEDOUT; 546b78e91bcSPrameela Rani Garnepudi } 547b78e91bcSPrameela Rani Garnepudi 548b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 549b78e91bcSPrameela Rani Garnepudi "Issuing write to Regin val:%0x sending cmd:%0x\n", 550b78e91bcSPrameela Rani Garnepudi regin_val, (cmd | regin_input << 8)); 551b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_write(adapter, SWBL_REGIN, 552b78e91bcSPrameela Rani Garnepudi (cmd | regin_input << 8), 2); 553b78e91bcSPrameela Rani Garnepudi if (status < 0) 554b78e91bcSPrameela Rani Garnepudi return status; 555b78e91bcSPrameela Rani Garnepudi mdelay(1); 556b78e91bcSPrameela Rani Garnepudi 557b78e91bcSPrameela Rani Garnepudi if (cmd == LOAD_HOSTED_FW || cmd == JUMP_TO_ZERO_PC) { 558b78e91bcSPrameela Rani Garnepudi /* JUMP_TO_ZERO_PC doesn't expect 559b78e91bcSPrameela Rani Garnepudi * any response. So return from here 560b78e91bcSPrameela Rani Garnepudi */ 561b78e91bcSPrameela Rani Garnepudi return 0; 562b78e91bcSPrameela Rani Garnepudi } 563b78e91bcSPrameela Rani Garnepudi 564b78e91bcSPrameela Rani Garnepudi while (!adapter->blcmd_timer_expired) { 565b78e91bcSPrameela Rani Garnepudi regout_val = 0; 566b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_read(adapter, SWBL_REGOUT, 567b78e91bcSPrameela Rani Garnepudi ®out_val, 2); 568b78e91bcSPrameela Rani Garnepudi if (status < 0) { 569b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 570b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGOUT reading failed..\n", 571b78e91bcSPrameela Rani Garnepudi __func__, cmd); 572b78e91bcSPrameela Rani Garnepudi return status; 573b78e91bcSPrameela Rani Garnepudi } 574b78e91bcSPrameela Rani Garnepudi mdelay(1); 575b78e91bcSPrameela Rani Garnepudi if ((regout_val >> 8) == REGOUT_VALID) 576b78e91bcSPrameela Rani Garnepudi break; 577b78e91bcSPrameela Rani Garnepudi } 578b78e91bcSPrameela Rani Garnepudi if (adapter->blcmd_timer_expired) { 579b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 580b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGOUT reading timed out..\n", 581b78e91bcSPrameela Rani Garnepudi __func__, cmd); 582b78e91bcSPrameela Rani Garnepudi return status; 583b78e91bcSPrameela Rani Garnepudi } 584b78e91bcSPrameela Rani Garnepudi 585b78e91bcSPrameela Rani Garnepudi *cmd_resp = ((u16 *)®out_val)[0] & 0xffff; 586b78e91bcSPrameela Rani Garnepudi 587b78e91bcSPrameela Rani Garnepudi output = ((u8 *)®out_val)[0] & 0xff; 588b78e91bcSPrameela Rani Garnepudi 589b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_write(adapter, SWBL_REGOUT, 590b78e91bcSPrameela Rani Garnepudi (cmd | REGOUT_INVALID << 8), 2); 591b78e91bcSPrameela Rani Garnepudi if (status < 0) { 592b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 593b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGOUT writing failed..\n", 594b78e91bcSPrameela Rani Garnepudi __func__, cmd); 595b78e91bcSPrameela Rani Garnepudi return status; 596b78e91bcSPrameela Rani Garnepudi } 597b78e91bcSPrameela Rani Garnepudi mdelay(1); 598b78e91bcSPrameela Rani Garnepudi 599b78e91bcSPrameela Rani Garnepudi if (output != exp_resp) { 600b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 601b78e91bcSPrameela Rani Garnepudi "%s: Recvd resp %x for cmd %0x\n", 602b78e91bcSPrameela Rani Garnepudi __func__, output, cmd); 603b78e91bcSPrameela Rani Garnepudi return -EINVAL; 604b78e91bcSPrameela Rani Garnepudi } 605b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 606b78e91bcSPrameela Rani Garnepudi "%s: Recvd Expected resp %x for cmd %0x\n", 607b78e91bcSPrameela Rani Garnepudi __func__, output, cmd); 608b78e91bcSPrameela Rani Garnepudi 609b78e91bcSPrameela Rani Garnepudi return 0; 610b78e91bcSPrameela Rani Garnepudi } 611b78e91bcSPrameela Rani Garnepudi 612b78e91bcSPrameela Rani Garnepudi static int bl_cmd(struct rsi_hw *adapter, u8 cmd, u8 exp_resp, char *str) 613b78e91bcSPrameela Rani Garnepudi { 614b78e91bcSPrameela Rani Garnepudi u16 regout_val = 0; 615b78e91bcSPrameela Rani Garnepudi u32 timeout; 616b78e91bcSPrameela Rani Garnepudi int status; 617b78e91bcSPrameela Rani Garnepudi 618b78e91bcSPrameela Rani Garnepudi if ((cmd == EOF_REACHED) || (cmd == PING_VALID) || (cmd == PONG_VALID)) 619b78e91bcSPrameela Rani Garnepudi timeout = BL_BURN_TIMEOUT; 620b78e91bcSPrameela Rani Garnepudi else 621b78e91bcSPrameela Rani Garnepudi timeout = BL_CMD_TIMEOUT; 622b78e91bcSPrameela Rani Garnepudi 623b78e91bcSPrameela Rani Garnepudi bl_start_cmd_timer(adapter, timeout); 624b78e91bcSPrameela Rani Garnepudi status = bl_write_cmd(adapter, cmd, exp_resp, ®out_val); 625b78e91bcSPrameela Rani Garnepudi if (status < 0) { 62692aafe77SJohan Hovold bl_stop_cmd_timer(adapter); 627b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 628b78e91bcSPrameela Rani Garnepudi "%s: Command %s (%0x) writing failed..\n", 629b78e91bcSPrameela Rani Garnepudi __func__, str, cmd); 630b78e91bcSPrameela Rani Garnepudi return status; 631b78e91bcSPrameela Rani Garnepudi } 632b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 633b78e91bcSPrameela Rani Garnepudi return 0; 634b78e91bcSPrameela Rani Garnepudi } 635b78e91bcSPrameela Rani Garnepudi 636b78e91bcSPrameela Rani Garnepudi #define CHECK_SUM_OFFSET 20 637b78e91bcSPrameela Rani Garnepudi #define LEN_OFFSET 8 638b78e91bcSPrameela Rani Garnepudi #define ADDR_OFFSET 16 639b78e91bcSPrameela Rani Garnepudi static int bl_write_header(struct rsi_hw *adapter, u8 *flash_content, 640b78e91bcSPrameela Rani Garnepudi u32 content_size) 641b78e91bcSPrameela Rani Garnepudi { 642b78e91bcSPrameela Rani Garnepudi struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 643f7005466SSiva Rebbagondla struct bl_header *bl_hdr; 644b78e91bcSPrameela Rani Garnepudi u32 write_addr, write_len; 645b78e91bcSPrameela Rani Garnepudi int status; 646b78e91bcSPrameela Rani Garnepudi 647f7005466SSiva Rebbagondla bl_hdr = kzalloc(sizeof(*bl_hdr), GFP_KERNEL); 648f7005466SSiva Rebbagondla if (!bl_hdr) 649f7005466SSiva Rebbagondla return -ENOMEM; 650f7005466SSiva Rebbagondla 651f7005466SSiva Rebbagondla bl_hdr->flags = 0; 652f7005466SSiva Rebbagondla bl_hdr->image_no = cpu_to_le32(adapter->priv->coex_mode); 653f7005466SSiva Rebbagondla bl_hdr->check_sum = 654f7005466SSiva Rebbagondla cpu_to_le32(*(u32 *)&flash_content[CHECK_SUM_OFFSET]); 655f7005466SSiva Rebbagondla bl_hdr->flash_start_address = 656f7005466SSiva Rebbagondla cpu_to_le32(*(u32 *)&flash_content[ADDR_OFFSET]); 657f7005466SSiva Rebbagondla bl_hdr->flash_len = cpu_to_le32(*(u32 *)&flash_content[LEN_OFFSET]); 658b78e91bcSPrameela Rani Garnepudi write_len = sizeof(struct bl_header); 659b78e91bcSPrameela Rani Garnepudi 660b78e91bcSPrameela Rani Garnepudi if (adapter->rsi_host_intf == RSI_HOST_INTF_USB) { 661b78e91bcSPrameela Rani Garnepudi write_addr = PING_BUFFER_ADDRESS; 662b78e91bcSPrameela Rani Garnepudi status = hif_ops->write_reg_multiple(adapter, write_addr, 663f7005466SSiva Rebbagondla (u8 *)bl_hdr, write_len); 664b78e91bcSPrameela Rani Garnepudi if (status < 0) { 665b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 666b78e91bcSPrameela Rani Garnepudi "%s: Failed to load Version/CRC structure\n", 667b78e91bcSPrameela Rani Garnepudi __func__); 668f7005466SSiva Rebbagondla goto fail; 669b78e91bcSPrameela Rani Garnepudi } 670b78e91bcSPrameela Rani Garnepudi } else { 671b78e91bcSPrameela Rani Garnepudi write_addr = PING_BUFFER_ADDRESS >> 16; 672b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_access_msword(adapter, write_addr); 673b78e91bcSPrameela Rani Garnepudi if (status < 0) { 674b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 675b78e91bcSPrameela Rani Garnepudi "%s: Unable to set ms word to common reg\n", 676b78e91bcSPrameela Rani Garnepudi __func__); 677f7005466SSiva Rebbagondla goto fail; 678b78e91bcSPrameela Rani Garnepudi } 679b78e91bcSPrameela Rani Garnepudi write_addr = RSI_SD_REQUEST_MASTER | 680b78e91bcSPrameela Rani Garnepudi (PING_BUFFER_ADDRESS & 0xFFFF); 681b78e91bcSPrameela Rani Garnepudi status = hif_ops->write_reg_multiple(adapter, write_addr, 682f7005466SSiva Rebbagondla (u8 *)bl_hdr, write_len); 683b78e91bcSPrameela Rani Garnepudi if (status < 0) { 684b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 685b78e91bcSPrameela Rani Garnepudi "%s: Failed to load Version/CRC structure\n", 686b78e91bcSPrameela Rani Garnepudi __func__); 687f7005466SSiva Rebbagondla goto fail; 688f7005466SSiva Rebbagondla } 689f7005466SSiva Rebbagondla } 690f7005466SSiva Rebbagondla status = 0; 691f7005466SSiva Rebbagondla fail: 692f7005466SSiva Rebbagondla kfree(bl_hdr); 693b78e91bcSPrameela Rani Garnepudi return status; 694b78e91bcSPrameela Rani Garnepudi } 695b78e91bcSPrameela Rani Garnepudi 696b78e91bcSPrameela Rani Garnepudi static u32 read_flash_capacity(struct rsi_hw *adapter) 697b78e91bcSPrameela Rani Garnepudi { 698b78e91bcSPrameela Rani Garnepudi u32 flash_sz = 0; 699b78e91bcSPrameela Rani Garnepudi 700b78e91bcSPrameela Rani Garnepudi if ((adapter->host_intf_ops->master_reg_read(adapter, FLASH_SIZE_ADDR, 701b78e91bcSPrameela Rani Garnepudi &flash_sz, 2)) < 0) { 702b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 703b78e91bcSPrameela Rani Garnepudi "%s: Flash size reading failed..\n", 704b78e91bcSPrameela Rani Garnepudi __func__); 705b78e91bcSPrameela Rani Garnepudi return 0; 706b78e91bcSPrameela Rani Garnepudi } 707b78e91bcSPrameela Rani Garnepudi rsi_dbg(INIT_ZONE, "Flash capacity: %d KiloBytes\n", flash_sz); 708b78e91bcSPrameela Rani Garnepudi 709b78e91bcSPrameela Rani Garnepudi return (flash_sz * 1024); /* Return size in kbytes */ 710b78e91bcSPrameela Rani Garnepudi } 711b78e91bcSPrameela Rani Garnepudi 712b78e91bcSPrameela Rani Garnepudi static int ping_pong_write(struct rsi_hw *adapter, u8 cmd, u8 *addr, u32 size) 713b78e91bcSPrameela Rani Garnepudi { 714b78e91bcSPrameela Rani Garnepudi struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 715b78e91bcSPrameela Rani Garnepudi u32 block_size = adapter->block_size; 716b78e91bcSPrameela Rani Garnepudi u32 cmd_addr; 717b78e91bcSPrameela Rani Garnepudi u16 cmd_resp, cmd_req; 718b78e91bcSPrameela Rani Garnepudi u8 *str; 719b78e91bcSPrameela Rani Garnepudi int status; 720b78e91bcSPrameela Rani Garnepudi 721b78e91bcSPrameela Rani Garnepudi if (cmd == PING_WRITE) { 722b78e91bcSPrameela Rani Garnepudi cmd_addr = PING_BUFFER_ADDRESS; 723b78e91bcSPrameela Rani Garnepudi cmd_resp = PONG_AVAIL; 724b78e91bcSPrameela Rani Garnepudi cmd_req = PING_VALID; 725b78e91bcSPrameela Rani Garnepudi str = "PING_VALID"; 726b78e91bcSPrameela Rani Garnepudi } else { 727b78e91bcSPrameela Rani Garnepudi cmd_addr = PONG_BUFFER_ADDRESS; 728b78e91bcSPrameela Rani Garnepudi cmd_resp = PING_AVAIL; 729b78e91bcSPrameela Rani Garnepudi cmd_req = PONG_VALID; 730b78e91bcSPrameela Rani Garnepudi str = "PONG_VALID"; 731b78e91bcSPrameela Rani Garnepudi } 732b78e91bcSPrameela Rani Garnepudi 733b78e91bcSPrameela Rani Garnepudi status = hif_ops->load_data_master_write(adapter, cmd_addr, size, 734b78e91bcSPrameela Rani Garnepudi block_size, addr); 735b78e91bcSPrameela Rani Garnepudi if (status) { 736b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Unable to write blk at addr %0x\n", 737b78e91bcSPrameela Rani Garnepudi __func__, *addr); 738b78e91bcSPrameela Rani Garnepudi return status; 739b78e91bcSPrameela Rani Garnepudi } 740b78e91bcSPrameela Rani Garnepudi 741b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, cmd_req, cmd_resp, str); 74292aafe77SJohan Hovold if (status) 743b78e91bcSPrameela Rani Garnepudi return status; 74492aafe77SJohan Hovold 745b78e91bcSPrameela Rani Garnepudi return 0; 746b78e91bcSPrameela Rani Garnepudi } 747b78e91bcSPrameela Rani Garnepudi 748b78e91bcSPrameela Rani Garnepudi static int auto_fw_upgrade(struct rsi_hw *adapter, u8 *flash_content, 749b78e91bcSPrameela Rani Garnepudi u32 content_size) 750b78e91bcSPrameela Rani Garnepudi { 75135204d0aSColin Ian King u8 cmd; 752b78e91bcSPrameela Rani Garnepudi u32 temp_content_size, num_flash, index; 753b78e91bcSPrameela Rani Garnepudi u32 flash_start_address; 754b78e91bcSPrameela Rani Garnepudi int status; 755b78e91bcSPrameela Rani Garnepudi 756b78e91bcSPrameela Rani Garnepudi if (content_size > MAX_FLASH_FILE_SIZE) { 757b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 758b78e91bcSPrameela Rani Garnepudi "%s: Flash Content size is more than 400K %u\n", 759b78e91bcSPrameela Rani Garnepudi __func__, MAX_FLASH_FILE_SIZE); 760b78e91bcSPrameela Rani Garnepudi return -EINVAL; 761b78e91bcSPrameela Rani Garnepudi } 762b78e91bcSPrameela Rani Garnepudi 763b78e91bcSPrameela Rani Garnepudi flash_start_address = *(u32 *)&flash_content[FLASH_START_ADDRESS]; 764b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "flash start address: %08x\n", flash_start_address); 765b78e91bcSPrameela Rani Garnepudi 766b78e91bcSPrameela Rani Garnepudi if (flash_start_address < FW_IMAGE_MIN_ADDRESS) { 767b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 768b78e91bcSPrameela Rani Garnepudi "%s: Fw image Flash Start Address is less than 64K\n", 769b78e91bcSPrameela Rani Garnepudi __func__); 770b78e91bcSPrameela Rani Garnepudi return -EINVAL; 771b78e91bcSPrameela Rani Garnepudi } 772b78e91bcSPrameela Rani Garnepudi 773b78e91bcSPrameela Rani Garnepudi if (flash_start_address % FLASH_SECTOR_SIZE) { 774b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 775b78e91bcSPrameela Rani Garnepudi "%s: Flash Start Address is not multiple of 4K\n", 776b78e91bcSPrameela Rani Garnepudi __func__); 777b78e91bcSPrameela Rani Garnepudi return -EINVAL; 778b78e91bcSPrameela Rani Garnepudi } 779b78e91bcSPrameela Rani Garnepudi 780b78e91bcSPrameela Rani Garnepudi if ((flash_start_address + content_size) > adapter->flash_capacity) { 781b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 782b78e91bcSPrameela Rani Garnepudi "%s: Flash Content will cross max flash size\n", 783b78e91bcSPrameela Rani Garnepudi __func__); 784b78e91bcSPrameela Rani Garnepudi return -EINVAL; 785b78e91bcSPrameela Rani Garnepudi } 786b78e91bcSPrameela Rani Garnepudi 787b78e91bcSPrameela Rani Garnepudi temp_content_size = content_size; 788b78e91bcSPrameela Rani Garnepudi num_flash = content_size / FLASH_WRITE_CHUNK_SIZE; 789b78e91bcSPrameela Rani Garnepudi 790b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "content_size: %d, num_flash: %d\n", 791b78e91bcSPrameela Rani Garnepudi content_size, num_flash); 792b78e91bcSPrameela Rani Garnepudi 793b78e91bcSPrameela Rani Garnepudi for (index = 0; index <= num_flash; index++) { 794b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "flash index: %d\n", index); 795b78e91bcSPrameela Rani Garnepudi if (index != num_flash) { 796b78e91bcSPrameela Rani Garnepudi content_size = FLASH_WRITE_CHUNK_SIZE; 797b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "QSPI content_size:%d\n", 798b78e91bcSPrameela Rani Garnepudi content_size); 799b78e91bcSPrameela Rani Garnepudi } else { 800b78e91bcSPrameela Rani Garnepudi content_size = 801b78e91bcSPrameela Rani Garnepudi temp_content_size % FLASH_WRITE_CHUNK_SIZE; 802b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 803b78e91bcSPrameela Rani Garnepudi "Writing last sector content_size:%d\n", 804b78e91bcSPrameela Rani Garnepudi content_size); 805b78e91bcSPrameela Rani Garnepudi if (!content_size) { 806b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "instruction size zero\n"); 807b78e91bcSPrameela Rani Garnepudi break; 808b78e91bcSPrameela Rani Garnepudi } 809b78e91bcSPrameela Rani Garnepudi } 810b78e91bcSPrameela Rani Garnepudi 811b78e91bcSPrameela Rani Garnepudi if (index % 2) 812b78e91bcSPrameela Rani Garnepudi cmd = PING_WRITE; 813b78e91bcSPrameela Rani Garnepudi else 814b78e91bcSPrameela Rani Garnepudi cmd = PONG_WRITE; 815b78e91bcSPrameela Rani Garnepudi 816b78e91bcSPrameela Rani Garnepudi status = ping_pong_write(adapter, cmd, flash_content, 817b78e91bcSPrameela Rani Garnepudi content_size); 818b78e91bcSPrameela Rani Garnepudi if (status) { 819b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Unable to load %d block\n", 820b78e91bcSPrameela Rani Garnepudi __func__, index); 821b78e91bcSPrameela Rani Garnepudi return status; 822b78e91bcSPrameela Rani Garnepudi } 823b78e91bcSPrameela Rani Garnepudi 824b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 825b78e91bcSPrameela Rani Garnepudi "%s: Successfully loaded %d instructions\n", 826b78e91bcSPrameela Rani Garnepudi __func__, index); 827b78e91bcSPrameela Rani Garnepudi flash_content += content_size; 828b78e91bcSPrameela Rani Garnepudi } 829b78e91bcSPrameela Rani Garnepudi 830b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, EOF_REACHED, FW_LOADING_SUCCESSFUL, 831b78e91bcSPrameela Rani Garnepudi "EOF_REACHED"); 83292aafe77SJohan Hovold if (status) 833b78e91bcSPrameela Rani Garnepudi return status; 83492aafe77SJohan Hovold 835b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "FW loading is done and FW is running..\n"); 836b78e91bcSPrameela Rani Garnepudi return 0; 837b78e91bcSPrameela Rani Garnepudi } 838b78e91bcSPrameela Rani Garnepudi 8393ac61578SSiva Rebbagondla static int rsi_hal_prepare_fwload(struct rsi_hw *adapter) 840b78e91bcSPrameela Rani Garnepudi { 841b78e91bcSPrameela Rani Garnepudi struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 8423ac61578SSiva Rebbagondla u32 regout_val = 0; 843b78e91bcSPrameela Rani Garnepudi int status; 844b78e91bcSPrameela Rani Garnepudi 845b78e91bcSPrameela Rani Garnepudi bl_start_cmd_timer(adapter, BL_CMD_TIMEOUT); 846b78e91bcSPrameela Rani Garnepudi 847b78e91bcSPrameela Rani Garnepudi while (!adapter->blcmd_timer_expired) { 848b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_read(adapter, SWBL_REGOUT, 8493ac61578SSiva Rebbagondla ®out_val, 8503ac61578SSiva Rebbagondla RSI_COMMON_REG_SIZE); 851b78e91bcSPrameela Rani Garnepudi if (status < 0) { 85292aafe77SJohan Hovold bl_stop_cmd_timer(adapter); 853b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 854b78e91bcSPrameela Rani Garnepudi "%s: REGOUT read failed\n", __func__); 855b78e91bcSPrameela Rani Garnepudi return status; 856b78e91bcSPrameela Rani Garnepudi } 857b78e91bcSPrameela Rani Garnepudi mdelay(1); 858b78e91bcSPrameela Rani Garnepudi if ((regout_val >> 8) == REGOUT_VALID) 859b78e91bcSPrameela Rani Garnepudi break; 860b78e91bcSPrameela Rani Garnepudi } 861b78e91bcSPrameela Rani Garnepudi if (adapter->blcmd_timer_expired) { 862b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: REGOUT read timedout\n", __func__); 863b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 864b78e91bcSPrameela Rani Garnepudi "%s: Soft boot loader not present\n", __func__); 865b78e91bcSPrameela Rani Garnepudi return -ETIMEDOUT; 866b78e91bcSPrameela Rani Garnepudi } 867b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 868b78e91bcSPrameela Rani Garnepudi 869b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Received Board Version Number: %x\n", 870b78e91bcSPrameela Rani Garnepudi (regout_val & 0xff)); 871b78e91bcSPrameela Rani Garnepudi 872b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_write(adapter, SWBL_REGOUT, 8733ac61578SSiva Rebbagondla (REGOUT_INVALID | 8743ac61578SSiva Rebbagondla REGOUT_INVALID << 8), 8753ac61578SSiva Rebbagondla RSI_COMMON_REG_SIZE); 8763ac61578SSiva Rebbagondla if (status < 0) 877b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: REGOUT writing failed..\n", __func__); 8783ac61578SSiva Rebbagondla else 8793ac61578SSiva Rebbagondla rsi_dbg(INFO_ZONE, 8803ac61578SSiva Rebbagondla "===> Device is ready to load firmware <===\n"); 8813ac61578SSiva Rebbagondla 882b78e91bcSPrameela Rani Garnepudi return status; 883b78e91bcSPrameela Rani Garnepudi } 8843ac61578SSiva Rebbagondla 8853ac61578SSiva Rebbagondla static int rsi_load_9113_firmware(struct rsi_hw *adapter) 8863ac61578SSiva Rebbagondla { 8873ac61578SSiva Rebbagondla struct rsi_common *common = adapter->priv; 8883ac61578SSiva Rebbagondla const struct firmware *fw_entry = NULL; 8893ac61578SSiva Rebbagondla u32 content_size; 8903ac61578SSiva Rebbagondla u16 tmp_regout_val = 0; 8913ac61578SSiva Rebbagondla struct ta_metadata *metadata_p; 8923ac61578SSiva Rebbagondla int status; 893b78e91bcSPrameela Rani Garnepudi 894b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, CONFIG_AUTO_READ_MODE, CMD_PASS, 895b78e91bcSPrameela Rani Garnepudi "AUTO_READ_CMD"); 896b78e91bcSPrameela Rani Garnepudi if (status < 0) 897b78e91bcSPrameela Rani Garnepudi return status; 898b78e91bcSPrameela Rani Garnepudi 899b78e91bcSPrameela Rani Garnepudi adapter->flash_capacity = read_flash_capacity(adapter); 900b78e91bcSPrameela Rani Garnepudi if (adapter->flash_capacity <= 0) { 901b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 902b78e91bcSPrameela Rani Garnepudi "%s: Unable to read flash size from EEPROM\n", 903b78e91bcSPrameela Rani Garnepudi __func__); 904b78e91bcSPrameela Rani Garnepudi return -EINVAL; 905b78e91bcSPrameela Rani Garnepudi } 906b78e91bcSPrameela Rani Garnepudi 907b78e91bcSPrameela Rani Garnepudi metadata_p = &metadata_flash_content[adapter->priv->coex_mode]; 908b78e91bcSPrameela Rani Garnepudi 909b78e91bcSPrameela Rani Garnepudi rsi_dbg(INIT_ZONE, "%s: Loading file %s\n", __func__, metadata_p->name); 910b78e91bcSPrameela Rani Garnepudi adapter->fw_file_name = metadata_p->name; 911b78e91bcSPrameela Rani Garnepudi 912b78e91bcSPrameela Rani Garnepudi status = request_firmware(&fw_entry, metadata_p->name, adapter->device); 913b78e91bcSPrameela Rani Garnepudi if (status < 0) { 914b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Failed to open file %s\n", 915b78e91bcSPrameela Rani Garnepudi __func__, metadata_p->name); 916b78e91bcSPrameela Rani Garnepudi return status; 917b78e91bcSPrameela Rani Garnepudi } 918b78e91bcSPrameela Rani Garnepudi content_size = fw_entry->size; 919b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "FW Length = %d bytes\n", content_size); 920b78e91bcSPrameela Rani Garnepudi 921192524a4SPavani Muthyala /* Get the firmware version */ 922192524a4SPavani Muthyala common->lmac_ver.ver.info.fw_ver[0] = 9233ac61578SSiva Rebbagondla fw_entry->data[LMAC_VER_OFFSET_9113] & 0xFF; 924192524a4SPavani Muthyala common->lmac_ver.ver.info.fw_ver[1] = 9253ac61578SSiva Rebbagondla fw_entry->data[LMAC_VER_OFFSET_9113 + 1] & 0xFF; 9263ac61578SSiva Rebbagondla common->lmac_ver.major = 9273ac61578SSiva Rebbagondla fw_entry->data[LMAC_VER_OFFSET_9113 + 2] & 0xFF; 928192524a4SPavani Muthyala common->lmac_ver.release_num = 9293ac61578SSiva Rebbagondla fw_entry->data[LMAC_VER_OFFSET_9113 + 3] & 0xFF; 9303ac61578SSiva Rebbagondla common->lmac_ver.minor = 9313ac61578SSiva Rebbagondla fw_entry->data[LMAC_VER_OFFSET_9113 + 4] & 0xFF; 932192524a4SPavani Muthyala common->lmac_ver.patch_num = 0; 933192524a4SPavani Muthyala rsi_print_version(common); 934192524a4SPavani Muthyala 935bae40292SSiva Rebbagondla status = bl_write_header(adapter, (u8 *)fw_entry->data, content_size); 936b78e91bcSPrameela Rani Garnepudi if (status) { 937b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 938b78e91bcSPrameela Rani Garnepudi "%s: RPS Image header loading failed\n", 939b78e91bcSPrameela Rani Garnepudi __func__); 940b78e91bcSPrameela Rani Garnepudi goto fail; 941b78e91bcSPrameela Rani Garnepudi } 942b78e91bcSPrameela Rani Garnepudi 943b78e91bcSPrameela Rani Garnepudi bl_start_cmd_timer(adapter, BL_CMD_TIMEOUT); 944b78e91bcSPrameela Rani Garnepudi status = bl_write_cmd(adapter, CHECK_CRC, CMD_PASS, &tmp_regout_val); 945b78e91bcSPrameela Rani Garnepudi if (status) { 946b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 947b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 948b78e91bcSPrameela Rani Garnepudi "%s: CHECK_CRC Command writing failed..\n", 949b78e91bcSPrameela Rani Garnepudi __func__); 950b78e91bcSPrameela Rani Garnepudi if ((tmp_regout_val & 0xff) == CMD_FAIL) { 951b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 952b78e91bcSPrameela Rani Garnepudi "CRC Fail.. Proceeding to Upgrade mode\n"); 953b78e91bcSPrameela Rani Garnepudi goto fw_upgrade; 954b78e91bcSPrameela Rani Garnepudi } 955b78e91bcSPrameela Rani Garnepudi } 956b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 957b78e91bcSPrameela Rani Garnepudi 958b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, POLLING_MODE, CMD_PASS, "POLLING_MODE"); 959b78e91bcSPrameela Rani Garnepudi if (status) 960b78e91bcSPrameela Rani Garnepudi goto fail; 961b78e91bcSPrameela Rani Garnepudi 962b78e91bcSPrameela Rani Garnepudi load_image_cmd: 963b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, LOAD_HOSTED_FW, LOADING_INITIATED, 964b78e91bcSPrameela Rani Garnepudi "LOAD_HOSTED_FW"); 965b78e91bcSPrameela Rani Garnepudi if (status) 966b78e91bcSPrameela Rani Garnepudi goto fail; 967b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Load Image command passed..\n"); 968b78e91bcSPrameela Rani Garnepudi goto success; 969b78e91bcSPrameela Rani Garnepudi 970b78e91bcSPrameela Rani Garnepudi fw_upgrade: 971b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, BURN_HOSTED_FW, SEND_RPS_FILE, "FW_UPGRADE"); 972b78e91bcSPrameela Rani Garnepudi if (status) 973b78e91bcSPrameela Rani Garnepudi goto fail; 974b78e91bcSPrameela Rani Garnepudi 975b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Burn Command Pass.. Upgrading the firmware\n"); 976b78e91bcSPrameela Rani Garnepudi 977bae40292SSiva Rebbagondla status = auto_fw_upgrade(adapter, (u8 *)fw_entry->data, content_size); 978b78e91bcSPrameela Rani Garnepudi if (status == 0) { 979b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Firmware upgradation Done\n"); 980b78e91bcSPrameela Rani Garnepudi goto load_image_cmd; 981b78e91bcSPrameela Rani Garnepudi } 982b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Firmware upgrade failed\n"); 983b78e91bcSPrameela Rani Garnepudi 984b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, CONFIG_AUTO_READ_MODE, CMD_PASS, 985b78e91bcSPrameela Rani Garnepudi "AUTO_READ_MODE"); 986b78e91bcSPrameela Rani Garnepudi if (status) 987b78e91bcSPrameela Rani Garnepudi goto fail; 988b78e91bcSPrameela Rani Garnepudi 989b78e91bcSPrameela Rani Garnepudi success: 990b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "***** Firmware Loading successful *****\n"); 991b78e91bcSPrameela Rani Garnepudi release_firmware(fw_entry); 992b78e91bcSPrameela Rani Garnepudi return 0; 993b78e91bcSPrameela Rani Garnepudi 994b78e91bcSPrameela Rani Garnepudi fail: 995b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "##### Firmware loading failed #####\n"); 996b78e91bcSPrameela Rani Garnepudi release_firmware(fw_entry); 997b78e91bcSPrameela Rani Garnepudi return status; 998b78e91bcSPrameela Rani Garnepudi } 999b78e91bcSPrameela Rani Garnepudi 1000e5a1ecc9SSiva Rebbagondla static int rsi_load_9116_firmware(struct rsi_hw *adapter) 1001e5a1ecc9SSiva Rebbagondla { 1002e5a1ecc9SSiva Rebbagondla struct rsi_common *common = adapter->priv; 1003e5a1ecc9SSiva Rebbagondla struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 1004e5a1ecc9SSiva Rebbagondla const struct firmware *fw_entry; 1005e5a1ecc9SSiva Rebbagondla struct ta_metadata *metadata_p; 1006e5a1ecc9SSiva Rebbagondla u8 *ta_firmware, *fw_p; 1007e5a1ecc9SSiva Rebbagondla struct bootload_ds bootload_ds; 1008e5a1ecc9SSiva Rebbagondla u32 instructions_sz, base_address; 1009e5a1ecc9SSiva Rebbagondla u16 block_size = adapter->block_size; 1010e5a1ecc9SSiva Rebbagondla u32 dest, len; 1011e5a1ecc9SSiva Rebbagondla int status, cnt; 1012e5a1ecc9SSiva Rebbagondla 1013e5a1ecc9SSiva Rebbagondla rsi_dbg(INIT_ZONE, "***** Load 9116 TA Instructions *****\n"); 1014e5a1ecc9SSiva Rebbagondla 1015e5a1ecc9SSiva Rebbagondla if (adapter->rsi_host_intf == RSI_HOST_INTF_USB) { 1016e5a1ecc9SSiva Rebbagondla status = bl_cmd(adapter, POLLING_MODE, CMD_PASS, 1017e5a1ecc9SSiva Rebbagondla "POLLING_MODE"); 1018e5a1ecc9SSiva Rebbagondla if (status < 0) 1019e5a1ecc9SSiva Rebbagondla return status; 1020e5a1ecc9SSiva Rebbagondla } 1021e5a1ecc9SSiva Rebbagondla 1022e5a1ecc9SSiva Rebbagondla status = hif_ops->master_reg_write(adapter, MEM_ACCESS_CTRL_FROM_HOST, 1023e5a1ecc9SSiva Rebbagondla RAM_384K_ACCESS_FROM_TA, 1024e5a1ecc9SSiva Rebbagondla RSI_9116_REG_SIZE); 1025e5a1ecc9SSiva Rebbagondla if (status < 0) { 1026e5a1ecc9SSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Unable to access full RAM memory\n", 1027e5a1ecc9SSiva Rebbagondla __func__); 1028e5a1ecc9SSiva Rebbagondla return status; 1029e5a1ecc9SSiva Rebbagondla } 1030e5a1ecc9SSiva Rebbagondla 1031e5a1ecc9SSiva Rebbagondla metadata_p = &metadata[adapter->priv->coex_mode]; 1032e5a1ecc9SSiva Rebbagondla rsi_dbg(INIT_ZONE, "%s: loading file %s\n", __func__, metadata_p->name); 1033e5a1ecc9SSiva Rebbagondla status = request_firmware(&fw_entry, metadata_p->name, adapter->device); 1034e5a1ecc9SSiva Rebbagondla if (status < 0) { 1035e5a1ecc9SSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Failed to open file %s\n", 1036e5a1ecc9SSiva Rebbagondla __func__, metadata_p->name); 1037e5a1ecc9SSiva Rebbagondla return status; 1038e5a1ecc9SSiva Rebbagondla } 1039e5a1ecc9SSiva Rebbagondla 1040e5a1ecc9SSiva Rebbagondla ta_firmware = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); 1041e5a1ecc9SSiva Rebbagondla if (!ta_firmware) 1042e5a1ecc9SSiva Rebbagondla goto fail_release_fw; 1043e5a1ecc9SSiva Rebbagondla fw_p = ta_firmware; 1044e5a1ecc9SSiva Rebbagondla instructions_sz = fw_entry->size; 1045e5a1ecc9SSiva Rebbagondla rsi_dbg(INFO_ZONE, "FW Length = %d bytes\n", instructions_sz); 1046e5a1ecc9SSiva Rebbagondla 1047e5a1ecc9SSiva Rebbagondla common->lmac_ver.major = ta_firmware[LMAC_VER_OFFSET_9116]; 1048e5a1ecc9SSiva Rebbagondla common->lmac_ver.minor = ta_firmware[LMAC_VER_OFFSET_9116 + 1]; 1049e5a1ecc9SSiva Rebbagondla common->lmac_ver.release_num = ta_firmware[LMAC_VER_OFFSET_9116 + 2]; 1050e5a1ecc9SSiva Rebbagondla common->lmac_ver.patch_num = ta_firmware[LMAC_VER_OFFSET_9116 + 3]; 1051e5a1ecc9SSiva Rebbagondla common->lmac_ver.ver.info.fw_ver[0] = 1052e5a1ecc9SSiva Rebbagondla ta_firmware[LMAC_VER_OFFSET_9116 + 4]; 1053e5a1ecc9SSiva Rebbagondla 1054e5a1ecc9SSiva Rebbagondla if (instructions_sz % FW_ALIGN_SIZE) 1055e5a1ecc9SSiva Rebbagondla instructions_sz += 1056e5a1ecc9SSiva Rebbagondla (FW_ALIGN_SIZE - (instructions_sz % FW_ALIGN_SIZE)); 1057e5a1ecc9SSiva Rebbagondla rsi_dbg(INFO_ZONE, "instructions_sz : %d\n", instructions_sz); 1058e5a1ecc9SSiva Rebbagondla 1059e5a1ecc9SSiva Rebbagondla if (*(u16 *)fw_p == RSI_9116_FW_MAGIC_WORD) { 1060e5a1ecc9SSiva Rebbagondla memcpy(&bootload_ds, fw_p, sizeof(struct bootload_ds)); 1061e5a1ecc9SSiva Rebbagondla fw_p += le16_to_cpu(bootload_ds.offset); 1062e5a1ecc9SSiva Rebbagondla rsi_dbg(INFO_ZONE, "FW start = %x\n", *(u32 *)fw_p); 1063e5a1ecc9SSiva Rebbagondla 1064e5a1ecc9SSiva Rebbagondla cnt = 0; 1065e5a1ecc9SSiva Rebbagondla do { 1066e5a1ecc9SSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Loading chunk %d\n", 1067e5a1ecc9SSiva Rebbagondla __func__, cnt); 1068e5a1ecc9SSiva Rebbagondla 1069e5a1ecc9SSiva Rebbagondla dest = le32_to_cpu(bootload_ds.bl_entry[cnt].dst_addr); 1070e5a1ecc9SSiva Rebbagondla len = le32_to_cpu(bootload_ds.bl_entry[cnt].control) & 1071e5a1ecc9SSiva Rebbagondla RSI_BL_CTRL_LEN_MASK; 1072e5a1ecc9SSiva Rebbagondla rsi_dbg(INFO_ZONE, "length %d destination %x\n", 1073e5a1ecc9SSiva Rebbagondla len, dest); 1074e5a1ecc9SSiva Rebbagondla 1075e5a1ecc9SSiva Rebbagondla status = hif_ops->load_data_master_write(adapter, dest, 1076e5a1ecc9SSiva Rebbagondla len, 1077e5a1ecc9SSiva Rebbagondla block_size, 1078e5a1ecc9SSiva Rebbagondla fw_p); 1079e5a1ecc9SSiva Rebbagondla if (status < 0) { 1080e5a1ecc9SSiva Rebbagondla rsi_dbg(ERR_ZONE, 1081e5a1ecc9SSiva Rebbagondla "Failed to load chunk %d\n", cnt); 1082e5a1ecc9SSiva Rebbagondla break; 1083e5a1ecc9SSiva Rebbagondla } 1084e5a1ecc9SSiva Rebbagondla fw_p += len; 1085e5a1ecc9SSiva Rebbagondla if (le32_to_cpu(bootload_ds.bl_entry[cnt].control) & 1086e5a1ecc9SSiva Rebbagondla RSI_BL_CTRL_LAST_ENTRY) 1087e5a1ecc9SSiva Rebbagondla break; 1088e5a1ecc9SSiva Rebbagondla cnt++; 1089e5a1ecc9SSiva Rebbagondla } while (1); 1090e5a1ecc9SSiva Rebbagondla } else { 1091e5a1ecc9SSiva Rebbagondla base_address = metadata_p->address; 1092e5a1ecc9SSiva Rebbagondla status = hif_ops->load_data_master_write(adapter, 1093e5a1ecc9SSiva Rebbagondla base_address, 1094e5a1ecc9SSiva Rebbagondla instructions_sz, 1095e5a1ecc9SSiva Rebbagondla block_size, 1096e5a1ecc9SSiva Rebbagondla ta_firmware); 1097e5a1ecc9SSiva Rebbagondla } 1098e5a1ecc9SSiva Rebbagondla if (status) { 1099e5a1ecc9SSiva Rebbagondla rsi_dbg(ERR_ZONE, 1100e5a1ecc9SSiva Rebbagondla "%s: Unable to load %s blk\n", 1101e5a1ecc9SSiva Rebbagondla __func__, metadata_p->name); 1102e5a1ecc9SSiva Rebbagondla goto fail_free_fw; 1103e5a1ecc9SSiva Rebbagondla } 1104e5a1ecc9SSiva Rebbagondla 1105e5a1ecc9SSiva Rebbagondla rsi_dbg(INIT_ZONE, "%s: Successfully loaded %s instructions\n", 1106e5a1ecc9SSiva Rebbagondla __func__, metadata_p->name); 1107e5a1ecc9SSiva Rebbagondla 1108e5a1ecc9SSiva Rebbagondla if (adapter->rsi_host_intf == RSI_HOST_INTF_SDIO) { 1109e5a1ecc9SSiva Rebbagondla if (hif_ops->ta_reset(adapter)) 1110e5a1ecc9SSiva Rebbagondla rsi_dbg(ERR_ZONE, "Unable to put ta in reset\n"); 1111e5a1ecc9SSiva Rebbagondla } else { 1112e5a1ecc9SSiva Rebbagondla if (bl_cmd(adapter, JUMP_TO_ZERO_PC, 1113e5a1ecc9SSiva Rebbagondla CMD_PASS, "JUMP_TO_ZERO") < 0) 1114e5a1ecc9SSiva Rebbagondla rsi_dbg(INFO_ZONE, "Jump to zero command failed\n"); 1115e5a1ecc9SSiva Rebbagondla else 1116e5a1ecc9SSiva Rebbagondla rsi_dbg(INFO_ZONE, "Jump to zero command successful\n"); 1117e5a1ecc9SSiva Rebbagondla } 1118e5a1ecc9SSiva Rebbagondla 1119e5a1ecc9SSiva Rebbagondla fail_free_fw: 1120e5a1ecc9SSiva Rebbagondla kfree(ta_firmware); 1121e5a1ecc9SSiva Rebbagondla fail_release_fw: 1122e5a1ecc9SSiva Rebbagondla release_firmware(fw_entry); 1123e5a1ecc9SSiva Rebbagondla 1124e5a1ecc9SSiva Rebbagondla return status; 1125e5a1ecc9SSiva Rebbagondla } 1126e5a1ecc9SSiva Rebbagondla 1127b78e91bcSPrameela Rani Garnepudi int rsi_hal_device_init(struct rsi_hw *adapter) 1128b78e91bcSPrameela Rani Garnepudi { 1129b78e91bcSPrameela Rani Garnepudi struct rsi_common *common = adapter->priv; 11303ac61578SSiva Rebbagondla int status; 1131b78e91bcSPrameela Rani Garnepudi 1132b78e91bcSPrameela Rani Garnepudi switch (adapter->device_model) { 1133b78e91bcSPrameela Rani Garnepudi case RSI_DEV_9113: 11343ac61578SSiva Rebbagondla status = rsi_hal_prepare_fwload(adapter); 11353ac61578SSiva Rebbagondla if (status < 0) 11363ac61578SSiva Rebbagondla return status; 11373ac61578SSiva Rebbagondla if (rsi_load_9113_firmware(adapter)) { 1138b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 1139b78e91bcSPrameela Rani Garnepudi "%s: Failed to load TA instructions\n", 1140b78e91bcSPrameela Rani Garnepudi __func__); 1141b78e91bcSPrameela Rani Garnepudi return -EINVAL; 1142b78e91bcSPrameela Rani Garnepudi } 1143b78e91bcSPrameela Rani Garnepudi break; 1144e5a1ecc9SSiva Rebbagondla case RSI_DEV_9116: 1145e5a1ecc9SSiva Rebbagondla status = rsi_hal_prepare_fwload(adapter); 1146e5a1ecc9SSiva Rebbagondla if (status < 0) 1147e5a1ecc9SSiva Rebbagondla return status; 1148e5a1ecc9SSiva Rebbagondla if (rsi_load_9116_firmware(adapter)) { 1149e5a1ecc9SSiva Rebbagondla rsi_dbg(ERR_ZONE, 1150e5a1ecc9SSiva Rebbagondla "%s: Failed to load firmware to 9116 device\n", 1151e5a1ecc9SSiva Rebbagondla __func__); 1152e5a1ecc9SSiva Rebbagondla return -EINVAL; 1153e5a1ecc9SSiva Rebbagondla } 1154e5a1ecc9SSiva Rebbagondla break; 1155b78e91bcSPrameela Rani Garnepudi default: 1156b78e91bcSPrameela Rani Garnepudi return -EINVAL; 1157b78e91bcSPrameela Rani Garnepudi } 1158015e3674SPrameela Rani Garnepudi common->fsm_state = FSM_CARD_NOT_READY; 1159b78e91bcSPrameela Rani Garnepudi 1160b78e91bcSPrameela Rani Garnepudi return 0; 1161b78e91bcSPrameela Rani Garnepudi } 1162b78e91bcSPrameela Rani Garnepudi EXPORT_SYMBOL_GPL(rsi_hal_device_init); 1163b78e91bcSPrameela Rani Garnepudi 1164