1dad0d04fSFariya Fatima /** 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}, 29b78e91bcSPrameela Rani Garnepudi }; 30dad0d04fSFariya Fatima 31d26a9559SPrameela Rani Garnepudi int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb) 32d26a9559SPrameela Rani Garnepudi { 33d26a9559SPrameela Rani Garnepudi struct rsi_hw *adapter = common->priv; 34d26a9559SPrameela Rani Garnepudi int status; 35d26a9559SPrameela Rani Garnepudi 362108df3cSPrameela Rani Garnepudi if (common->coex_mode > 1) 372108df3cSPrameela Rani Garnepudi mutex_lock(&common->tx_bus_mutex); 382108df3cSPrameela Rani Garnepudi 39d26a9559SPrameela Rani Garnepudi status = adapter->host_intf_ops->write_pkt(common->priv, 40d26a9559SPrameela Rani Garnepudi skb->data, skb->len); 412108df3cSPrameela Rani Garnepudi 422108df3cSPrameela Rani Garnepudi if (common->coex_mode > 1) 432108df3cSPrameela Rani Garnepudi mutex_unlock(&common->tx_bus_mutex); 442108df3cSPrameela Rani Garnepudi 45d26a9559SPrameela Rani Garnepudi return status; 46d26a9559SPrameela Rani Garnepudi } 476507de6dSPrameela Rani Garnepudi 481be05eb5SPrameela Rani Garnepudi int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb) 496507de6dSPrameela Rani Garnepudi { 506507de6dSPrameela Rani Garnepudi struct rsi_hw *adapter = common->priv; 516507de6dSPrameela Rani Garnepudi struct ieee80211_hdr *wh = NULL; 526507de6dSPrameela Rani Garnepudi struct ieee80211_tx_info *info; 536507de6dSPrameela Rani Garnepudi struct ieee80211_conf *conf = &adapter->hw->conf; 544671c209SPrameela Rani Garnepudi struct ieee80211_vif *vif; 556507de6dSPrameela Rani Garnepudi struct rsi_mgmt_desc *mgmt_desc; 566507de6dSPrameela Rani Garnepudi struct skb_info *tx_params; 576507de6dSPrameela Rani Garnepudi struct ieee80211_bss_conf *bss = NULL; 585dc36387SPrameela Rani Garnepudi struct rsi_xtended_desc *xtend_desc = NULL; 596507de6dSPrameela Rani Garnepudi u8 header_size; 606507de6dSPrameela Rani Garnepudi u32 dword_align_bytes = 0; 616507de6dSPrameela Rani Garnepudi 6219844c0aSPrameela Rani Garnepudi if (skb->len > MAX_MGMT_PKT_SIZE) { 6319844c0aSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); 6419844c0aSPrameela Rani Garnepudi return -EINVAL; 6519844c0aSPrameela Rani Garnepudi } 6619844c0aSPrameela Rani Garnepudi 676507de6dSPrameela Rani Garnepudi info = IEEE80211_SKB_CB(skb); 686507de6dSPrameela Rani Garnepudi tx_params = (struct skb_info *)info->driver_data; 694671c209SPrameela Rani Garnepudi vif = tx_params->vif; 706507de6dSPrameela Rani Garnepudi 716507de6dSPrameela Rani Garnepudi /* Update header size */ 725dc36387SPrameela Rani Garnepudi header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc); 736507de6dSPrameela Rani Garnepudi if (header_size > skb_headroom(skb)) { 746507de6dSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 756507de6dSPrameela Rani Garnepudi "%s: Failed to add extended descriptor\n", 766507de6dSPrameela Rani Garnepudi __func__); 776507de6dSPrameela Rani Garnepudi return -ENOSPC; 786507de6dSPrameela Rani Garnepudi } 796507de6dSPrameela Rani Garnepudi skb_push(skb, header_size); 806507de6dSPrameela Rani Garnepudi dword_align_bytes = ((unsigned long)skb->data & 0x3f); 816507de6dSPrameela Rani Garnepudi if (dword_align_bytes > skb_headroom(skb)) { 826507de6dSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 836507de6dSPrameela Rani Garnepudi "%s: Failed to add dword align\n", __func__); 846507de6dSPrameela Rani Garnepudi return -ENOSPC; 856507de6dSPrameela Rani Garnepudi } 866507de6dSPrameela Rani Garnepudi skb_push(skb, dword_align_bytes); 876507de6dSPrameela Rani Garnepudi header_size += dword_align_bytes; 886507de6dSPrameela Rani Garnepudi 896507de6dSPrameela Rani Garnepudi tx_params->internal_hdr_size = header_size; 906507de6dSPrameela Rani Garnepudi memset(&skb->data[0], 0, header_size); 914671c209SPrameela Rani Garnepudi bss = &vif->bss_conf; 926507de6dSPrameela Rani Garnepudi wh = (struct ieee80211_hdr *)&skb->data[header_size]; 936507de6dSPrameela Rani Garnepudi 946507de6dSPrameela Rani Garnepudi mgmt_desc = (struct rsi_mgmt_desc *)skb->data; 955dc36387SPrameela Rani Garnepudi xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 966507de6dSPrameela Rani Garnepudi 976507de6dSPrameela Rani Garnepudi rsi_set_len_qno(&mgmt_desc->len_qno, (skb->len - FRAME_DESC_SZ), 986507de6dSPrameela Rani Garnepudi RSI_WIFI_MGMT_Q); 996507de6dSPrameela Rani Garnepudi mgmt_desc->frame_type = TX_DOT11_MGMT; 1006507de6dSPrameela Rani Garnepudi mgmt_desc->header_len = MIN_802_11_HDR_LEN; 1016507de6dSPrameela Rani Garnepudi mgmt_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; 1026507de6dSPrameela Rani Garnepudi mgmt_desc->frame_info |= cpu_to_le16(RATE_INFO_ENABLE); 1036507de6dSPrameela Rani Garnepudi if (is_broadcast_ether_addr(wh->addr1)) 1046507de6dSPrameela Rani Garnepudi mgmt_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); 1056507de6dSPrameela Rani Garnepudi 1066507de6dSPrameela Rani Garnepudi mgmt_desc->seq_ctrl = 1076507de6dSPrameela Rani Garnepudi cpu_to_le16(IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl))); 1084671c209SPrameela Rani Garnepudi if ((common->band == NL80211_BAND_2GHZ) && !common->p2p_enabled) 1094671c209SPrameela Rani Garnepudi mgmt_desc->rate_info = cpu_to_le16(RSI_RATE_1); 1106507de6dSPrameela Rani Garnepudi else 1114671c209SPrameela Rani Garnepudi mgmt_desc->rate_info = cpu_to_le16(RSI_RATE_6); 1126507de6dSPrameela Rani Garnepudi 1136507de6dSPrameela Rani Garnepudi if (conf_is_ht40(conf)) 1146507de6dSPrameela Rani Garnepudi mgmt_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); 1156507de6dSPrameela Rani Garnepudi 11619844c0aSPrameela Rani Garnepudi if (ieee80211_is_probe_resp(wh->frame_control)) { 11719844c0aSPrameela Rani Garnepudi mgmt_desc->misc_flags |= (RSI_ADD_DELTA_TSF_VAP_ID | 11819844c0aSPrameela Rani Garnepudi RSI_FETCH_RETRY_CNT_FRM_HST); 11919844c0aSPrameela Rani Garnepudi #define PROBE_RESP_RETRY_CNT 3 12019844c0aSPrameela Rani Garnepudi xtend_desc->retry_cnt = PROBE_RESP_RETRY_CNT; 12119844c0aSPrameela Rani Garnepudi } 12219844c0aSPrameela Rani Garnepudi 1234671c209SPrameela Rani Garnepudi if (((vif->type == NL80211_IFTYPE_AP) || 1244671c209SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_GO)) && 12519844c0aSPrameela Rani Garnepudi (ieee80211_is_action(wh->frame_control))) { 12619844c0aSPrameela Rani Garnepudi struct rsi_sta *rsta = rsi_find_sta(common, wh->addr1); 12719844c0aSPrameela Rani Garnepudi 12819844c0aSPrameela Rani Garnepudi if (rsta) 12919844c0aSPrameela Rani Garnepudi mgmt_desc->sta_id = tx_params->sta_id; 13019844c0aSPrameela Rani Garnepudi else 13119844c0aSPrameela Rani Garnepudi return -EINVAL; 13219844c0aSPrameela Rani Garnepudi } 1334671c209SPrameela Rani Garnepudi mgmt_desc->rate_info |= 1344671c209SPrameela Rani Garnepudi cpu_to_le16((tx_params->vap_id << RSI_DESC_VAP_ID_OFST) & 1354671c209SPrameela Rani Garnepudi RSI_DESC_VAP_ID_MASK); 1364671c209SPrameela Rani Garnepudi 1376507de6dSPrameela Rani Garnepudi return 0; 1386507de6dSPrameela Rani Garnepudi } 1396507de6dSPrameela Rani Garnepudi 140ceb2e4eaSPavani Muthyala /* This function prepares descriptor for given data packet */ 1411be05eb5SPrameela Rani Garnepudi int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) 142dad0d04fSFariya Fatima { 143ce86893fSKarun Eagalapati struct rsi_hw *adapter = common->priv; 144ce86893fSKarun Eagalapati struct ieee80211_vif *vif; 1450eb42586SPavani Muthyala struct ieee80211_hdr *wh = NULL; 146dad0d04fSFariya Fatima struct ieee80211_tx_info *info; 147dad0d04fSFariya Fatima struct skb_info *tx_params; 148ab2ef1d6SMarkus Elfring struct ieee80211_bss_conf *bss; 149af193097SPavani Muthyala struct rsi_data_desc *data_desc; 1505dc36387SPrameela Rani Garnepudi struct rsi_xtended_desc *xtend_desc; 151dad0d04fSFariya Fatima u8 ieee80211_size = MIN_802_11_HDR_LEN; 1520eb42586SPavani Muthyala u8 header_size; 1530eb42586SPavani Muthyala u8 vap_id = 0; 1540eb42586SPavani Muthyala u8 dword_align_bytes; 155ab2ef1d6SMarkus Elfring u16 seq_num; 156dad0d04fSFariya Fatima 157dad0d04fSFariya Fatima info = IEEE80211_SKB_CB(skb); 158eac4eed3SPrameela Rani Garnepudi vif = info->control.vif; 159eac4eed3SPrameela Rani Garnepudi bss = &vif->bss_conf; 160dad0d04fSFariya Fatima tx_params = (struct skb_info *)info->driver_data; 161dad0d04fSFariya Fatima 1625dc36387SPrameela Rani Garnepudi header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc); 1630eb42586SPavani Muthyala if (header_size > skb_headroom(skb)) { 164dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); 165ceb2e4eaSPavani Muthyala return -ENOSPC; 166dad0d04fSFariya Fatima } 1670eb42586SPavani Muthyala skb_push(skb, header_size); 1680eb42586SPavani Muthyala dword_align_bytes = ((unsigned long)skb->data & 0x3f); 1690eb42586SPavani Muthyala if (header_size > skb_headroom(skb)) { 1700eb42586SPavani Muthyala rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__); 171ceb2e4eaSPavani Muthyala return -ENOSPC; 1720eb42586SPavani Muthyala } 1730eb42586SPavani Muthyala skb_push(skb, dword_align_bytes); 1740eb42586SPavani Muthyala header_size += dword_align_bytes; 175dad0d04fSFariya Fatima 1760eb42586SPavani Muthyala tx_params->internal_hdr_size = header_size; 177af193097SPavani Muthyala data_desc = (struct rsi_data_desc *)skb->data; 1780eb42586SPavani Muthyala memset(data_desc, 0, header_size); 179dad0d04fSFariya Fatima 1805dc36387SPrameela Rani Garnepudi xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 1810eb42586SPavani Muthyala wh = (struct ieee80211_hdr *)&skb->data[header_size]; 18219844c0aSPrameela Rani Garnepudi seq_num = IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl)); 1830eb42586SPavani Muthyala 1840eb42586SPavani Muthyala data_desc->xtend_desc_size = header_size - FRAME_DESC_SZ; 1850eb42586SPavani Muthyala 1860eb42586SPavani Muthyala if (ieee80211_is_data_qos(wh->frame_control)) { 187dad0d04fSFariya Fatima ieee80211_size += 2; 188af193097SPavani Muthyala data_desc->mac_flags |= cpu_to_le16(RSI_QOS_ENABLE); 189dad0d04fSFariya Fatima } 190dad0d04fSFariya Fatima 191eac4eed3SPrameela Rani Garnepudi if (((vif->type == NL80211_IFTYPE_STATION) || 192eac4eed3SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_CLIENT)) && 193ce86893fSKarun Eagalapati (adapter->ps_state == PS_ENABLED)) 194ce86893fSKarun Eagalapati wh->frame_control |= cpu_to_le16(RSI_SET_PS_ENABLE); 195ce86893fSKarun Eagalapati 196dad0d04fSFariya Fatima if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) && 197dad0d04fSFariya Fatima (common->secinfo.security_enable)) { 198dad0d04fSFariya Fatima if (rsi_is_cipher_wep(common)) 199dad0d04fSFariya Fatima ieee80211_size += 4; 200dad0d04fSFariya Fatima else 201dad0d04fSFariya Fatima ieee80211_size += 8; 202af193097SPavani Muthyala data_desc->mac_flags |= cpu_to_le16(RSI_ENCRYPT_PKT); 203dad0d04fSFariya Fatima } 204af193097SPavani Muthyala rsi_set_len_qno(&data_desc->len_qno, (skb->len - FRAME_DESC_SZ), 205af193097SPavani Muthyala RSI_WIFI_DATA_Q); 206af193097SPavani Muthyala data_desc->header_len = ieee80211_size; 207dad0d04fSFariya Fatima 208af193097SPavani Muthyala if (common->min_rate != RSI_RATE_AUTO) { 209dad0d04fSFariya Fatima /* Send fixed rate */ 210af193097SPavani Muthyala data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 211af193097SPavani Muthyala data_desc->rate_info = cpu_to_le16(common->min_rate); 212e8c58e7aSJahnavi Meher 213e8c58e7aSJahnavi Meher if (conf_is_ht40(&common->priv->hw->conf)) 214af193097SPavani Muthyala data_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE); 215e8c58e7aSJahnavi Meher 21619844c0aSPrameela Rani Garnepudi if ((common->vif_info[0].sgi) && (common->min_rate & 0x100)) { 21719844c0aSPrameela Rani Garnepudi /* Only MCS rates */ 218af193097SPavani Muthyala data_desc->rate_info |= 2192bfa6969SJahnavi Meher cpu_to_le16(ENABLE_SHORTGI_RATE); 2202bfa6969SJahnavi Meher } 2210eb42586SPavani Muthyala } 2220eb42586SPavani Muthyala 2230eb42586SPavani Muthyala if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { 2240eb42586SPavani Muthyala rsi_dbg(INFO_ZONE, "*** Tx EAPOL ***\n"); 2250eb42586SPavani Muthyala 2260eb42586SPavani Muthyala data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 2270eb42586SPavani Muthyala if (common->band == NL80211_BAND_5GHZ) 2280eb42586SPavani Muthyala data_desc->rate_info = cpu_to_le16(RSI_RATE_6); 2290eb42586SPavani Muthyala else 2300eb42586SPavani Muthyala data_desc->rate_info = cpu_to_le16(RSI_RATE_1); 2310eb42586SPavani Muthyala data_desc->mac_flags |= cpu_to_le16(RSI_REKEY_PURPOSE); 2320eb42586SPavani Muthyala data_desc->misc_flags |= RSI_FETCH_RETRY_CNT_FRM_HST; 2330eb42586SPavani Muthyala #define EAPOL_RETRY_CNT 15 2340eb42586SPavani Muthyala xtend_desc->retry_cnt = EAPOL_RETRY_CNT; 2354fd6c476SPrameela Rani Garnepudi 2364fd6c476SPrameela Rani Garnepudi if (common->eapol4_confirm) 2374fd6c476SPrameela Rani Garnepudi skb->priority = VO_Q; 2384fd6c476SPrameela Rani Garnepudi else 2394fd6c476SPrameela Rani Garnepudi rsi_set_len_qno(&data_desc->len_qno, 2404fd6c476SPrameela Rani Garnepudi (skb->len - FRAME_DESC_SZ), 2414fd6c476SPrameela Rani Garnepudi RSI_WIFI_MGMT_Q); 2424fd6c476SPrameela Rani Garnepudi if ((skb->len - header_size) == EAPOL4_PACKET_LEN) { 2434fd6c476SPrameela Rani Garnepudi data_desc->misc_flags |= 2444fd6c476SPrameela Rani Garnepudi RSI_DESC_REQUIRE_CFM_TO_HOST; 2454fd6c476SPrameela Rani Garnepudi xtend_desc->confirm_frame_type = EAPOL4_CONFIRM; 2464fd6c476SPrameela Rani Garnepudi } 247dad0d04fSFariya Fatima } 248dad0d04fSFariya Fatima 249*92e97123SSiva Rebbagondla data_desc->mac_flags |= cpu_to_le16(seq_num & 0xfff); 250af193097SPavani Muthyala data_desc->qid_tid = ((skb->priority & 0xf) | 251af193097SPavani Muthyala ((tx_params->tid & 0xf) << 4)); 252af193097SPavani Muthyala data_desc->sta_id = tx_params->sta_id; 253dad0d04fSFariya Fatima 2540eb42586SPavani Muthyala if ((is_broadcast_ether_addr(wh->addr1)) || 2550eb42586SPavani Muthyala (is_multicast_ether_addr(wh->addr1))) { 2560eb42586SPavani Muthyala data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); 2570eb42586SPavani Muthyala data_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT); 2580eb42586SPavani Muthyala data_desc->sta_id = vap_id; 25919844c0aSPrameela Rani Garnepudi 260eac4eed3SPrameela Rani Garnepudi if ((vif->type == NL80211_IFTYPE_AP) || 261eac4eed3SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_GO)) { 26219844c0aSPrameela Rani Garnepudi if (common->band == NL80211_BAND_5GHZ) 26319844c0aSPrameela Rani Garnepudi data_desc->rate_info = cpu_to_le16(RSI_RATE_6); 26419844c0aSPrameela Rani Garnepudi else 26519844c0aSPrameela Rani Garnepudi data_desc->rate_info = cpu_to_le16(RSI_RATE_1); 2660eb42586SPavani Muthyala } 26719844c0aSPrameela Rani Garnepudi } 268eac4eed3SPrameela Rani Garnepudi if (((vif->type == NL80211_IFTYPE_AP) || 269eac4eed3SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_GO)) && 27019844c0aSPrameela Rani Garnepudi (ieee80211_has_moredata(wh->frame_control))) 27119844c0aSPrameela Rani Garnepudi data_desc->frame_info |= cpu_to_le16(MORE_DATA_PRESENT); 2720eb42586SPavani Muthyala 273eac4eed3SPrameela Rani Garnepudi data_desc->rate_info |= 274eac4eed3SPrameela Rani Garnepudi cpu_to_le16((tx_params->vap_id << RSI_DESC_VAP_ID_OFST) & 275eac4eed3SPrameela Rani Garnepudi RSI_DESC_VAP_ID_MASK); 276eac4eed3SPrameela Rani Garnepudi 277ceb2e4eaSPavani Muthyala return 0; 278ceb2e4eaSPavani Muthyala } 279ceb2e4eaSPavani Muthyala 280ceb2e4eaSPavani Muthyala /* This function sends received data packet from driver to device */ 281ceb2e4eaSPavani Muthyala int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) 282ceb2e4eaSPavani Muthyala { 283ceb2e4eaSPavani Muthyala struct rsi_hw *adapter = common->priv; 284eac4eed3SPrameela Rani Garnepudi struct ieee80211_vif *vif; 285ceb2e4eaSPavani Muthyala struct ieee80211_tx_info *info; 2864fd6c476SPrameela Rani Garnepudi struct skb_info *tx_params; 287ceb2e4eaSPavani Muthyala struct ieee80211_bss_conf *bss; 2884fd6c476SPrameela Rani Garnepudi struct ieee80211_hdr *wh; 28919844c0aSPrameela Rani Garnepudi int status = -EINVAL; 2904fd6c476SPrameela Rani Garnepudi u8 header_size; 29119844c0aSPrameela Rani Garnepudi 29219844c0aSPrameela Rani Garnepudi if (!skb) 29319844c0aSPrameela Rani Garnepudi return 0; 29419844c0aSPrameela Rani Garnepudi if (common->iface_down) 29519844c0aSPrameela Rani Garnepudi goto err; 296ceb2e4eaSPavani Muthyala 297ceb2e4eaSPavani Muthyala info = IEEE80211_SKB_CB(skb); 29819844c0aSPrameela Rani Garnepudi if (!info->control.vif) 29919844c0aSPrameela Rani Garnepudi goto err; 300eac4eed3SPrameela Rani Garnepudi vif = info->control.vif; 301eac4eed3SPrameela Rani Garnepudi bss = &vif->bss_conf; 3024fd6c476SPrameela Rani Garnepudi tx_params = (struct skb_info *)info->driver_data; 3034fd6c476SPrameela Rani Garnepudi header_size = tx_params->internal_hdr_size; 3044fd6c476SPrameela Rani Garnepudi wh = (struct ieee80211_hdr *)&skb->data[header_size]; 305ceb2e4eaSPavani Muthyala 306eac4eed3SPrameela Rani Garnepudi if (((vif->type == NL80211_IFTYPE_STATION) || 307eac4eed3SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_CLIENT)) && 308eac4eed3SPrameela Rani Garnepudi (!bss->assoc)) 309ceb2e4eaSPavani Muthyala goto err; 310ceb2e4eaSPavani Muthyala 3112108df3cSPrameela Rani Garnepudi status = rsi_send_pkt_to_bus(common, skb); 312dad0d04fSFariya Fatima if (status) 313ceb2e4eaSPavani Muthyala rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", __func__); 314dad0d04fSFariya Fatima 315dad0d04fSFariya Fatima err: 316dad0d04fSFariya Fatima ++common->tx_stats.total_tx_pkt_freed[skb->priority]; 317af193097SPavani Muthyala rsi_indicate_tx_status(adapter, skb, status); 318dad0d04fSFariya Fatima return status; 319dad0d04fSFariya Fatima } 320dad0d04fSFariya Fatima 321dad0d04fSFariya Fatima /** 322dad0d04fSFariya Fatima * rsi_send_mgmt_pkt() - This functions sends the received management packet 323dad0d04fSFariya Fatima * from driver to device. 324dad0d04fSFariya Fatima * @common: Pointer to the driver private structure. 325dad0d04fSFariya Fatima * @skb: Pointer to the socket buffer structure. 326dad0d04fSFariya Fatima * 327dad0d04fSFariya Fatima * Return: status: 0 on success, -1 on failure. 328dad0d04fSFariya Fatima */ 329dad0d04fSFariya Fatima int rsi_send_mgmt_pkt(struct rsi_common *common, 330dad0d04fSFariya Fatima struct sk_buff *skb) 331dad0d04fSFariya Fatima { 332dad0d04fSFariya Fatima struct rsi_hw *adapter = common->priv; 3331be05eb5SPrameela Rani Garnepudi struct ieee80211_bss_conf *bss; 3341be05eb5SPrameela Rani Garnepudi struct ieee80211_hdr *wh; 335dad0d04fSFariya Fatima struct ieee80211_tx_info *info; 336dad0d04fSFariya Fatima struct skb_info *tx_params; 3371be05eb5SPrameela Rani Garnepudi struct rsi_mgmt_desc *mgmt_desc; 3381be05eb5SPrameela Rani Garnepudi struct rsi_xtended_desc *xtend_desc; 339dad0d04fSFariya Fatima int status = -E2BIG; 3401be05eb5SPrameela Rani Garnepudi u8 header_size; 341dad0d04fSFariya Fatima 342dad0d04fSFariya Fatima info = IEEE80211_SKB_CB(skb); 343dad0d04fSFariya Fatima tx_params = (struct skb_info *)info->driver_data; 3441be05eb5SPrameela Rani Garnepudi header_size = tx_params->internal_hdr_size; 345dad0d04fSFariya Fatima 346dad0d04fSFariya Fatima if (tx_params->flags & INTERNAL_MGMT_PKT) { 347a2ce952cSPrameela Rani Garnepudi status = adapter->host_intf_ops->write_pkt(common->priv, 348dad0d04fSFariya Fatima (u8 *)skb->data, 349dad0d04fSFariya Fatima skb->len); 350dad0d04fSFariya Fatima if (status) { 351dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, 352dad0d04fSFariya Fatima "%s: Failed to write the packet\n", __func__); 353dad0d04fSFariya Fatima } 354dad0d04fSFariya Fatima dev_kfree_skb(skb); 355dad0d04fSFariya Fatima return status; 356dad0d04fSFariya Fatima } 357dad0d04fSFariya Fatima 3581be05eb5SPrameela Rani Garnepudi bss = &info->control.vif->bss_conf; 3591be05eb5SPrameela Rani Garnepudi wh = (struct ieee80211_hdr *)&skb->data[header_size]; 3601be05eb5SPrameela Rani Garnepudi mgmt_desc = (struct rsi_mgmt_desc *)skb->data; 3611be05eb5SPrameela Rani Garnepudi xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ]; 362dad0d04fSFariya Fatima 3631be05eb5SPrameela Rani Garnepudi /* Indicate to firmware to give cfm for probe */ 3641be05eb5SPrameela Rani Garnepudi if (ieee80211_is_probe_req(wh->frame_control) && !bss->assoc) { 3651be05eb5SPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 3661be05eb5SPrameela Rani Garnepudi "%s: blocking mgmt queue\n", __func__); 3671be05eb5SPrameela Rani Garnepudi mgmt_desc->misc_flags = RSI_DESC_REQUIRE_CFM_TO_HOST; 3681be05eb5SPrameela Rani Garnepudi xtend_desc->confirm_frame_type = PROBEREQ_CONFIRM; 3691be05eb5SPrameela Rani Garnepudi common->mgmt_q_block = true; 3701be05eb5SPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Mgmt queue blocked\n"); 3711be05eb5SPrameela Rani Garnepudi } 3721be05eb5SPrameela Rani Garnepudi 3732108df3cSPrameela Rani Garnepudi status = rsi_send_pkt_to_bus(common, skb); 374dad0d04fSFariya Fatima if (status) 375dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__); 376dad0d04fSFariya Fatima 377dad0d04fSFariya Fatima rsi_indicate_tx_status(common->priv, skb, status); 378dad0d04fSFariya Fatima return status; 379dad0d04fSFariya Fatima } 380b78e91bcSPrameela Rani Garnepudi 381716b840cSSiva Rebbagondla int rsi_send_bt_pkt(struct rsi_common *common, struct sk_buff *skb) 382716b840cSSiva Rebbagondla { 383716b840cSSiva Rebbagondla int status = -EINVAL; 384716b840cSSiva Rebbagondla u8 header_size = 0; 385716b840cSSiva Rebbagondla struct rsi_bt_desc *bt_desc; 386716b840cSSiva Rebbagondla u8 queueno = ((skb->data[1] >> 4) & 0xf); 387716b840cSSiva Rebbagondla 388716b840cSSiva Rebbagondla if (queueno == RSI_BT_MGMT_Q) { 389716b840cSSiva Rebbagondla status = rsi_send_pkt_to_bus(common, skb); 390716b840cSSiva Rebbagondla if (status) 391716b840cSSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Failed to write bt mgmt pkt\n", 392716b840cSSiva Rebbagondla __func__); 393716b840cSSiva Rebbagondla goto out; 394716b840cSSiva Rebbagondla } 395716b840cSSiva Rebbagondla header_size = FRAME_DESC_SZ; 396716b840cSSiva Rebbagondla if (header_size > skb_headroom(skb)) { 397716b840cSSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__); 398716b840cSSiva Rebbagondla status = -ENOSPC; 399716b840cSSiva Rebbagondla goto out; 400716b840cSSiva Rebbagondla } 401716b840cSSiva Rebbagondla skb_push(skb, header_size); 402716b840cSSiva Rebbagondla memset(skb->data, 0, header_size); 403716b840cSSiva Rebbagondla bt_desc = (struct rsi_bt_desc *)skb->data; 404716b840cSSiva Rebbagondla 405716b840cSSiva Rebbagondla rsi_set_len_qno(&bt_desc->len_qno, (skb->len - FRAME_DESC_SZ), 406716b840cSSiva Rebbagondla RSI_BT_DATA_Q); 407716b840cSSiva Rebbagondla bt_desc->bt_pkt_type = cpu_to_le16(bt_cb(skb)->pkt_type); 408716b840cSSiva Rebbagondla 409716b840cSSiva Rebbagondla status = rsi_send_pkt_to_bus(common, skb); 410716b840cSSiva Rebbagondla if (status) 411716b840cSSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Failed to write bt pkt\n", __func__); 412716b840cSSiva Rebbagondla 413716b840cSSiva Rebbagondla out: 414716b840cSSiva Rebbagondla dev_kfree_skb(skb); 415716b840cSSiva Rebbagondla return status; 416716b840cSSiva Rebbagondla } 417716b840cSSiva Rebbagondla 418d26a9559SPrameela Rani Garnepudi int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb) 419d26a9559SPrameela Rani Garnepudi { 420d26a9559SPrameela Rani Garnepudi struct rsi_hw *adapter = (struct rsi_hw *)common->priv; 421d26a9559SPrameela Rani Garnepudi struct rsi_data_desc *bcn_frm; 422d26a9559SPrameela Rani Garnepudi struct ieee80211_hw *hw = common->priv->hw; 423d26a9559SPrameela Rani Garnepudi struct ieee80211_conf *conf = &hw->conf; 4244671c209SPrameela Rani Garnepudi struct ieee80211_vif *vif; 425d26a9559SPrameela Rani Garnepudi struct sk_buff *mac_bcn; 4264671c209SPrameela Rani Garnepudi u8 vap_id = 0, i; 4274671c209SPrameela Rani Garnepudi u16 tim_offset = 0; 428d26a9559SPrameela Rani Garnepudi 4294671c209SPrameela Rani Garnepudi for (i = 0; i < RSI_MAX_VIFS; i++) { 4304671c209SPrameela Rani Garnepudi vif = adapter->vifs[i]; 4314671c209SPrameela Rani Garnepudi if (!vif) 4324671c209SPrameela Rani Garnepudi continue; 4334671c209SPrameela Rani Garnepudi if ((vif->type == NL80211_IFTYPE_AP) || 4344671c209SPrameela Rani Garnepudi (vif->type == NL80211_IFTYPE_P2P_GO)) 4354671c209SPrameela Rani Garnepudi break; 4364671c209SPrameela Rani Garnepudi } 4374671c209SPrameela Rani Garnepudi if (!vif) 4384671c209SPrameela Rani Garnepudi return -EINVAL; 439d26a9559SPrameela Rani Garnepudi mac_bcn = ieee80211_beacon_get_tim(adapter->hw, 4404671c209SPrameela Rani Garnepudi vif, 441d26a9559SPrameela Rani Garnepudi &tim_offset, NULL); 442d26a9559SPrameela Rani Garnepudi if (!mac_bcn) { 443d26a9559SPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Failed to get beacon from mac80211\n"); 444d26a9559SPrameela Rani Garnepudi return -EINVAL; 445d26a9559SPrameela Rani Garnepudi } 446d26a9559SPrameela Rani Garnepudi 447d26a9559SPrameela Rani Garnepudi common->beacon_cnt++; 448d26a9559SPrameela Rani Garnepudi bcn_frm = (struct rsi_data_desc *)skb->data; 449d26a9559SPrameela Rani Garnepudi rsi_set_len_qno(&bcn_frm->len_qno, mac_bcn->len, RSI_WIFI_DATA_Q); 450d26a9559SPrameela Rani Garnepudi bcn_frm->header_len = MIN_802_11_HDR_LEN; 451d26a9559SPrameela Rani Garnepudi bcn_frm->frame_info = cpu_to_le16(RSI_DATA_DESC_MAC_BBP_INFO | 452d26a9559SPrameela Rani Garnepudi RSI_DATA_DESC_NO_ACK_IND | 453d26a9559SPrameela Rani Garnepudi RSI_DATA_DESC_BEACON_FRAME | 454d26a9559SPrameela Rani Garnepudi RSI_DATA_DESC_INSERT_TSF | 455d26a9559SPrameela Rani Garnepudi RSI_DATA_DESC_INSERT_SEQ_NO | 456d26a9559SPrameela Rani Garnepudi RATE_INFO_ENABLE); 457d26a9559SPrameela Rani Garnepudi bcn_frm->rate_info = cpu_to_le16(vap_id << 14); 458d26a9559SPrameela Rani Garnepudi bcn_frm->qid_tid = BEACON_HW_Q; 459d26a9559SPrameela Rani Garnepudi 460d26a9559SPrameela Rani Garnepudi if (conf_is_ht40_plus(conf)) { 461d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info = cpu_to_le16(LOWER_20_ENABLE); 462d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info |= cpu_to_le16(LOWER_20_ENABLE >> 12); 463d26a9559SPrameela Rani Garnepudi } else if (conf_is_ht40_minus(conf)) { 464d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info = cpu_to_le16(UPPER_20_ENABLE); 465d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info |= cpu_to_le16(UPPER_20_ENABLE >> 12); 466d26a9559SPrameela Rani Garnepudi } 467d26a9559SPrameela Rani Garnepudi 468d26a9559SPrameela Rani Garnepudi if (common->band == NL80211_BAND_2GHZ) 469d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info |= cpu_to_le16(RSI_RATE_1); 470d26a9559SPrameela Rani Garnepudi else 471d26a9559SPrameela Rani Garnepudi bcn_frm->bbp_info |= cpu_to_le16(RSI_RATE_6); 472d26a9559SPrameela Rani Garnepudi 473d26a9559SPrameela Rani Garnepudi if (mac_bcn->data[tim_offset + 2] == 0) 474d26a9559SPrameela Rani Garnepudi bcn_frm->frame_info |= cpu_to_le16(RSI_DATA_DESC_DTIM_BEACON); 475d26a9559SPrameela Rani Garnepudi 476d26a9559SPrameela Rani Garnepudi memcpy(&skb->data[FRAME_DESC_SZ], mac_bcn->data, mac_bcn->len); 477d26a9559SPrameela Rani Garnepudi skb_put(skb, mac_bcn->len + FRAME_DESC_SZ); 478d26a9559SPrameela Rani Garnepudi 479d26a9559SPrameela Rani Garnepudi dev_kfree_skb(mac_bcn); 480d26a9559SPrameela Rani Garnepudi 481d26a9559SPrameela Rani Garnepudi return 0; 482d26a9559SPrameela Rani Garnepudi } 483d26a9559SPrameela Rani Garnepudi 484dfefb9f8SKees Cook static void bl_cmd_timeout(struct timer_list *t) 485b78e91bcSPrameela Rani Garnepudi { 486dfefb9f8SKees Cook struct rsi_hw *adapter = from_timer(adapter, t, bl_cmd_timer); 487b78e91bcSPrameela Rani Garnepudi 488b78e91bcSPrameela Rani Garnepudi adapter->blcmd_timer_expired = true; 489b78e91bcSPrameela Rani Garnepudi del_timer(&adapter->bl_cmd_timer); 490b78e91bcSPrameela Rani Garnepudi } 491b78e91bcSPrameela Rani Garnepudi 492b78e91bcSPrameela Rani Garnepudi static int bl_start_cmd_timer(struct rsi_hw *adapter, u32 timeout) 493b78e91bcSPrameela Rani Garnepudi { 494dfefb9f8SKees Cook timer_setup(&adapter->bl_cmd_timer, bl_cmd_timeout, 0); 495b78e91bcSPrameela Rani Garnepudi adapter->bl_cmd_timer.expires = (msecs_to_jiffies(timeout) + jiffies); 496b78e91bcSPrameela Rani Garnepudi 497b78e91bcSPrameela Rani Garnepudi adapter->blcmd_timer_expired = false; 498b78e91bcSPrameela Rani Garnepudi add_timer(&adapter->bl_cmd_timer); 499b78e91bcSPrameela Rani Garnepudi 500b78e91bcSPrameela Rani Garnepudi return 0; 501b78e91bcSPrameela Rani Garnepudi } 502b78e91bcSPrameela Rani Garnepudi 503b78e91bcSPrameela Rani Garnepudi static int bl_stop_cmd_timer(struct rsi_hw *adapter) 504b78e91bcSPrameela Rani Garnepudi { 505b78e91bcSPrameela Rani Garnepudi adapter->blcmd_timer_expired = false; 506b78e91bcSPrameela Rani Garnepudi if (timer_pending(&adapter->bl_cmd_timer)) 507b78e91bcSPrameela Rani Garnepudi del_timer(&adapter->bl_cmd_timer); 508b78e91bcSPrameela Rani Garnepudi 509b78e91bcSPrameela Rani Garnepudi return 0; 510b78e91bcSPrameela Rani Garnepudi } 511b78e91bcSPrameela Rani Garnepudi 512b78e91bcSPrameela Rani Garnepudi static int bl_write_cmd(struct rsi_hw *adapter, u8 cmd, u8 exp_resp, 513b78e91bcSPrameela Rani Garnepudi u16 *cmd_resp) 514b78e91bcSPrameela Rani Garnepudi { 515b78e91bcSPrameela Rani Garnepudi struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 516b78e91bcSPrameela Rani Garnepudi u32 regin_val = 0, regout_val = 0; 517b78e91bcSPrameela Rani Garnepudi u32 regin_input = 0; 518b78e91bcSPrameela Rani Garnepudi u8 output = 0; 519b78e91bcSPrameela Rani Garnepudi int status; 520b78e91bcSPrameela Rani Garnepudi 521b78e91bcSPrameela Rani Garnepudi regin_input = (REGIN_INPUT | adapter->priv->coex_mode); 522b78e91bcSPrameela Rani Garnepudi 523b78e91bcSPrameela Rani Garnepudi while (!adapter->blcmd_timer_expired) { 524b78e91bcSPrameela Rani Garnepudi regin_val = 0; 525b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_read(adapter, SWBL_REGIN, 526b78e91bcSPrameela Rani Garnepudi ®in_val, 2); 527b78e91bcSPrameela Rani Garnepudi if (status < 0) { 528b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 529b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGIN reading failed..\n", 530b78e91bcSPrameela Rani Garnepudi __func__, cmd); 531b78e91bcSPrameela Rani Garnepudi return status; 532b78e91bcSPrameela Rani Garnepudi } 533b78e91bcSPrameela Rani Garnepudi mdelay(1); 534b78e91bcSPrameela Rani Garnepudi if ((regin_val >> 12) != REGIN_VALID) 535b78e91bcSPrameela Rani Garnepudi break; 536b78e91bcSPrameela Rani Garnepudi } 537b78e91bcSPrameela Rani Garnepudi if (adapter->blcmd_timer_expired) { 538b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 539b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGIN reading timed out..\n", 540b78e91bcSPrameela Rani Garnepudi __func__, cmd); 541b78e91bcSPrameela Rani Garnepudi return -ETIMEDOUT; 542b78e91bcSPrameela Rani Garnepudi } 543b78e91bcSPrameela Rani Garnepudi 544b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 545b78e91bcSPrameela Rani Garnepudi "Issuing write to Regin val:%0x sending cmd:%0x\n", 546b78e91bcSPrameela Rani Garnepudi regin_val, (cmd | regin_input << 8)); 547b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_write(adapter, SWBL_REGIN, 548b78e91bcSPrameela Rani Garnepudi (cmd | regin_input << 8), 2); 549b78e91bcSPrameela Rani Garnepudi if (status < 0) 550b78e91bcSPrameela Rani Garnepudi return status; 551b78e91bcSPrameela Rani Garnepudi mdelay(1); 552b78e91bcSPrameela Rani Garnepudi 553b78e91bcSPrameela Rani Garnepudi if (cmd == LOAD_HOSTED_FW || cmd == JUMP_TO_ZERO_PC) { 554b78e91bcSPrameela Rani Garnepudi /* JUMP_TO_ZERO_PC doesn't expect 555b78e91bcSPrameela Rani Garnepudi * any response. So return from here 556b78e91bcSPrameela Rani Garnepudi */ 557b78e91bcSPrameela Rani Garnepudi return 0; 558b78e91bcSPrameela Rani Garnepudi } 559b78e91bcSPrameela Rani Garnepudi 560b78e91bcSPrameela Rani Garnepudi while (!adapter->blcmd_timer_expired) { 561b78e91bcSPrameela Rani Garnepudi regout_val = 0; 562b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_read(adapter, SWBL_REGOUT, 563b78e91bcSPrameela Rani Garnepudi ®out_val, 2); 564b78e91bcSPrameela Rani Garnepudi if (status < 0) { 565b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 566b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGOUT reading failed..\n", 567b78e91bcSPrameela Rani Garnepudi __func__, cmd); 568b78e91bcSPrameela Rani Garnepudi return status; 569b78e91bcSPrameela Rani Garnepudi } 570b78e91bcSPrameela Rani Garnepudi mdelay(1); 571b78e91bcSPrameela Rani Garnepudi if ((regout_val >> 8) == REGOUT_VALID) 572b78e91bcSPrameela Rani Garnepudi break; 573b78e91bcSPrameela Rani Garnepudi } 574b78e91bcSPrameela Rani Garnepudi if (adapter->blcmd_timer_expired) { 575b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 576b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGOUT reading timed out..\n", 577b78e91bcSPrameela Rani Garnepudi __func__, cmd); 578b78e91bcSPrameela Rani Garnepudi return status; 579b78e91bcSPrameela Rani Garnepudi } 580b78e91bcSPrameela Rani Garnepudi 581b78e91bcSPrameela Rani Garnepudi *cmd_resp = ((u16 *)®out_val)[0] & 0xffff; 582b78e91bcSPrameela Rani Garnepudi 583b78e91bcSPrameela Rani Garnepudi output = ((u8 *)®out_val)[0] & 0xff; 584b78e91bcSPrameela Rani Garnepudi 585b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_write(adapter, SWBL_REGOUT, 586b78e91bcSPrameela Rani Garnepudi (cmd | REGOUT_INVALID << 8), 2); 587b78e91bcSPrameela Rani Garnepudi if (status < 0) { 588b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 589b78e91bcSPrameela Rani Garnepudi "%s: Command %0x REGOUT writing failed..\n", 590b78e91bcSPrameela Rani Garnepudi __func__, cmd); 591b78e91bcSPrameela Rani Garnepudi return status; 592b78e91bcSPrameela Rani Garnepudi } 593b78e91bcSPrameela Rani Garnepudi mdelay(1); 594b78e91bcSPrameela Rani Garnepudi 595b78e91bcSPrameela Rani Garnepudi if (output != exp_resp) { 596b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 597b78e91bcSPrameela Rani Garnepudi "%s: Recvd resp %x for cmd %0x\n", 598b78e91bcSPrameela Rani Garnepudi __func__, output, cmd); 599b78e91bcSPrameela Rani Garnepudi return -EINVAL; 600b78e91bcSPrameela Rani Garnepudi } 601b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 602b78e91bcSPrameela Rani Garnepudi "%s: Recvd Expected resp %x for cmd %0x\n", 603b78e91bcSPrameela Rani Garnepudi __func__, output, cmd); 604b78e91bcSPrameela Rani Garnepudi 605b78e91bcSPrameela Rani Garnepudi return 0; 606b78e91bcSPrameela Rani Garnepudi } 607b78e91bcSPrameela Rani Garnepudi 608b78e91bcSPrameela Rani Garnepudi static int bl_cmd(struct rsi_hw *adapter, u8 cmd, u8 exp_resp, char *str) 609b78e91bcSPrameela Rani Garnepudi { 610b78e91bcSPrameela Rani Garnepudi u16 regout_val = 0; 611b78e91bcSPrameela Rani Garnepudi u32 timeout; 612b78e91bcSPrameela Rani Garnepudi int status; 613b78e91bcSPrameela Rani Garnepudi 614b78e91bcSPrameela Rani Garnepudi if ((cmd == EOF_REACHED) || (cmd == PING_VALID) || (cmd == PONG_VALID)) 615b78e91bcSPrameela Rani Garnepudi timeout = BL_BURN_TIMEOUT; 616b78e91bcSPrameela Rani Garnepudi else 617b78e91bcSPrameela Rani Garnepudi timeout = BL_CMD_TIMEOUT; 618b78e91bcSPrameela Rani Garnepudi 619b78e91bcSPrameela Rani Garnepudi bl_start_cmd_timer(adapter, timeout); 620b78e91bcSPrameela Rani Garnepudi status = bl_write_cmd(adapter, cmd, exp_resp, ®out_val); 621b78e91bcSPrameela Rani Garnepudi if (status < 0) { 622b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 623b78e91bcSPrameela Rani Garnepudi "%s: Command %s (%0x) writing failed..\n", 624b78e91bcSPrameela Rani Garnepudi __func__, str, cmd); 625b78e91bcSPrameela Rani Garnepudi return status; 626b78e91bcSPrameela Rani Garnepudi } 627b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 628b78e91bcSPrameela Rani Garnepudi return 0; 629b78e91bcSPrameela Rani Garnepudi } 630b78e91bcSPrameela Rani Garnepudi 631b78e91bcSPrameela Rani Garnepudi #define CHECK_SUM_OFFSET 20 632b78e91bcSPrameela Rani Garnepudi #define LEN_OFFSET 8 633b78e91bcSPrameela Rani Garnepudi #define ADDR_OFFSET 16 634b78e91bcSPrameela Rani Garnepudi static int bl_write_header(struct rsi_hw *adapter, u8 *flash_content, 635b78e91bcSPrameela Rani Garnepudi u32 content_size) 636b78e91bcSPrameela Rani Garnepudi { 637b78e91bcSPrameela Rani Garnepudi struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 638f7005466SSiva Rebbagondla struct bl_header *bl_hdr; 639b78e91bcSPrameela Rani Garnepudi u32 write_addr, write_len; 640b78e91bcSPrameela Rani Garnepudi int status; 641b78e91bcSPrameela Rani Garnepudi 642f7005466SSiva Rebbagondla bl_hdr = kzalloc(sizeof(*bl_hdr), GFP_KERNEL); 643f7005466SSiva Rebbagondla if (!bl_hdr) 644f7005466SSiva Rebbagondla return -ENOMEM; 645f7005466SSiva Rebbagondla 646f7005466SSiva Rebbagondla bl_hdr->flags = 0; 647f7005466SSiva Rebbagondla bl_hdr->image_no = cpu_to_le32(adapter->priv->coex_mode); 648f7005466SSiva Rebbagondla bl_hdr->check_sum = 649f7005466SSiva Rebbagondla cpu_to_le32(*(u32 *)&flash_content[CHECK_SUM_OFFSET]); 650f7005466SSiva Rebbagondla bl_hdr->flash_start_address = 651f7005466SSiva Rebbagondla cpu_to_le32(*(u32 *)&flash_content[ADDR_OFFSET]); 652f7005466SSiva Rebbagondla bl_hdr->flash_len = cpu_to_le32(*(u32 *)&flash_content[LEN_OFFSET]); 653b78e91bcSPrameela Rani Garnepudi write_len = sizeof(struct bl_header); 654b78e91bcSPrameela Rani Garnepudi 655b78e91bcSPrameela Rani Garnepudi if (adapter->rsi_host_intf == RSI_HOST_INTF_USB) { 656b78e91bcSPrameela Rani Garnepudi write_addr = PING_BUFFER_ADDRESS; 657b78e91bcSPrameela Rani Garnepudi status = hif_ops->write_reg_multiple(adapter, write_addr, 658f7005466SSiva Rebbagondla (u8 *)bl_hdr, write_len); 659b78e91bcSPrameela Rani Garnepudi if (status < 0) { 660b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 661b78e91bcSPrameela Rani Garnepudi "%s: Failed to load Version/CRC structure\n", 662b78e91bcSPrameela Rani Garnepudi __func__); 663f7005466SSiva Rebbagondla goto fail; 664b78e91bcSPrameela Rani Garnepudi } 665b78e91bcSPrameela Rani Garnepudi } else { 666b78e91bcSPrameela Rani Garnepudi write_addr = PING_BUFFER_ADDRESS >> 16; 667b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_access_msword(adapter, write_addr); 668b78e91bcSPrameela Rani Garnepudi if (status < 0) { 669b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 670b78e91bcSPrameela Rani Garnepudi "%s: Unable to set ms word to common reg\n", 671b78e91bcSPrameela Rani Garnepudi __func__); 672f7005466SSiva Rebbagondla goto fail; 673b78e91bcSPrameela Rani Garnepudi } 674b78e91bcSPrameela Rani Garnepudi write_addr = RSI_SD_REQUEST_MASTER | 675b78e91bcSPrameela Rani Garnepudi (PING_BUFFER_ADDRESS & 0xFFFF); 676b78e91bcSPrameela Rani Garnepudi status = hif_ops->write_reg_multiple(adapter, write_addr, 677f7005466SSiva Rebbagondla (u8 *)bl_hdr, write_len); 678b78e91bcSPrameela Rani Garnepudi if (status < 0) { 679b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 680b78e91bcSPrameela Rani Garnepudi "%s: Failed to load Version/CRC structure\n", 681b78e91bcSPrameela Rani Garnepudi __func__); 682f7005466SSiva Rebbagondla goto fail; 683f7005466SSiva Rebbagondla } 684f7005466SSiva Rebbagondla } 685f7005466SSiva Rebbagondla status = 0; 686f7005466SSiva Rebbagondla fail: 687f7005466SSiva Rebbagondla kfree(bl_hdr); 688b78e91bcSPrameela Rani Garnepudi return status; 689b78e91bcSPrameela Rani Garnepudi } 690b78e91bcSPrameela Rani Garnepudi 691b78e91bcSPrameela Rani Garnepudi static u32 read_flash_capacity(struct rsi_hw *adapter) 692b78e91bcSPrameela Rani Garnepudi { 693b78e91bcSPrameela Rani Garnepudi u32 flash_sz = 0; 694b78e91bcSPrameela Rani Garnepudi 695b78e91bcSPrameela Rani Garnepudi if ((adapter->host_intf_ops->master_reg_read(adapter, FLASH_SIZE_ADDR, 696b78e91bcSPrameela Rani Garnepudi &flash_sz, 2)) < 0) { 697b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 698b78e91bcSPrameela Rani Garnepudi "%s: Flash size reading failed..\n", 699b78e91bcSPrameela Rani Garnepudi __func__); 700b78e91bcSPrameela Rani Garnepudi return 0; 701b78e91bcSPrameela Rani Garnepudi } 702b78e91bcSPrameela Rani Garnepudi rsi_dbg(INIT_ZONE, "Flash capacity: %d KiloBytes\n", flash_sz); 703b78e91bcSPrameela Rani Garnepudi 704b78e91bcSPrameela Rani Garnepudi return (flash_sz * 1024); /* Return size in kbytes */ 705b78e91bcSPrameela Rani Garnepudi } 706b78e91bcSPrameela Rani Garnepudi 707b78e91bcSPrameela Rani Garnepudi static int ping_pong_write(struct rsi_hw *adapter, u8 cmd, u8 *addr, u32 size) 708b78e91bcSPrameela Rani Garnepudi { 709b78e91bcSPrameela Rani Garnepudi struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 710b78e91bcSPrameela Rani Garnepudi u32 block_size = adapter->block_size; 711b78e91bcSPrameela Rani Garnepudi u32 cmd_addr; 712b78e91bcSPrameela Rani Garnepudi u16 cmd_resp, cmd_req; 713b78e91bcSPrameela Rani Garnepudi u8 *str; 714b78e91bcSPrameela Rani Garnepudi int status; 715b78e91bcSPrameela Rani Garnepudi 716b78e91bcSPrameela Rani Garnepudi if (cmd == PING_WRITE) { 717b78e91bcSPrameela Rani Garnepudi cmd_addr = PING_BUFFER_ADDRESS; 718b78e91bcSPrameela Rani Garnepudi cmd_resp = PONG_AVAIL; 719b78e91bcSPrameela Rani Garnepudi cmd_req = PING_VALID; 720b78e91bcSPrameela Rani Garnepudi str = "PING_VALID"; 721b78e91bcSPrameela Rani Garnepudi } else { 722b78e91bcSPrameela Rani Garnepudi cmd_addr = PONG_BUFFER_ADDRESS; 723b78e91bcSPrameela Rani Garnepudi cmd_resp = PING_AVAIL; 724b78e91bcSPrameela Rani Garnepudi cmd_req = PONG_VALID; 725b78e91bcSPrameela Rani Garnepudi str = "PONG_VALID"; 726b78e91bcSPrameela Rani Garnepudi } 727b78e91bcSPrameela Rani Garnepudi 728b78e91bcSPrameela Rani Garnepudi status = hif_ops->load_data_master_write(adapter, cmd_addr, size, 729b78e91bcSPrameela Rani Garnepudi block_size, addr); 730b78e91bcSPrameela Rani Garnepudi if (status) { 731b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Unable to write blk at addr %0x\n", 732b78e91bcSPrameela Rani Garnepudi __func__, *addr); 733b78e91bcSPrameela Rani Garnepudi return status; 734b78e91bcSPrameela Rani Garnepudi } 735b78e91bcSPrameela Rani Garnepudi 736b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, cmd_req, cmd_resp, str); 737b78e91bcSPrameela Rani Garnepudi if (status) { 738b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 739b78e91bcSPrameela Rani Garnepudi return status; 740b78e91bcSPrameela Rani Garnepudi } 741b78e91bcSPrameela Rani Garnepudi return 0; 742b78e91bcSPrameela Rani Garnepudi } 743b78e91bcSPrameela Rani Garnepudi 744b78e91bcSPrameela Rani Garnepudi static int auto_fw_upgrade(struct rsi_hw *adapter, u8 *flash_content, 745b78e91bcSPrameela Rani Garnepudi u32 content_size) 746b78e91bcSPrameela Rani Garnepudi { 747b78e91bcSPrameela Rani Garnepudi u8 cmd, *temp_flash_content; 748b78e91bcSPrameela Rani Garnepudi u32 temp_content_size, num_flash, index; 749b78e91bcSPrameela Rani Garnepudi u32 flash_start_address; 750b78e91bcSPrameela Rani Garnepudi int status; 751b78e91bcSPrameela Rani Garnepudi 752b78e91bcSPrameela Rani Garnepudi temp_flash_content = flash_content; 753b78e91bcSPrameela Rani Garnepudi 754b78e91bcSPrameela Rani Garnepudi if (content_size > MAX_FLASH_FILE_SIZE) { 755b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 756b78e91bcSPrameela Rani Garnepudi "%s: Flash Content size is more than 400K %u\n", 757b78e91bcSPrameela Rani Garnepudi __func__, MAX_FLASH_FILE_SIZE); 758b78e91bcSPrameela Rani Garnepudi return -EINVAL; 759b78e91bcSPrameela Rani Garnepudi } 760b78e91bcSPrameela Rani Garnepudi 761b78e91bcSPrameela Rani Garnepudi flash_start_address = *(u32 *)&flash_content[FLASH_START_ADDRESS]; 762b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "flash start address: %08x\n", flash_start_address); 763b78e91bcSPrameela Rani Garnepudi 764b78e91bcSPrameela Rani Garnepudi if (flash_start_address < FW_IMAGE_MIN_ADDRESS) { 765b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 766b78e91bcSPrameela Rani Garnepudi "%s: Fw image Flash Start Address is less than 64K\n", 767b78e91bcSPrameela Rani Garnepudi __func__); 768b78e91bcSPrameela Rani Garnepudi return -EINVAL; 769b78e91bcSPrameela Rani Garnepudi } 770b78e91bcSPrameela Rani Garnepudi 771b78e91bcSPrameela Rani Garnepudi if (flash_start_address % FLASH_SECTOR_SIZE) { 772b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 773b78e91bcSPrameela Rani Garnepudi "%s: Flash Start Address is not multiple of 4K\n", 774b78e91bcSPrameela Rani Garnepudi __func__); 775b78e91bcSPrameela Rani Garnepudi return -EINVAL; 776b78e91bcSPrameela Rani Garnepudi } 777b78e91bcSPrameela Rani Garnepudi 778b78e91bcSPrameela Rani Garnepudi if ((flash_start_address + content_size) > adapter->flash_capacity) { 779b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 780b78e91bcSPrameela Rani Garnepudi "%s: Flash Content will cross max flash size\n", 781b78e91bcSPrameela Rani Garnepudi __func__); 782b78e91bcSPrameela Rani Garnepudi return -EINVAL; 783b78e91bcSPrameela Rani Garnepudi } 784b78e91bcSPrameela Rani Garnepudi 785b78e91bcSPrameela Rani Garnepudi temp_content_size = content_size; 786b78e91bcSPrameela Rani Garnepudi num_flash = content_size / FLASH_WRITE_CHUNK_SIZE; 787b78e91bcSPrameela Rani Garnepudi 788b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "content_size: %d, num_flash: %d\n", 789b78e91bcSPrameela Rani Garnepudi content_size, num_flash); 790b78e91bcSPrameela Rani Garnepudi 791b78e91bcSPrameela Rani Garnepudi for (index = 0; index <= num_flash; index++) { 792b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "flash index: %d\n", index); 793b78e91bcSPrameela Rani Garnepudi if (index != num_flash) { 794b78e91bcSPrameela Rani Garnepudi content_size = FLASH_WRITE_CHUNK_SIZE; 795b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "QSPI content_size:%d\n", 796b78e91bcSPrameela Rani Garnepudi content_size); 797b78e91bcSPrameela Rani Garnepudi } else { 798b78e91bcSPrameela Rani Garnepudi content_size = 799b78e91bcSPrameela Rani Garnepudi temp_content_size % FLASH_WRITE_CHUNK_SIZE; 800b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 801b78e91bcSPrameela Rani Garnepudi "Writing last sector content_size:%d\n", 802b78e91bcSPrameela Rani Garnepudi content_size); 803b78e91bcSPrameela Rani Garnepudi if (!content_size) { 804b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "instruction size zero\n"); 805b78e91bcSPrameela Rani Garnepudi break; 806b78e91bcSPrameela Rani Garnepudi } 807b78e91bcSPrameela Rani Garnepudi } 808b78e91bcSPrameela Rani Garnepudi 809b78e91bcSPrameela Rani Garnepudi if (index % 2) 810b78e91bcSPrameela Rani Garnepudi cmd = PING_WRITE; 811b78e91bcSPrameela Rani Garnepudi else 812b78e91bcSPrameela Rani Garnepudi cmd = PONG_WRITE; 813b78e91bcSPrameela Rani Garnepudi 814b78e91bcSPrameela Rani Garnepudi status = ping_pong_write(adapter, cmd, flash_content, 815b78e91bcSPrameela Rani Garnepudi content_size); 816b78e91bcSPrameela Rani Garnepudi if (status) { 817b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Unable to load %d block\n", 818b78e91bcSPrameela Rani Garnepudi __func__, index); 819b78e91bcSPrameela Rani Garnepudi return status; 820b78e91bcSPrameela Rani Garnepudi } 821b78e91bcSPrameela Rani Garnepudi 822b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 823b78e91bcSPrameela Rani Garnepudi "%s: Successfully loaded %d instructions\n", 824b78e91bcSPrameela Rani Garnepudi __func__, index); 825b78e91bcSPrameela Rani Garnepudi flash_content += content_size; 826b78e91bcSPrameela Rani Garnepudi } 827b78e91bcSPrameela Rani Garnepudi 828b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, EOF_REACHED, FW_LOADING_SUCCESSFUL, 829b78e91bcSPrameela Rani Garnepudi "EOF_REACHED"); 830b78e91bcSPrameela Rani Garnepudi if (status) { 831b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 832b78e91bcSPrameela Rani Garnepudi return status; 833b78e91bcSPrameela Rani Garnepudi } 834b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "FW loading is done and FW is running..\n"); 835b78e91bcSPrameela Rani Garnepudi return 0; 836b78e91bcSPrameela Rani Garnepudi } 837b78e91bcSPrameela Rani Garnepudi 838b78e91bcSPrameela Rani Garnepudi static int rsi_load_firmware(struct rsi_hw *adapter) 839b78e91bcSPrameela Rani Garnepudi { 840192524a4SPavani Muthyala struct rsi_common *common = adapter->priv; 841b78e91bcSPrameela Rani Garnepudi struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; 842b78e91bcSPrameela Rani Garnepudi const struct firmware *fw_entry = NULL; 843b78e91bcSPrameela Rani Garnepudi u32 regout_val = 0, content_size; 844b78e91bcSPrameela Rani Garnepudi u16 tmp_regout_val = 0; 845b78e91bcSPrameela Rani Garnepudi u8 *flash_content = NULL; 846b78e91bcSPrameela Rani Garnepudi struct ta_metadata *metadata_p; 847b78e91bcSPrameela Rani Garnepudi int status; 848b78e91bcSPrameela Rani Garnepudi 849b78e91bcSPrameela Rani Garnepudi bl_start_cmd_timer(adapter, BL_CMD_TIMEOUT); 850b78e91bcSPrameela Rani Garnepudi 851b78e91bcSPrameela Rani Garnepudi while (!adapter->blcmd_timer_expired) { 852b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_read(adapter, SWBL_REGOUT, 853b78e91bcSPrameela Rani Garnepudi ®out_val, 2); 854b78e91bcSPrameela Rani Garnepudi if (status < 0) { 855b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 856b78e91bcSPrameela Rani Garnepudi "%s: REGOUT read failed\n", __func__); 857b78e91bcSPrameela Rani Garnepudi return status; 858b78e91bcSPrameela Rani Garnepudi } 859b78e91bcSPrameela Rani Garnepudi mdelay(1); 860b78e91bcSPrameela Rani Garnepudi if ((regout_val >> 8) == REGOUT_VALID) 861b78e91bcSPrameela Rani Garnepudi break; 862b78e91bcSPrameela Rani Garnepudi } 863b78e91bcSPrameela Rani Garnepudi if (adapter->blcmd_timer_expired) { 864b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: REGOUT read timedout\n", __func__); 865b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 866b78e91bcSPrameela Rani Garnepudi "%s: Soft boot loader not present\n", __func__); 867b78e91bcSPrameela Rani Garnepudi return -ETIMEDOUT; 868b78e91bcSPrameela Rani Garnepudi } 869b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 870b78e91bcSPrameela Rani Garnepudi 871b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Received Board Version Number: %x\n", 872b78e91bcSPrameela Rani Garnepudi (regout_val & 0xff)); 873b78e91bcSPrameela Rani Garnepudi 874b78e91bcSPrameela Rani Garnepudi status = hif_ops->master_reg_write(adapter, SWBL_REGOUT, 875b78e91bcSPrameela Rani Garnepudi (REGOUT_INVALID | REGOUT_INVALID << 8), 876b78e91bcSPrameela Rani Garnepudi 2); 877b78e91bcSPrameela Rani Garnepudi if (status < 0) { 878b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: REGOUT writing failed..\n", __func__); 879b78e91bcSPrameela Rani Garnepudi return status; 880b78e91bcSPrameela Rani Garnepudi } 881b78e91bcSPrameela Rani Garnepudi mdelay(1); 882b78e91bcSPrameela Rani Garnepudi 883b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, CONFIG_AUTO_READ_MODE, CMD_PASS, 884b78e91bcSPrameela Rani Garnepudi "AUTO_READ_CMD"); 885b78e91bcSPrameela Rani Garnepudi if (status < 0) 886b78e91bcSPrameela Rani Garnepudi return status; 887b78e91bcSPrameela Rani Garnepudi 888b78e91bcSPrameela Rani Garnepudi adapter->flash_capacity = read_flash_capacity(adapter); 889b78e91bcSPrameela Rani Garnepudi if (adapter->flash_capacity <= 0) { 890b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 891b78e91bcSPrameela Rani Garnepudi "%s: Unable to read flash size from EEPROM\n", 892b78e91bcSPrameela Rani Garnepudi __func__); 893b78e91bcSPrameela Rani Garnepudi return -EINVAL; 894b78e91bcSPrameela Rani Garnepudi } 895b78e91bcSPrameela Rani Garnepudi 896b78e91bcSPrameela Rani Garnepudi metadata_p = &metadata_flash_content[adapter->priv->coex_mode]; 897b78e91bcSPrameela Rani Garnepudi 898b78e91bcSPrameela Rani Garnepudi rsi_dbg(INIT_ZONE, "%s: Loading file %s\n", __func__, metadata_p->name); 899b78e91bcSPrameela Rani Garnepudi adapter->fw_file_name = metadata_p->name; 900b78e91bcSPrameela Rani Garnepudi 901b78e91bcSPrameela Rani Garnepudi status = request_firmware(&fw_entry, metadata_p->name, adapter->device); 902b78e91bcSPrameela Rani Garnepudi if (status < 0) { 903b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Failed to open file %s\n", 904b78e91bcSPrameela Rani Garnepudi __func__, metadata_p->name); 905b78e91bcSPrameela Rani Garnepudi return status; 906b78e91bcSPrameela Rani Garnepudi } 907b78e91bcSPrameela Rani Garnepudi flash_content = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); 908b78e91bcSPrameela Rani Garnepudi if (!flash_content) { 909b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Failed to copy firmware\n", __func__); 910b78e91bcSPrameela Rani Garnepudi status = -EIO; 911b78e91bcSPrameela Rani Garnepudi goto fail; 912b78e91bcSPrameela Rani Garnepudi } 913b78e91bcSPrameela Rani Garnepudi content_size = fw_entry->size; 914b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "FW Length = %d bytes\n", content_size); 915b78e91bcSPrameela Rani Garnepudi 916192524a4SPavani Muthyala /* Get the firmware version */ 917192524a4SPavani Muthyala common->lmac_ver.ver.info.fw_ver[0] = 918192524a4SPavani Muthyala flash_content[LMAC_VER_OFFSET] & 0xFF; 919192524a4SPavani Muthyala common->lmac_ver.ver.info.fw_ver[1] = 920192524a4SPavani Muthyala flash_content[LMAC_VER_OFFSET + 1] & 0xFF; 921192524a4SPavani Muthyala common->lmac_ver.major = flash_content[LMAC_VER_OFFSET + 2] & 0xFF; 922192524a4SPavani Muthyala common->lmac_ver.release_num = 923192524a4SPavani Muthyala flash_content[LMAC_VER_OFFSET + 3] & 0xFF; 924192524a4SPavani Muthyala common->lmac_ver.minor = flash_content[LMAC_VER_OFFSET + 4] & 0xFF; 925192524a4SPavani Muthyala common->lmac_ver.patch_num = 0; 926192524a4SPavani Muthyala rsi_print_version(common); 927192524a4SPavani Muthyala 928b78e91bcSPrameela Rani Garnepudi status = bl_write_header(adapter, flash_content, content_size); 929b78e91bcSPrameela Rani Garnepudi if (status) { 930b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 931b78e91bcSPrameela Rani Garnepudi "%s: RPS Image header loading failed\n", 932b78e91bcSPrameela Rani Garnepudi __func__); 933b78e91bcSPrameela Rani Garnepudi goto fail; 934b78e91bcSPrameela Rani Garnepudi } 935b78e91bcSPrameela Rani Garnepudi 936b78e91bcSPrameela Rani Garnepudi bl_start_cmd_timer(adapter, BL_CMD_TIMEOUT); 937b78e91bcSPrameela Rani Garnepudi status = bl_write_cmd(adapter, CHECK_CRC, CMD_PASS, &tmp_regout_val); 938b78e91bcSPrameela Rani Garnepudi if (status) { 939b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 940b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 941b78e91bcSPrameela Rani Garnepudi "%s: CHECK_CRC Command writing failed..\n", 942b78e91bcSPrameela Rani Garnepudi __func__); 943b78e91bcSPrameela Rani Garnepudi if ((tmp_regout_val & 0xff) == CMD_FAIL) { 944b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 945b78e91bcSPrameela Rani Garnepudi "CRC Fail.. Proceeding to Upgrade mode\n"); 946b78e91bcSPrameela Rani Garnepudi goto fw_upgrade; 947b78e91bcSPrameela Rani Garnepudi } 948b78e91bcSPrameela Rani Garnepudi } 949b78e91bcSPrameela Rani Garnepudi bl_stop_cmd_timer(adapter); 950b78e91bcSPrameela Rani Garnepudi 951b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, POLLING_MODE, CMD_PASS, "POLLING_MODE"); 952b78e91bcSPrameela Rani Garnepudi if (status) 953b78e91bcSPrameela Rani Garnepudi goto fail; 954b78e91bcSPrameela Rani Garnepudi 955b78e91bcSPrameela Rani Garnepudi load_image_cmd: 956b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, LOAD_HOSTED_FW, LOADING_INITIATED, 957b78e91bcSPrameela Rani Garnepudi "LOAD_HOSTED_FW"); 958b78e91bcSPrameela Rani Garnepudi if (status) 959b78e91bcSPrameela Rani Garnepudi goto fail; 960b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Load Image command passed..\n"); 961b78e91bcSPrameela Rani Garnepudi goto success; 962b78e91bcSPrameela Rani Garnepudi 963b78e91bcSPrameela Rani Garnepudi fw_upgrade: 964b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, BURN_HOSTED_FW, SEND_RPS_FILE, "FW_UPGRADE"); 965b78e91bcSPrameela Rani Garnepudi if (status) 966b78e91bcSPrameela Rani Garnepudi goto fail; 967b78e91bcSPrameela Rani Garnepudi 968b78e91bcSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Burn Command Pass.. Upgrading the firmware\n"); 969b78e91bcSPrameela Rani Garnepudi 970b78e91bcSPrameela Rani Garnepudi status = auto_fw_upgrade(adapter, flash_content, content_size); 971b78e91bcSPrameela Rani Garnepudi if (status == 0) { 972b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Firmware upgradation Done\n"); 973b78e91bcSPrameela Rani Garnepudi goto load_image_cmd; 974b78e91bcSPrameela Rani Garnepudi } 975b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Firmware upgrade failed\n"); 976b78e91bcSPrameela Rani Garnepudi 977b78e91bcSPrameela Rani Garnepudi status = bl_cmd(adapter, CONFIG_AUTO_READ_MODE, CMD_PASS, 978b78e91bcSPrameela Rani Garnepudi "AUTO_READ_MODE"); 979b78e91bcSPrameela Rani Garnepudi if (status) 980b78e91bcSPrameela Rani Garnepudi goto fail; 981b78e91bcSPrameela Rani Garnepudi 982b78e91bcSPrameela Rani Garnepudi success: 983b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "***** Firmware Loading successful *****\n"); 984b78e91bcSPrameela Rani Garnepudi kfree(flash_content); 985b78e91bcSPrameela Rani Garnepudi release_firmware(fw_entry); 986b78e91bcSPrameela Rani Garnepudi return 0; 987b78e91bcSPrameela Rani Garnepudi 988b78e91bcSPrameela Rani Garnepudi fail: 989b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "##### Firmware loading failed #####\n"); 990b78e91bcSPrameela Rani Garnepudi kfree(flash_content); 991b78e91bcSPrameela Rani Garnepudi release_firmware(fw_entry); 992b78e91bcSPrameela Rani Garnepudi return status; 993b78e91bcSPrameela Rani Garnepudi } 994b78e91bcSPrameela Rani Garnepudi 995b78e91bcSPrameela Rani Garnepudi int rsi_hal_device_init(struct rsi_hw *adapter) 996b78e91bcSPrameela Rani Garnepudi { 997b78e91bcSPrameela Rani Garnepudi struct rsi_common *common = adapter->priv; 998b78e91bcSPrameela Rani Garnepudi 999b78e91bcSPrameela Rani Garnepudi switch (adapter->device_model) { 1000b78e91bcSPrameela Rani Garnepudi case RSI_DEV_9113: 1001b78e91bcSPrameela Rani Garnepudi if (rsi_load_firmware(adapter)) { 1002b78e91bcSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 1003b78e91bcSPrameela Rani Garnepudi "%s: Failed to load TA instructions\n", 1004b78e91bcSPrameela Rani Garnepudi __func__); 1005b78e91bcSPrameela Rani Garnepudi return -EINVAL; 1006b78e91bcSPrameela Rani Garnepudi } 1007b78e91bcSPrameela Rani Garnepudi break; 1008b78e91bcSPrameela Rani Garnepudi default: 1009b78e91bcSPrameela Rani Garnepudi return -EINVAL; 1010b78e91bcSPrameela Rani Garnepudi } 1011015e3674SPrameela Rani Garnepudi common->fsm_state = FSM_CARD_NOT_READY; 1012b78e91bcSPrameela Rani Garnepudi 1013b78e91bcSPrameela Rani Garnepudi return 0; 1014b78e91bcSPrameela Rani Garnepudi } 1015b78e91bcSPrameela Rani Garnepudi EXPORT_SYMBOL_GPL(rsi_hal_device_init); 1016b78e91bcSPrameela Rani Garnepudi 1017