18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
48c2ecf20Sopenharmony_ci * All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
88c2ecf20Sopenharmony_ci#include <linux/ip.h>
98c2ecf20Sopenharmony_ci#include "cfg80211.h"
108c2ecf20Sopenharmony_ci#include "wlan_cfg.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic inline bool is_wilc1000(u32 id)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	return (id & (~WILC_CHIP_REV_FIELD)) == WILC_1000_BASE_ID;
158c2ecf20Sopenharmony_ci}
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic inline void acquire_bus(struct wilc *wilc, enum bus_acquire acquire)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	mutex_lock(&wilc->hif_cs);
208c2ecf20Sopenharmony_ci	if (acquire == WILC_BUS_ACQUIRE_AND_WAKEUP)
218c2ecf20Sopenharmony_ci		chip_wakeup(wilc);
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic inline void release_bus(struct wilc *wilc, enum bus_release release)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	if (release == WILC_BUS_RELEASE_ALLOW_SLEEP)
278c2ecf20Sopenharmony_ci		chip_allow_sleep(wilc);
288c2ecf20Sopenharmony_ci	mutex_unlock(&wilc->hif_cs);
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void wilc_wlan_txq_remove(struct wilc *wilc, struct txq_entry_t *tqe)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	list_del(&tqe->list);
348c2ecf20Sopenharmony_ci	wilc->txq_entries -= 1;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic struct txq_entry_t *
388c2ecf20Sopenharmony_ciwilc_wlan_txq_remove_from_head(struct net_device *dev)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct txq_entry_t *tqe = NULL;
418c2ecf20Sopenharmony_ci	unsigned long flags;
428c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
438c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wilc->txq_spinlock, flags);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (!list_empty(&wilc->txq_head.list)) {
488c2ecf20Sopenharmony_ci		tqe = list_first_entry(&wilc->txq_head.list, struct txq_entry_t,
498c2ecf20Sopenharmony_ci				       list);
508c2ecf20Sopenharmony_ci		list_del(&tqe->list);
518c2ecf20Sopenharmony_ci		wilc->txq_entries -= 1;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
548c2ecf20Sopenharmony_ci	return tqe;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void wilc_wlan_txq_add_to_tail(struct net_device *dev,
588c2ecf20Sopenharmony_ci				      struct txq_entry_t *tqe)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	unsigned long flags;
618c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
628c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wilc->txq_spinlock, flags);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	list_add_tail(&tqe->list, &wilc->txq_head.list);
678c2ecf20Sopenharmony_ci	wilc->txq_entries += 1;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	complete(&wilc->txq_event);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void wilc_wlan_txq_add_to_head(struct wilc_vif *vif,
758c2ecf20Sopenharmony_ci				      struct txq_entry_t *tqe)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	unsigned long flags;
788c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	mutex_lock(&wilc->txq_add_to_head_cs);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wilc->txq_spinlock, flags);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	list_add(&tqe->list, &wilc->txq_head.list);
858c2ecf20Sopenharmony_ci	wilc->txq_entries += 1;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
888c2ecf20Sopenharmony_ci	mutex_unlock(&wilc->txq_add_to_head_cs);
898c2ecf20Sopenharmony_ci	complete(&wilc->txq_event);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define NOT_TCP_ACK			(-1)
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic inline void add_tcp_session(struct wilc_vif *vif, u32 src_prt,
958c2ecf20Sopenharmony_ci				   u32 dst_prt, u32 seq)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct tcp_ack_filter *f = &vif->ack_filter;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (f->tcp_session < 2 * MAX_TCP_SESSION) {
1008c2ecf20Sopenharmony_ci		f->ack_session_info[f->tcp_session].seq_num = seq;
1018c2ecf20Sopenharmony_ci		f->ack_session_info[f->tcp_session].bigger_ack_num = 0;
1028c2ecf20Sopenharmony_ci		f->ack_session_info[f->tcp_session].src_port = src_prt;
1038c2ecf20Sopenharmony_ci		f->ack_session_info[f->tcp_session].dst_port = dst_prt;
1048c2ecf20Sopenharmony_ci		f->tcp_session++;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic inline void update_tcp_session(struct wilc_vif *vif, u32 index, u32 ack)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct tcp_ack_filter *f = &vif->ack_filter;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (index < 2 * MAX_TCP_SESSION &&
1138c2ecf20Sopenharmony_ci	    ack > f->ack_session_info[index].bigger_ack_num)
1148c2ecf20Sopenharmony_ci		f->ack_session_info[index].bigger_ack_num = ack;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic inline void add_tcp_pending_ack(struct wilc_vif *vif, u32 ack,
1188c2ecf20Sopenharmony_ci				       u32 session_index,
1198c2ecf20Sopenharmony_ci				       struct txq_entry_t *txqe)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct tcp_ack_filter *f = &vif->ack_filter;
1228c2ecf20Sopenharmony_ci	u32 i = f->pending_base + f->pending_acks_idx;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (i < MAX_PENDING_ACKS) {
1258c2ecf20Sopenharmony_ci		f->pending_acks[i].ack_num = ack;
1268c2ecf20Sopenharmony_ci		f->pending_acks[i].txqe = txqe;
1278c2ecf20Sopenharmony_ci		f->pending_acks[i].session_index = session_index;
1288c2ecf20Sopenharmony_ci		txqe->ack_idx = i;
1298c2ecf20Sopenharmony_ci		f->pending_acks_idx++;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic inline void tcp_process(struct net_device *dev, struct txq_entry_t *tqe)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	void *buffer = tqe->buffer;
1368c2ecf20Sopenharmony_ci	const struct ethhdr *eth_hdr_ptr = buffer;
1378c2ecf20Sopenharmony_ci	int i;
1388c2ecf20Sopenharmony_ci	unsigned long flags;
1398c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
1408c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
1418c2ecf20Sopenharmony_ci	struct tcp_ack_filter *f = &vif->ack_filter;
1428c2ecf20Sopenharmony_ci	const struct iphdr *ip_hdr_ptr;
1438c2ecf20Sopenharmony_ci	const struct tcphdr *tcp_hdr_ptr;
1448c2ecf20Sopenharmony_ci	u32 ihl, total_length, data_offset;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wilc->txq_spinlock, flags);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (eth_hdr_ptr->h_proto != htons(ETH_P_IP))
1498c2ecf20Sopenharmony_ci		goto out;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	ip_hdr_ptr = buffer + ETH_HLEN;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (ip_hdr_ptr->protocol != IPPROTO_TCP)
1548c2ecf20Sopenharmony_ci		goto out;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ihl = ip_hdr_ptr->ihl << 2;
1578c2ecf20Sopenharmony_ci	tcp_hdr_ptr = buffer + ETH_HLEN + ihl;
1588c2ecf20Sopenharmony_ci	total_length = ntohs(ip_hdr_ptr->tot_len);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	data_offset = tcp_hdr_ptr->doff << 2;
1618c2ecf20Sopenharmony_ci	if (total_length == (ihl + data_offset)) {
1628c2ecf20Sopenharmony_ci		u32 seq_no, ack_no;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		seq_no = ntohl(tcp_hdr_ptr->seq);
1658c2ecf20Sopenharmony_ci		ack_no = ntohl(tcp_hdr_ptr->ack_seq);
1668c2ecf20Sopenharmony_ci		for (i = 0; i < f->tcp_session; i++) {
1678c2ecf20Sopenharmony_ci			u32 j = f->ack_session_info[i].seq_num;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci			if (i < 2 * MAX_TCP_SESSION &&
1708c2ecf20Sopenharmony_ci			    j == seq_no) {
1718c2ecf20Sopenharmony_ci				update_tcp_session(vif, i, ack_no);
1728c2ecf20Sopenharmony_ci				break;
1738c2ecf20Sopenharmony_ci			}
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci		if (i == f->tcp_session)
1768c2ecf20Sopenharmony_ci			add_tcp_session(vif, 0, 0, seq_no);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		add_tcp_pending_ack(vif, ack_no, i, tqe);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ciout:
1828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic void wilc_wlan_txq_filter_dup_tcp_ack(struct net_device *dev)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
1888c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
1898c2ecf20Sopenharmony_ci	struct tcp_ack_filter *f = &vif->ack_filter;
1908c2ecf20Sopenharmony_ci	u32 i = 0;
1918c2ecf20Sopenharmony_ci	u32 dropped = 0;
1928c2ecf20Sopenharmony_ci	unsigned long flags;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wilc->txq_spinlock, flags);
1958c2ecf20Sopenharmony_ci	for (i = f->pending_base;
1968c2ecf20Sopenharmony_ci	     i < (f->pending_base + f->pending_acks_idx); i++) {
1978c2ecf20Sopenharmony_ci		u32 index;
1988c2ecf20Sopenharmony_ci		u32 bigger_ack_num;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		if (i >= MAX_PENDING_ACKS)
2018c2ecf20Sopenharmony_ci			break;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		index = f->pending_acks[i].session_index;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		if (index >= 2 * MAX_TCP_SESSION)
2068c2ecf20Sopenharmony_ci			break;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		bigger_ack_num = f->ack_session_info[index].bigger_ack_num;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		if (f->pending_acks[i].ack_num < bigger_ack_num) {
2118c2ecf20Sopenharmony_ci			struct txq_entry_t *tqe;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci			tqe = f->pending_acks[i].txqe;
2148c2ecf20Sopenharmony_ci			if (tqe) {
2158c2ecf20Sopenharmony_ci				wilc_wlan_txq_remove(wilc, tqe);
2168c2ecf20Sopenharmony_ci				tqe->status = 1;
2178c2ecf20Sopenharmony_ci				if (tqe->tx_complete_func)
2188c2ecf20Sopenharmony_ci					tqe->tx_complete_func(tqe->priv,
2198c2ecf20Sopenharmony_ci							      tqe->status);
2208c2ecf20Sopenharmony_ci				kfree(tqe);
2218c2ecf20Sopenharmony_ci				dropped++;
2228c2ecf20Sopenharmony_ci			}
2238c2ecf20Sopenharmony_ci		}
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci	f->pending_acks_idx = 0;
2268c2ecf20Sopenharmony_ci	f->tcp_session = 0;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (f->pending_base == 0)
2298c2ecf20Sopenharmony_ci		f->pending_base = MAX_TCP_SESSION;
2308c2ecf20Sopenharmony_ci	else
2318c2ecf20Sopenharmony_ci		f->pending_base = 0;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	while (dropped > 0) {
2368c2ecf20Sopenharmony_ci		wait_for_completion_timeout(&wilc->txq_event,
2378c2ecf20Sopenharmony_ci					    msecs_to_jiffies(1));
2388c2ecf20Sopenharmony_ci		dropped--;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_civoid wilc_enable_tcp_ack_filter(struct wilc_vif *vif, bool value)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	vif->ack_filter.enabled = value;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int wilc_wlan_txq_add_cfg_pkt(struct wilc_vif *vif, u8 *buffer,
2488c2ecf20Sopenharmony_ci				     u32 buffer_size)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct txq_entry_t *tqe;
2518c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	netdev_dbg(vif->ndev, "Adding config packet ...\n");
2548c2ecf20Sopenharmony_ci	if (wilc->quit) {
2558c2ecf20Sopenharmony_ci		netdev_dbg(vif->ndev, "Return due to clear function\n");
2568c2ecf20Sopenharmony_ci		complete(&wilc->cfg_event);
2578c2ecf20Sopenharmony_ci		return 0;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
2618c2ecf20Sopenharmony_ci	if (!tqe)
2628c2ecf20Sopenharmony_ci		return 0;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	tqe->type = WILC_CFG_PKT;
2658c2ecf20Sopenharmony_ci	tqe->buffer = buffer;
2668c2ecf20Sopenharmony_ci	tqe->buffer_size = buffer_size;
2678c2ecf20Sopenharmony_ci	tqe->tx_complete_func = NULL;
2688c2ecf20Sopenharmony_ci	tqe->priv = NULL;
2698c2ecf20Sopenharmony_ci	tqe->ack_idx = NOT_TCP_ACK;
2708c2ecf20Sopenharmony_ci	tqe->vif = vif;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	wilc_wlan_txq_add_to_head(vif, tqe);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return 1;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ciint wilc_wlan_txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer,
2788c2ecf20Sopenharmony_ci			      u32 buffer_size,
2798c2ecf20Sopenharmony_ci			      void (*tx_complete_fn)(void *, int))
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct txq_entry_t *tqe;
2828c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
2838c2ecf20Sopenharmony_ci	struct wilc *wilc;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	wilc = vif->wilc;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (wilc->quit)
2888c2ecf20Sopenharmony_ci		return 0;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (!tqe)
2938c2ecf20Sopenharmony_ci		return 0;
2948c2ecf20Sopenharmony_ci	tqe->type = WILC_NET_PKT;
2958c2ecf20Sopenharmony_ci	tqe->buffer = buffer;
2968c2ecf20Sopenharmony_ci	tqe->buffer_size = buffer_size;
2978c2ecf20Sopenharmony_ci	tqe->tx_complete_func = tx_complete_fn;
2988c2ecf20Sopenharmony_ci	tqe->priv = priv;
2998c2ecf20Sopenharmony_ci	tqe->vif = vif;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	tqe->ack_idx = NOT_TCP_ACK;
3028c2ecf20Sopenharmony_ci	if (vif->ack_filter.enabled)
3038c2ecf20Sopenharmony_ci		tcp_process(dev, tqe);
3048c2ecf20Sopenharmony_ci	wilc_wlan_txq_add_to_tail(dev, tqe);
3058c2ecf20Sopenharmony_ci	return wilc->txq_entries;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ciint wilc_wlan_txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
3098c2ecf20Sopenharmony_ci			       u32 buffer_size,
3108c2ecf20Sopenharmony_ci			       void (*tx_complete_fn)(void *, int))
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct txq_entry_t *tqe;
3138c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
3148c2ecf20Sopenharmony_ci	struct wilc *wilc;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	wilc = vif->wilc;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (wilc->quit)
3198c2ecf20Sopenharmony_ci		return 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (!tqe)
3248c2ecf20Sopenharmony_ci		return 0;
3258c2ecf20Sopenharmony_ci	tqe->type = WILC_MGMT_PKT;
3268c2ecf20Sopenharmony_ci	tqe->buffer = buffer;
3278c2ecf20Sopenharmony_ci	tqe->buffer_size = buffer_size;
3288c2ecf20Sopenharmony_ci	tqe->tx_complete_func = tx_complete_fn;
3298c2ecf20Sopenharmony_ci	tqe->priv = priv;
3308c2ecf20Sopenharmony_ci	tqe->ack_idx = NOT_TCP_ACK;
3318c2ecf20Sopenharmony_ci	tqe->vif = vif;
3328c2ecf20Sopenharmony_ci	wilc_wlan_txq_add_to_tail(dev, tqe);
3338c2ecf20Sopenharmony_ci	return 1;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic struct txq_entry_t *wilc_wlan_txq_get_first(struct wilc *wilc)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct txq_entry_t *tqe = NULL;
3398c2ecf20Sopenharmony_ci	unsigned long flags;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wilc->txq_spinlock, flags);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (!list_empty(&wilc->txq_head.list))
3448c2ecf20Sopenharmony_ci		tqe = list_first_entry(&wilc->txq_head.list, struct txq_entry_t,
3458c2ecf20Sopenharmony_ci				       list);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return tqe;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic struct txq_entry_t *wilc_wlan_txq_get_next(struct wilc *wilc,
3538c2ecf20Sopenharmony_ci						  struct txq_entry_t *tqe)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	unsigned long flags;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wilc->txq_spinlock, flags);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (!list_is_last(&tqe->list, &wilc->txq_head.list))
3608c2ecf20Sopenharmony_ci		tqe = list_next_entry(tqe, list);
3618c2ecf20Sopenharmony_ci	else
3628c2ecf20Sopenharmony_ci		tqe = NULL;
3638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return tqe;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void wilc_wlan_rxq_add(struct wilc *wilc, struct rxq_entry_t *rqe)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	if (wilc->quit)
3718c2ecf20Sopenharmony_ci		return;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	mutex_lock(&wilc->rxq_cs);
3748c2ecf20Sopenharmony_ci	list_add_tail(&rqe->list, &wilc->rxq_head.list);
3758c2ecf20Sopenharmony_ci	mutex_unlock(&wilc->rxq_cs);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic struct rxq_entry_t *wilc_wlan_rxq_remove(struct wilc *wilc)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct rxq_entry_t *rqe = NULL;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	mutex_lock(&wilc->rxq_cs);
3838c2ecf20Sopenharmony_ci	if (!list_empty(&wilc->rxq_head.list)) {
3848c2ecf20Sopenharmony_ci		rqe = list_first_entry(&wilc->rxq_head.list, struct rxq_entry_t,
3858c2ecf20Sopenharmony_ci				       list);
3868c2ecf20Sopenharmony_ci		list_del(&rqe->list);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci	mutex_unlock(&wilc->rxq_cs);
3898c2ecf20Sopenharmony_ci	return rqe;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_civoid chip_allow_sleep(struct wilc *wilc)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	u32 reg = 0;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	wilc->hif_func->hif_read_reg(wilc, WILC_SDIO_WAKEUP_REG, &reg);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	wilc->hif_func->hif_write_reg(wilc, WILC_SDIO_WAKEUP_REG,
3998c2ecf20Sopenharmony_ci				      reg & ~WILC_SDIO_WAKEUP_BIT);
4008c2ecf20Sopenharmony_ci	wilc->hif_func->hif_write_reg(wilc, WILC_SDIO_HOST_TO_FW_REG, 0);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chip_allow_sleep);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_civoid chip_wakeup(struct wilc *wilc)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	u32 reg, clk_status_reg;
4078c2ecf20Sopenharmony_ci	const struct wilc_hif_func *h = wilc->hif_func;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (wilc->io_type == WILC_HIF_SPI) {
4108c2ecf20Sopenharmony_ci		do {
4118c2ecf20Sopenharmony_ci			h->hif_read_reg(wilc, WILC_SPI_WAKEUP_REG, &reg);
4128c2ecf20Sopenharmony_ci			h->hif_write_reg(wilc, WILC_SPI_WAKEUP_REG,
4138c2ecf20Sopenharmony_ci					 reg | WILC_SPI_WAKEUP_BIT);
4148c2ecf20Sopenharmony_ci			h->hif_write_reg(wilc, WILC_SPI_WAKEUP_REG,
4158c2ecf20Sopenharmony_ci					 reg & ~WILC_SPI_WAKEUP_BIT);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci			do {
4188c2ecf20Sopenharmony_ci				usleep_range(2000, 2500);
4198c2ecf20Sopenharmony_ci				wilc_get_chipid(wilc, true);
4208c2ecf20Sopenharmony_ci			} while (wilc_get_chipid(wilc, true) == 0);
4218c2ecf20Sopenharmony_ci		} while (wilc_get_chipid(wilc, true) == 0);
4228c2ecf20Sopenharmony_ci	} else if (wilc->io_type == WILC_HIF_SDIO) {
4238c2ecf20Sopenharmony_ci		h->hif_write_reg(wilc, WILC_SDIO_HOST_TO_FW_REG,
4248c2ecf20Sopenharmony_ci				 WILC_SDIO_HOST_TO_FW_BIT);
4258c2ecf20Sopenharmony_ci		usleep_range(200, 400);
4268c2ecf20Sopenharmony_ci		h->hif_read_reg(wilc, WILC_SDIO_WAKEUP_REG, &reg);
4278c2ecf20Sopenharmony_ci		do {
4288c2ecf20Sopenharmony_ci			h->hif_write_reg(wilc, WILC_SDIO_WAKEUP_REG,
4298c2ecf20Sopenharmony_ci					 reg | WILC_SDIO_WAKEUP_BIT);
4308c2ecf20Sopenharmony_ci			h->hif_read_reg(wilc, WILC_SDIO_CLK_STATUS_REG,
4318c2ecf20Sopenharmony_ci					&clk_status_reg);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci			while (!(clk_status_reg & WILC_SDIO_CLK_STATUS_BIT)) {
4348c2ecf20Sopenharmony_ci				usleep_range(2000, 2500);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci				h->hif_read_reg(wilc, WILC_SDIO_CLK_STATUS_REG,
4378c2ecf20Sopenharmony_ci						&clk_status_reg);
4388c2ecf20Sopenharmony_ci			}
4398c2ecf20Sopenharmony_ci			if (!(clk_status_reg & WILC_SDIO_CLK_STATUS_BIT)) {
4408c2ecf20Sopenharmony_ci				h->hif_write_reg(wilc, WILC_SDIO_WAKEUP_REG,
4418c2ecf20Sopenharmony_ci						 reg & ~WILC_SDIO_WAKEUP_BIT);
4428c2ecf20Sopenharmony_ci			}
4438c2ecf20Sopenharmony_ci		} while (!(clk_status_reg & WILC_SDIO_CLK_STATUS_BIT));
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (wilc->chip_ps_state == WILC_CHIP_SLEEPING_MANUAL) {
4478c2ecf20Sopenharmony_ci		if (wilc_get_chipid(wilc, false) < WILC_1000_BASE_ID_2B) {
4488c2ecf20Sopenharmony_ci			u32 val32;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci			h->hif_read_reg(wilc, WILC_REG_4_TO_1_RX, &val32);
4518c2ecf20Sopenharmony_ci			val32 |= BIT(6);
4528c2ecf20Sopenharmony_ci			h->hif_write_reg(wilc, WILC_REG_4_TO_1_RX, val32);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci			h->hif_read_reg(wilc, WILC_REG_4_TO_1_TX_BANK0, &val32);
4558c2ecf20Sopenharmony_ci			val32 |= BIT(6);
4568c2ecf20Sopenharmony_ci			h->hif_write_reg(wilc, WILC_REG_4_TO_1_TX_BANK0, val32);
4578c2ecf20Sopenharmony_ci		}
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci	wilc->chip_ps_state = WILC_CHIP_WAKEDUP;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chip_wakeup);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_civoid host_wakeup_notify(struct wilc *wilc)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
4668c2ecf20Sopenharmony_ci	wilc->hif_func->hif_write_reg(wilc, WILC_CORTUS_INTERRUPT_2, 1);
4678c2ecf20Sopenharmony_ci	release_bus(wilc, WILC_BUS_RELEASE_ONLY);
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(host_wakeup_notify);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_civoid host_sleep_notify(struct wilc *wilc)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
4748c2ecf20Sopenharmony_ci	wilc->hif_func->hif_write_reg(wilc, WILC_CORTUS_INTERRUPT_1, 1);
4758c2ecf20Sopenharmony_ci	release_bus(wilc, WILC_BUS_RELEASE_ONLY);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(host_sleep_notify);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ciint wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	int i, entries = 0;
4828c2ecf20Sopenharmony_ci	u32 sum;
4838c2ecf20Sopenharmony_ci	u32 reg;
4848c2ecf20Sopenharmony_ci	u32 offset = 0;
4858c2ecf20Sopenharmony_ci	int vmm_sz = 0;
4868c2ecf20Sopenharmony_ci	struct txq_entry_t *tqe;
4878c2ecf20Sopenharmony_ci	int ret = 0;
4888c2ecf20Sopenharmony_ci	int counter;
4898c2ecf20Sopenharmony_ci	int timeout;
4908c2ecf20Sopenharmony_ci	u32 vmm_table[WILC_VMM_TBL_SIZE];
4918c2ecf20Sopenharmony_ci	const struct wilc_hif_func *func;
4928c2ecf20Sopenharmony_ci	u8 *txb = wilc->tx_buffer;
4938c2ecf20Sopenharmony_ci	struct net_device *dev;
4948c2ecf20Sopenharmony_ci	struct wilc_vif *vif;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (wilc->quit)
4978c2ecf20Sopenharmony_ci		goto out_update_cnt;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	mutex_lock(&wilc->txq_add_to_head_cs);
5008c2ecf20Sopenharmony_ci	tqe = wilc_wlan_txq_get_first(wilc);
5018c2ecf20Sopenharmony_ci	if (!tqe)
5028c2ecf20Sopenharmony_ci		goto out_unlock;
5038c2ecf20Sopenharmony_ci	dev = tqe->vif->ndev;
5048c2ecf20Sopenharmony_ci	wilc_wlan_txq_filter_dup_tcp_ack(dev);
5058c2ecf20Sopenharmony_ci	i = 0;
5068c2ecf20Sopenharmony_ci	sum = 0;
5078c2ecf20Sopenharmony_ci	while (tqe && (i < (WILC_VMM_TBL_SIZE - 1))) {
5088c2ecf20Sopenharmony_ci		if (tqe->type == WILC_CFG_PKT)
5098c2ecf20Sopenharmony_ci			vmm_sz = ETH_CONFIG_PKT_HDR_OFFSET;
5108c2ecf20Sopenharmony_ci		else if (tqe->type == WILC_NET_PKT)
5118c2ecf20Sopenharmony_ci			vmm_sz = ETH_ETHERNET_HDR_OFFSET;
5128c2ecf20Sopenharmony_ci		else
5138c2ecf20Sopenharmony_ci			vmm_sz = HOST_HDR_OFFSET;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		vmm_sz += tqe->buffer_size;
5168c2ecf20Sopenharmony_ci		vmm_sz = ALIGN(vmm_sz, 4);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		if ((sum + vmm_sz) > WILC_TX_BUFF_SIZE)
5198c2ecf20Sopenharmony_ci			break;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci		vmm_table[i] = vmm_sz / 4;
5228c2ecf20Sopenharmony_ci		if (tqe->type == WILC_CFG_PKT)
5238c2ecf20Sopenharmony_ci			vmm_table[i] |= BIT(10);
5248c2ecf20Sopenharmony_ci		cpu_to_le32s(&vmm_table[i]);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		i++;
5278c2ecf20Sopenharmony_ci		sum += vmm_sz;
5288c2ecf20Sopenharmony_ci		tqe = wilc_wlan_txq_get_next(wilc, tqe);
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (i == 0)
5328c2ecf20Sopenharmony_ci		goto out_unlock;
5338c2ecf20Sopenharmony_ci	vmm_table[i] = 0x0;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
5368c2ecf20Sopenharmony_ci	counter = 0;
5378c2ecf20Sopenharmony_ci	func = wilc->hif_func;
5388c2ecf20Sopenharmony_ci	do {
5398c2ecf20Sopenharmony_ci		ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
5408c2ecf20Sopenharmony_ci		if (ret)
5418c2ecf20Sopenharmony_ci			break;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		if ((reg & 0x1) == 0)
5448c2ecf20Sopenharmony_ci			break;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		counter++;
5478c2ecf20Sopenharmony_ci		if (counter > 200) {
5488c2ecf20Sopenharmony_ci			counter = 0;
5498c2ecf20Sopenharmony_ci			ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0);
5508c2ecf20Sopenharmony_ci			break;
5518c2ecf20Sopenharmony_ci		}
5528c2ecf20Sopenharmony_ci	} while (!wilc->quit);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (ret)
5558c2ecf20Sopenharmony_ci		goto out_release_bus;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	timeout = 200;
5588c2ecf20Sopenharmony_ci	do {
5598c2ecf20Sopenharmony_ci		ret = func->hif_block_tx(wilc,
5608c2ecf20Sopenharmony_ci					 WILC_VMM_TBL_RX_SHADOW_BASE,
5618c2ecf20Sopenharmony_ci					 (u8 *)vmm_table,
5628c2ecf20Sopenharmony_ci					 ((i + 1) * 4));
5638c2ecf20Sopenharmony_ci		if (ret)
5648c2ecf20Sopenharmony_ci			break;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci		ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x2);
5678c2ecf20Sopenharmony_ci		if (ret)
5688c2ecf20Sopenharmony_ci			break;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci		do {
5718c2ecf20Sopenharmony_ci			ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, &reg);
5728c2ecf20Sopenharmony_ci			if (ret)
5738c2ecf20Sopenharmony_ci				break;
5748c2ecf20Sopenharmony_ci			if (FIELD_GET(WILC_VMM_ENTRY_AVAILABLE, reg)) {
5758c2ecf20Sopenharmony_ci				entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, reg);
5768c2ecf20Sopenharmony_ci				break;
5778c2ecf20Sopenharmony_ci			}
5788c2ecf20Sopenharmony_ci		} while (--timeout);
5798c2ecf20Sopenharmony_ci		if (timeout <= 0) {
5808c2ecf20Sopenharmony_ci			ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0);
5818c2ecf20Sopenharmony_ci			break;
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci		if (ret)
5858c2ecf20Sopenharmony_ci			break;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci		if (entries == 0) {
5888c2ecf20Sopenharmony_ci			ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
5898c2ecf20Sopenharmony_ci			if (ret)
5908c2ecf20Sopenharmony_ci				break;
5918c2ecf20Sopenharmony_ci			reg &= ~BIT(0);
5928c2ecf20Sopenharmony_ci			ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg);
5938c2ecf20Sopenharmony_ci		}
5948c2ecf20Sopenharmony_ci	} while (0);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (ret)
5978c2ecf20Sopenharmony_ci		goto out_release_bus;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (entries == 0) {
6008c2ecf20Sopenharmony_ci		/*
6018c2ecf20Sopenharmony_ci		 * No VMM space available in firmware so retry to transmit
6028c2ecf20Sopenharmony_ci		 * the packet from tx queue.
6038c2ecf20Sopenharmony_ci		 */
6048c2ecf20Sopenharmony_ci		ret = WILC_VMM_ENTRY_FULL_RETRY;
6058c2ecf20Sopenharmony_ci		goto out_release_bus;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	offset = 0;
6118c2ecf20Sopenharmony_ci	i = 0;
6128c2ecf20Sopenharmony_ci	do {
6138c2ecf20Sopenharmony_ci		u32 header, buffer_offset;
6148c2ecf20Sopenharmony_ci		char *bssid;
6158c2ecf20Sopenharmony_ci		u8 mgmt_ptk = 0;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		tqe = wilc_wlan_txq_remove_from_head(dev);
6188c2ecf20Sopenharmony_ci		if (!tqe)
6198c2ecf20Sopenharmony_ci			break;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci		vif = tqe->vif;
6228c2ecf20Sopenharmony_ci		if (vmm_table[i] == 0)
6238c2ecf20Sopenharmony_ci			break;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		le32_to_cpus(&vmm_table[i]);
6268c2ecf20Sopenharmony_ci		vmm_sz = FIELD_GET(WILC_VMM_BUFFER_SIZE, vmm_table[i]);
6278c2ecf20Sopenharmony_ci		vmm_sz *= 4;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		if (tqe->type == WILC_MGMT_PKT)
6308c2ecf20Sopenharmony_ci			mgmt_ptk = 1;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci		header = (FIELD_PREP(WILC_VMM_HDR_TYPE, tqe->type) |
6338c2ecf20Sopenharmony_ci			  FIELD_PREP(WILC_VMM_HDR_MGMT_FIELD, mgmt_ptk) |
6348c2ecf20Sopenharmony_ci			  FIELD_PREP(WILC_VMM_HDR_PKT_SIZE, tqe->buffer_size) |
6358c2ecf20Sopenharmony_ci			  FIELD_PREP(WILC_VMM_HDR_BUFF_SIZE, vmm_sz));
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		cpu_to_le32s(&header);
6388c2ecf20Sopenharmony_ci		memcpy(&txb[offset], &header, 4);
6398c2ecf20Sopenharmony_ci		if (tqe->type == WILC_CFG_PKT) {
6408c2ecf20Sopenharmony_ci			buffer_offset = ETH_CONFIG_PKT_HDR_OFFSET;
6418c2ecf20Sopenharmony_ci		} else if (tqe->type == WILC_NET_PKT) {
6428c2ecf20Sopenharmony_ci			bssid = tqe->vif->bssid;
6438c2ecf20Sopenharmony_ci			buffer_offset = ETH_ETHERNET_HDR_OFFSET;
6448c2ecf20Sopenharmony_ci			memcpy(&txb[offset + 8], bssid, 6);
6458c2ecf20Sopenharmony_ci		} else {
6468c2ecf20Sopenharmony_ci			buffer_offset = HOST_HDR_OFFSET;
6478c2ecf20Sopenharmony_ci		}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci		memcpy(&txb[offset + buffer_offset],
6508c2ecf20Sopenharmony_ci		       tqe->buffer, tqe->buffer_size);
6518c2ecf20Sopenharmony_ci		offset += vmm_sz;
6528c2ecf20Sopenharmony_ci		i++;
6538c2ecf20Sopenharmony_ci		tqe->status = 1;
6548c2ecf20Sopenharmony_ci		if (tqe->tx_complete_func)
6558c2ecf20Sopenharmony_ci			tqe->tx_complete_func(tqe->priv, tqe->status);
6568c2ecf20Sopenharmony_ci		if (tqe->ack_idx != NOT_TCP_ACK &&
6578c2ecf20Sopenharmony_ci		    tqe->ack_idx < MAX_PENDING_ACKS)
6588c2ecf20Sopenharmony_ci			vif->ack_filter.pending_acks[tqe->ack_idx].txqe = NULL;
6598c2ecf20Sopenharmony_ci		kfree(tqe);
6608c2ecf20Sopenharmony_ci	} while (--entries);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM);
6658c2ecf20Sopenharmony_ci	if (ret)
6668c2ecf20Sopenharmony_ci		goto out_release_bus;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	ret = func->hif_block_tx_ext(wilc, 0, txb, offset);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ciout_release_bus:
6718c2ecf20Sopenharmony_ci	release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ciout_unlock:
6748c2ecf20Sopenharmony_ci	mutex_unlock(&wilc->txq_add_to_head_cs);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ciout_update_cnt:
6778c2ecf20Sopenharmony_ci	*txq_count = wilc->txq_entries;
6788c2ecf20Sopenharmony_ci	return ret;
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cistatic void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	int offset = 0;
6848c2ecf20Sopenharmony_ci	u32 header;
6858c2ecf20Sopenharmony_ci	u32 pkt_len, pkt_offset, tp_len;
6868c2ecf20Sopenharmony_ci	int is_cfg_packet;
6878c2ecf20Sopenharmony_ci	u8 *buff_ptr;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	do {
6908c2ecf20Sopenharmony_ci		buff_ptr = buffer + offset;
6918c2ecf20Sopenharmony_ci		header = get_unaligned_le32(buff_ptr);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		is_cfg_packet = FIELD_GET(WILC_PKT_HDR_CONFIG_FIELD, header);
6948c2ecf20Sopenharmony_ci		pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header);
6958c2ecf20Sopenharmony_ci		tp_len = FIELD_GET(WILC_PKT_HDR_TOTAL_LEN_FIELD, header);
6968c2ecf20Sopenharmony_ci		pkt_len = FIELD_GET(WILC_PKT_HDR_LEN_FIELD, header);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		if (pkt_len == 0 || tp_len == 0)
6998c2ecf20Sopenharmony_ci			break;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci		if (pkt_offset & IS_MANAGMEMENT) {
7028c2ecf20Sopenharmony_ci			buff_ptr += HOST_HDR_OFFSET;
7038c2ecf20Sopenharmony_ci			wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len);
7048c2ecf20Sopenharmony_ci		} else {
7058c2ecf20Sopenharmony_ci			if (!is_cfg_packet) {
7068c2ecf20Sopenharmony_ci				wilc_frmw_to_host(wilc, buff_ptr, pkt_len,
7078c2ecf20Sopenharmony_ci						  pkt_offset);
7088c2ecf20Sopenharmony_ci			} else {
7098c2ecf20Sopenharmony_ci				struct wilc_cfg_rsp rsp;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci				buff_ptr += pkt_offset;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci				wilc_wlan_cfg_indicate_rx(wilc, buff_ptr,
7148c2ecf20Sopenharmony_ci							  pkt_len,
7158c2ecf20Sopenharmony_ci							  &rsp);
7168c2ecf20Sopenharmony_ci				if (rsp.type == WILC_CFG_RSP) {
7178c2ecf20Sopenharmony_ci					if (wilc->cfg_seq_no == rsp.seq_no)
7188c2ecf20Sopenharmony_ci						complete(&wilc->cfg_event);
7198c2ecf20Sopenharmony_ci				} else if (rsp.type == WILC_CFG_RSP_STATUS) {
7208c2ecf20Sopenharmony_ci					wilc_mac_indicate(wilc);
7218c2ecf20Sopenharmony_ci				}
7228c2ecf20Sopenharmony_ci			}
7238c2ecf20Sopenharmony_ci		}
7248c2ecf20Sopenharmony_ci		offset += tp_len;
7258c2ecf20Sopenharmony_ci	} while (offset < size);
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic void wilc_wlan_handle_rxq(struct wilc *wilc)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	int size;
7318c2ecf20Sopenharmony_ci	u8 *buffer;
7328c2ecf20Sopenharmony_ci	struct rxq_entry_t *rqe;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	while (!wilc->quit) {
7358c2ecf20Sopenharmony_ci		rqe = wilc_wlan_rxq_remove(wilc);
7368c2ecf20Sopenharmony_ci		if (!rqe)
7378c2ecf20Sopenharmony_ci			break;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		buffer = rqe->buffer;
7408c2ecf20Sopenharmony_ci		size = rqe->buffer_size;
7418c2ecf20Sopenharmony_ci		wilc_wlan_handle_rx_buff(wilc, buffer, size);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		kfree(rqe);
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci	if (wilc->quit)
7468c2ecf20Sopenharmony_ci		complete(&wilc->cfg_event);
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic void wilc_unknown_isr_ext(struct wilc *wilc)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	wilc->hif_func->hif_clear_int_ext(wilc, 0);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic void wilc_wlan_handle_isr_ext(struct wilc *wilc, u32 int_status)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	u32 offset = wilc->rx_buffer_offset;
7578c2ecf20Sopenharmony_ci	u8 *buffer = NULL;
7588c2ecf20Sopenharmony_ci	u32 size;
7598c2ecf20Sopenharmony_ci	u32 retries = 0;
7608c2ecf20Sopenharmony_ci	int ret = 0;
7618c2ecf20Sopenharmony_ci	struct rxq_entry_t *rqe;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, int_status) << 2;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	while (!size && retries < 10) {
7668c2ecf20Sopenharmony_ci		wilc->hif_func->hif_read_size(wilc, &size);
7678c2ecf20Sopenharmony_ci		size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, size) << 2;
7688c2ecf20Sopenharmony_ci		retries++;
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (size <= 0)
7728c2ecf20Sopenharmony_ci		return;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (WILC_RX_BUFF_SIZE - offset < size)
7758c2ecf20Sopenharmony_ci		offset = 0;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	buffer = &wilc->rx_buffer[offset];
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	wilc->hif_func->hif_clear_int_ext(wilc, DATA_INT_CLR | ENABLE_RX_VMM);
7808c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_block_rx_ext(wilc, 0, buffer, size);
7818c2ecf20Sopenharmony_ci	if (ret)
7828c2ecf20Sopenharmony_ci		return;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	offset += size;
7858c2ecf20Sopenharmony_ci	wilc->rx_buffer_offset = offset;
7868c2ecf20Sopenharmony_ci	rqe = kmalloc(sizeof(*rqe), GFP_KERNEL);
7878c2ecf20Sopenharmony_ci	if (!rqe)
7888c2ecf20Sopenharmony_ci		return;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	rqe->buffer = buffer;
7918c2ecf20Sopenharmony_ci	rqe->buffer_size = size;
7928c2ecf20Sopenharmony_ci	wilc_wlan_rxq_add(wilc, rqe);
7938c2ecf20Sopenharmony_ci	wilc_wlan_handle_rxq(wilc);
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_civoid wilc_handle_isr(struct wilc *wilc)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	u32 int_status;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
8018c2ecf20Sopenharmony_ci	wilc->hif_func->hif_read_int(wilc, &int_status);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	if (int_status & DATA_INT_EXT)
8048c2ecf20Sopenharmony_ci		wilc_wlan_handle_isr_ext(wilc, int_status);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (!(int_status & (ALL_INT_EXT)))
8078c2ecf20Sopenharmony_ci		wilc_unknown_isr_ext(wilc);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wilc_handle_isr);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ciint wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer,
8148c2ecf20Sopenharmony_ci				u32 buffer_size)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	u32 offset;
8178c2ecf20Sopenharmony_ci	u32 addr, size, size2, blksz;
8188c2ecf20Sopenharmony_ci	u8 *dma_buffer;
8198c2ecf20Sopenharmony_ci	int ret = 0;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	blksz = BIT(12);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	dma_buffer = kmalloc(blksz, GFP_KERNEL);
8248c2ecf20Sopenharmony_ci	if (!dma_buffer)
8258c2ecf20Sopenharmony_ci		return -EIO;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	offset = 0;
8288c2ecf20Sopenharmony_ci	do {
8298c2ecf20Sopenharmony_ci		addr = get_unaligned_le32(&buffer[offset]);
8308c2ecf20Sopenharmony_ci		size = get_unaligned_le32(&buffer[offset + 4]);
8318c2ecf20Sopenharmony_ci		acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
8328c2ecf20Sopenharmony_ci		offset += 8;
8338c2ecf20Sopenharmony_ci		while (((int)size) && (offset < buffer_size)) {
8348c2ecf20Sopenharmony_ci			if (size <= blksz)
8358c2ecf20Sopenharmony_ci				size2 = size;
8368c2ecf20Sopenharmony_ci			else
8378c2ecf20Sopenharmony_ci				size2 = blksz;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci			memcpy(dma_buffer, &buffer[offset], size2);
8408c2ecf20Sopenharmony_ci			ret = wilc->hif_func->hif_block_tx(wilc, addr,
8418c2ecf20Sopenharmony_ci							   dma_buffer, size2);
8428c2ecf20Sopenharmony_ci			if (ret)
8438c2ecf20Sopenharmony_ci				break;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci			addr += size2;
8468c2ecf20Sopenharmony_ci			offset += size2;
8478c2ecf20Sopenharmony_ci			size -= size2;
8488c2ecf20Sopenharmony_ci		}
8498c2ecf20Sopenharmony_ci		release_bus(wilc, WILC_BUS_RELEASE_ONLY);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci		if (ret)
8528c2ecf20Sopenharmony_ci			goto fail;
8538c2ecf20Sopenharmony_ci	} while (offset < buffer_size);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cifail:
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	kfree(dma_buffer);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	return ret;
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ciint wilc_wlan_start(struct wilc *wilc)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	u32 reg = 0;
8658c2ecf20Sopenharmony_ci	int ret;
8668c2ecf20Sopenharmony_ci	u32 chipid;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (wilc->io_type == WILC_HIF_SDIO) {
8698c2ecf20Sopenharmony_ci		reg = 0;
8708c2ecf20Sopenharmony_ci		reg |= BIT(3);
8718c2ecf20Sopenharmony_ci	} else if (wilc->io_type == WILC_HIF_SPI) {
8728c2ecf20Sopenharmony_ci		reg = 1;
8738c2ecf20Sopenharmony_ci	}
8748c2ecf20Sopenharmony_ci	acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
8758c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_write_reg(wilc, WILC_VMM_CORE_CFG, reg);
8768c2ecf20Sopenharmony_ci	if (ret) {
8778c2ecf20Sopenharmony_ci		release_bus(wilc, WILC_BUS_RELEASE_ONLY);
8788c2ecf20Sopenharmony_ci		return ret;
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci	reg = 0;
8818c2ecf20Sopenharmony_ci	if (wilc->io_type == WILC_HIF_SDIO && wilc->dev_irq_num)
8828c2ecf20Sopenharmony_ci		reg |= WILC_HAVE_SDIO_IRQ_GPIO;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_1, reg);
8858c2ecf20Sopenharmony_ci	if (ret) {
8868c2ecf20Sopenharmony_ci		release_bus(wilc, WILC_BUS_RELEASE_ONLY);
8878c2ecf20Sopenharmony_ci		return ret;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	wilc->hif_func->hif_sync_ext(wilc, NUM_INT_EXT);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &chipid);
8938c2ecf20Sopenharmony_ci	if (ret) {
8948c2ecf20Sopenharmony_ci		release_bus(wilc, WILC_BUS_RELEASE_ONLY);
8958c2ecf20Sopenharmony_ci		return ret;
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
8998c2ecf20Sopenharmony_ci	if ((reg & BIT(10)) == BIT(10)) {
9008c2ecf20Sopenharmony_ci		reg &= ~BIT(10);
9018c2ecf20Sopenharmony_ci		wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
9028c2ecf20Sopenharmony_ci		wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	reg |= BIT(10);
9068c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
9078c2ecf20Sopenharmony_ci	wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
9088c2ecf20Sopenharmony_ci	release_bus(wilc, WILC_BUS_RELEASE_ONLY);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	return ret;
9118c2ecf20Sopenharmony_ci}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ciint wilc_wlan_stop(struct wilc *wilc, struct wilc_vif *vif)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	u32 reg = 0;
9168c2ecf20Sopenharmony_ci	int ret;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_read_reg(wilc, WILC_GP_REG_0, &reg);
9218c2ecf20Sopenharmony_ci	if (ret) {
9228c2ecf20Sopenharmony_ci		netdev_err(vif->ndev, "Error while reading reg\n");
9238c2ecf20Sopenharmony_ci		release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
9248c2ecf20Sopenharmony_ci		return ret;
9258c2ecf20Sopenharmony_ci	}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_0,
9288c2ecf20Sopenharmony_ci					(reg | WILC_ABORT_REQ_BIT));
9298c2ecf20Sopenharmony_ci	if (ret) {
9308c2ecf20Sopenharmony_ci		netdev_err(vif->ndev, "Error while writing reg\n");
9318c2ecf20Sopenharmony_ci		release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
9328c2ecf20Sopenharmony_ci		return ret;
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_read_reg(wilc, WILC_FW_HOST_COMM, &reg);
9368c2ecf20Sopenharmony_ci	if (ret) {
9378c2ecf20Sopenharmony_ci		netdev_err(vif->ndev, "Error while reading reg\n");
9388c2ecf20Sopenharmony_ci		release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
9398c2ecf20Sopenharmony_ci		return ret;
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci	reg = BIT(0);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	ret = wilc->hif_func->hif_write_reg(wilc, WILC_FW_HOST_COMM, reg);
9448c2ecf20Sopenharmony_ci	if (ret) {
9458c2ecf20Sopenharmony_ci		netdev_err(vif->ndev, "Error while writing reg\n");
9468c2ecf20Sopenharmony_ci		release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
9478c2ecf20Sopenharmony_ci		return ret;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	return 0;
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_civoid wilc_wlan_cleanup(struct net_device *dev)
9568c2ecf20Sopenharmony_ci{
9578c2ecf20Sopenharmony_ci	struct txq_entry_t *tqe;
9588c2ecf20Sopenharmony_ci	struct rxq_entry_t *rqe;
9598c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
9608c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	wilc->quit = 1;
9638c2ecf20Sopenharmony_ci	while ((tqe = wilc_wlan_txq_remove_from_head(dev))) {
9648c2ecf20Sopenharmony_ci		if (tqe->tx_complete_func)
9658c2ecf20Sopenharmony_ci			tqe->tx_complete_func(tqe->priv, 0);
9668c2ecf20Sopenharmony_ci		kfree(tqe);
9678c2ecf20Sopenharmony_ci	}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	while ((rqe = wilc_wlan_rxq_remove(wilc)))
9708c2ecf20Sopenharmony_ci		kfree(rqe);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	kfree(wilc->rx_buffer);
9738c2ecf20Sopenharmony_ci	wilc->rx_buffer = NULL;
9748c2ecf20Sopenharmony_ci	kfree(wilc->tx_buffer);
9758c2ecf20Sopenharmony_ci	wilc->tx_buffer = NULL;
9768c2ecf20Sopenharmony_ci	wilc->hif_func->hif_deinit(NULL);
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic int wilc_wlan_cfg_commit(struct wilc_vif *vif, int type,
9808c2ecf20Sopenharmony_ci				u32 drv_handler)
9818c2ecf20Sopenharmony_ci{
9828c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
9838c2ecf20Sopenharmony_ci	struct wilc_cfg_frame *cfg = &wilc->cfg_frame;
9848c2ecf20Sopenharmony_ci	int t_len = wilc->cfg_frame_offset + sizeof(struct wilc_cfg_cmd_hdr);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	if (type == WILC_CFG_SET)
9878c2ecf20Sopenharmony_ci		cfg->hdr.cmd_type = 'W';
9888c2ecf20Sopenharmony_ci	else
9898c2ecf20Sopenharmony_ci		cfg->hdr.cmd_type = 'Q';
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	cfg->hdr.seq_no = wilc->cfg_seq_no % 256;
9928c2ecf20Sopenharmony_ci	cfg->hdr.total_len = cpu_to_le16(t_len);
9938c2ecf20Sopenharmony_ci	cfg->hdr.driver_handler = cpu_to_le32(drv_handler);
9948c2ecf20Sopenharmony_ci	wilc->cfg_seq_no = cfg->hdr.seq_no;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (!wilc_wlan_txq_add_cfg_pkt(vif, (u8 *)&cfg->hdr, t_len))
9978c2ecf20Sopenharmony_ci		return -1;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return 0;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ciint wilc_wlan_cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer,
10038c2ecf20Sopenharmony_ci		      u32 buffer_size, int commit, u32 drv_handler)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	u32 offset;
10068c2ecf20Sopenharmony_ci	int ret_size;
10078c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	mutex_lock(&wilc->cfg_cmd_lock);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	if (start)
10128c2ecf20Sopenharmony_ci		wilc->cfg_frame_offset = 0;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	offset = wilc->cfg_frame_offset;
10158c2ecf20Sopenharmony_ci	ret_size = wilc_wlan_cfg_set_wid(wilc->cfg_frame.frame, offset,
10168c2ecf20Sopenharmony_ci					 wid, buffer, buffer_size);
10178c2ecf20Sopenharmony_ci	offset += ret_size;
10188c2ecf20Sopenharmony_ci	wilc->cfg_frame_offset = offset;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	if (!commit) {
10218c2ecf20Sopenharmony_ci		mutex_unlock(&wilc->cfg_cmd_lock);
10228c2ecf20Sopenharmony_ci		return ret_size;
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	netdev_dbg(vif->ndev, "%s: seqno[%d]\n", __func__, wilc->cfg_seq_no);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (wilc_wlan_cfg_commit(vif, WILC_CFG_SET, drv_handler))
10288c2ecf20Sopenharmony_ci		ret_size = 0;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&wilc->cfg_event,
10318c2ecf20Sopenharmony_ci					 WILC_CFG_PKTS_TIMEOUT)) {
10328c2ecf20Sopenharmony_ci		netdev_dbg(vif->ndev, "%s: Timed Out\n", __func__);
10338c2ecf20Sopenharmony_ci		ret_size = 0;
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	wilc->cfg_frame_offset = 0;
10378c2ecf20Sopenharmony_ci	wilc->cfg_seq_no += 1;
10388c2ecf20Sopenharmony_ci	mutex_unlock(&wilc->cfg_cmd_lock);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	return ret_size;
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ciint wilc_wlan_cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit,
10448c2ecf20Sopenharmony_ci		      u32 drv_handler)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	u32 offset;
10478c2ecf20Sopenharmony_ci	int ret_size;
10488c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	mutex_lock(&wilc->cfg_cmd_lock);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	if (start)
10538c2ecf20Sopenharmony_ci		wilc->cfg_frame_offset = 0;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	offset = wilc->cfg_frame_offset;
10568c2ecf20Sopenharmony_ci	ret_size = wilc_wlan_cfg_get_wid(wilc->cfg_frame.frame, offset, wid);
10578c2ecf20Sopenharmony_ci	offset += ret_size;
10588c2ecf20Sopenharmony_ci	wilc->cfg_frame_offset = offset;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	if (!commit) {
10618c2ecf20Sopenharmony_ci		mutex_unlock(&wilc->cfg_cmd_lock);
10628c2ecf20Sopenharmony_ci		return ret_size;
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if (wilc_wlan_cfg_commit(vif, WILC_CFG_QUERY, drv_handler))
10668c2ecf20Sopenharmony_ci		ret_size = 0;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&wilc->cfg_event,
10698c2ecf20Sopenharmony_ci					 WILC_CFG_PKTS_TIMEOUT)) {
10708c2ecf20Sopenharmony_ci		netdev_dbg(vif->ndev, "%s: Timed Out\n", __func__);
10718c2ecf20Sopenharmony_ci		ret_size = 0;
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci	wilc->cfg_frame_offset = 0;
10748c2ecf20Sopenharmony_ci	wilc->cfg_seq_no += 1;
10758c2ecf20Sopenharmony_ci	mutex_unlock(&wilc->cfg_cmd_lock);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	return ret_size;
10788c2ecf20Sopenharmony_ci}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ciint wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids,
10818c2ecf20Sopenharmony_ci			 u32 count)
10828c2ecf20Sopenharmony_ci{
10838c2ecf20Sopenharmony_ci	int i;
10848c2ecf20Sopenharmony_ci	int ret = 0;
10858c2ecf20Sopenharmony_ci	u32 drv = wilc_get_vif_idx(vif);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	if (mode == WILC_GET_CFG) {
10888c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++) {
10898c2ecf20Sopenharmony_ci			if (!wilc_wlan_cfg_get(vif, !i,
10908c2ecf20Sopenharmony_ci					       wids[i].id,
10918c2ecf20Sopenharmony_ci					       (i == count - 1),
10928c2ecf20Sopenharmony_ci					       drv)) {
10938c2ecf20Sopenharmony_ci				ret = -ETIMEDOUT;
10948c2ecf20Sopenharmony_ci				break;
10958c2ecf20Sopenharmony_ci			}
10968c2ecf20Sopenharmony_ci		}
10978c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++) {
10988c2ecf20Sopenharmony_ci			wids[i].size = wilc_wlan_cfg_get_val(vif->wilc,
10998c2ecf20Sopenharmony_ci							     wids[i].id,
11008c2ecf20Sopenharmony_ci							     wids[i].val,
11018c2ecf20Sopenharmony_ci							     wids[i].size);
11028c2ecf20Sopenharmony_ci		}
11038c2ecf20Sopenharmony_ci	} else if (mode == WILC_SET_CFG) {
11048c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++) {
11058c2ecf20Sopenharmony_ci			if (!wilc_wlan_cfg_set(vif, !i,
11068c2ecf20Sopenharmony_ci					       wids[i].id,
11078c2ecf20Sopenharmony_ci					       wids[i].val,
11088c2ecf20Sopenharmony_ci					       wids[i].size,
11098c2ecf20Sopenharmony_ci					       (i == count - 1),
11108c2ecf20Sopenharmony_ci					       drv)) {
11118c2ecf20Sopenharmony_ci				ret = -ETIMEDOUT;
11128c2ecf20Sopenharmony_ci				break;
11138c2ecf20Sopenharmony_ci			}
11148c2ecf20Sopenharmony_ci		}
11158c2ecf20Sopenharmony_ci	}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	return ret;
11188c2ecf20Sopenharmony_ci}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic int init_chip(struct net_device *dev)
11218c2ecf20Sopenharmony_ci{
11228c2ecf20Sopenharmony_ci	u32 chipid;
11238c2ecf20Sopenharmony_ci	u32 reg;
11248c2ecf20Sopenharmony_ci	int ret = 0;
11258c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
11268c2ecf20Sopenharmony_ci	struct wilc *wilc = vif->wilc;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	chipid = wilc_get_chipid(wilc, true);
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	if ((chipid & 0xfff) != 0xa0) {
11338c2ecf20Sopenharmony_ci		ret = wilc->hif_func->hif_read_reg(wilc,
11348c2ecf20Sopenharmony_ci						   WILC_CORTUS_RESET_MUX_SEL,
11358c2ecf20Sopenharmony_ci						   &reg);
11368c2ecf20Sopenharmony_ci		if (ret) {
11378c2ecf20Sopenharmony_ci			netdev_err(dev, "fail read reg 0x1118\n");
11388c2ecf20Sopenharmony_ci			goto release;
11398c2ecf20Sopenharmony_ci		}
11408c2ecf20Sopenharmony_ci		reg |= BIT(0);
11418c2ecf20Sopenharmony_ci		ret = wilc->hif_func->hif_write_reg(wilc,
11428c2ecf20Sopenharmony_ci						    WILC_CORTUS_RESET_MUX_SEL,
11438c2ecf20Sopenharmony_ci						    reg);
11448c2ecf20Sopenharmony_ci		if (ret) {
11458c2ecf20Sopenharmony_ci			netdev_err(dev, "fail write reg 0x1118\n");
11468c2ecf20Sopenharmony_ci			goto release;
11478c2ecf20Sopenharmony_ci		}
11488c2ecf20Sopenharmony_ci		ret = wilc->hif_func->hif_write_reg(wilc,
11498c2ecf20Sopenharmony_ci						    WILC_CORTUS_BOOT_REGISTER,
11508c2ecf20Sopenharmony_ci						    WILC_CORTUS_BOOT_FROM_IRAM);
11518c2ecf20Sopenharmony_ci		if (ret) {
11528c2ecf20Sopenharmony_ci			netdev_err(dev, "fail write reg 0xc0000\n");
11538c2ecf20Sopenharmony_ci			goto release;
11548c2ecf20Sopenharmony_ci		}
11558c2ecf20Sopenharmony_ci	}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cirelease:
11588c2ecf20Sopenharmony_ci	release_bus(wilc, WILC_BUS_RELEASE_ONLY);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	return ret;
11618c2ecf20Sopenharmony_ci}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ciu32 wilc_get_chipid(struct wilc *wilc, bool update)
11648c2ecf20Sopenharmony_ci{
11658c2ecf20Sopenharmony_ci	static u32 chipid;
11668c2ecf20Sopenharmony_ci	u32 tempchipid = 0;
11678c2ecf20Sopenharmony_ci	u32 rfrevid = 0;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	if (chipid == 0 || update) {
11708c2ecf20Sopenharmony_ci		wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &tempchipid);
11718c2ecf20Sopenharmony_ci		wilc->hif_func->hif_read_reg(wilc, WILC_RF_REVISION_ID,
11728c2ecf20Sopenharmony_ci					     &rfrevid);
11738c2ecf20Sopenharmony_ci		if (!is_wilc1000(tempchipid)) {
11748c2ecf20Sopenharmony_ci			chipid = 0;
11758c2ecf20Sopenharmony_ci			return chipid;
11768c2ecf20Sopenharmony_ci		}
11778c2ecf20Sopenharmony_ci		if (tempchipid == WILC_1000_BASE_ID_2A) { /* 0x1002A0 */
11788c2ecf20Sopenharmony_ci			if (rfrevid != 0x1)
11798c2ecf20Sopenharmony_ci				tempchipid = WILC_1000_BASE_ID_2A_REV1;
11808c2ecf20Sopenharmony_ci		} else if (tempchipid == WILC_1000_BASE_ID_2B) { /* 0x1002B0 */
11818c2ecf20Sopenharmony_ci			if (rfrevid == 0x4)
11828c2ecf20Sopenharmony_ci				tempchipid = WILC_1000_BASE_ID_2B_REV1;
11838c2ecf20Sopenharmony_ci			else if (rfrevid != 0x3)
11848c2ecf20Sopenharmony_ci				tempchipid = WILC_1000_BASE_ID_2B_REV2;
11858c2ecf20Sopenharmony_ci		}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		chipid = tempchipid;
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci	return chipid;
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ciint wilc_wlan_init(struct net_device *dev)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	int ret = 0;
11958c2ecf20Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
11968c2ecf20Sopenharmony_ci	struct wilc *wilc;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	wilc = vif->wilc;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	wilc->quit = 0;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	if (wilc->hif_func->hif_init(wilc, false)) {
12038c2ecf20Sopenharmony_ci		ret = -EIO;
12048c2ecf20Sopenharmony_ci		goto fail;
12058c2ecf20Sopenharmony_ci	}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	if (!wilc->tx_buffer)
12088c2ecf20Sopenharmony_ci		wilc->tx_buffer = kmalloc(WILC_TX_BUFF_SIZE, GFP_KERNEL);
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	if (!wilc->tx_buffer) {
12118c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
12128c2ecf20Sopenharmony_ci		goto fail;
12138c2ecf20Sopenharmony_ci	}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	if (!wilc->rx_buffer)
12168c2ecf20Sopenharmony_ci		wilc->rx_buffer = kmalloc(WILC_RX_BUFF_SIZE, GFP_KERNEL);
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	if (!wilc->rx_buffer) {
12198c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
12208c2ecf20Sopenharmony_ci		goto fail;
12218c2ecf20Sopenharmony_ci	}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	if (init_chip(dev)) {
12248c2ecf20Sopenharmony_ci		ret = -EIO;
12258c2ecf20Sopenharmony_ci		goto fail;
12268c2ecf20Sopenharmony_ci	}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	return 0;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_cifail:
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	kfree(wilc->rx_buffer);
12338c2ecf20Sopenharmony_ci	wilc->rx_buffer = NULL;
12348c2ecf20Sopenharmony_ci	kfree(wilc->tx_buffer);
12358c2ecf20Sopenharmony_ci	wilc->tx_buffer = NULL;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	return ret;
12388c2ecf20Sopenharmony_ci}
1239