xref: /linux/drivers/net/wireless/rsi/rsi_91x_hal.c (revision 92e9712381383dd244a447b9e96a8065faaf3570)
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 						  &regin_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 					     &regout_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 *)&regout_val)[0] & 0xffff;
582b78e91bcSPrameela Rani Garnepudi 
583b78e91bcSPrameela Rani Garnepudi 	output = ((u8 *)&regout_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, &regout_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 					      &regout_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