blob: 87ae722f5726dad247896f50cc8cf1a5b1953666 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002 * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/**
29 * DOC: wlan_hdd_ipa.c
30 *
31 * WLAN HDD and ipa interface implementation
32 * Originally written by Qualcomm Atheros, Inc
33 */
34
35#ifdef IPA_OFFLOAD
36
37/* Include Files */
38#include <wlan_hdd_includes.h>
39#include <wlan_hdd_ipa.h>
40
41#include <linux/etherdevice.h>
42#include <linux/atomic.h>
43#include <linux/netdevice.h>
44#include <linux/skbuff.h>
45#include <linux/list.h>
46#include <linux/debugfs.h>
47#include <linux/inetdevice.h>
48#include <linux/ip.h>
49#include <wlan_hdd_softap_tx_rx.h>
50#include <ol_txrx_osif_api.h>
Manjunathappa Prakash3454fd62016-04-01 08:52:06 -070051#include <cdp_txrx_peer_ops.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080052
53#include "cds_sched.h"
54
55#include "wma.h"
56#include "wma_api.h"
57
Dhanashri Atreb08959a2016-03-01 17:28:03 -080058#include "cdp_txrx_ipa.h"
59
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080060#define HDD_IPA_DESC_BUFFER_RATIO 4
61#define HDD_IPA_IPV4_NAME_EXT "_ipv4"
62#define HDD_IPA_IPV6_NAME_EXT "_ipv6"
63
64#define HDD_IPA_RX_INACTIVITY_MSEC_DELAY 1000
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080065#define HDD_IPA_UC_WLAN_8023_HDR_SIZE 14
66/* WDI TX and RX PIPE */
67#define HDD_IPA_UC_NUM_WDI_PIPE 2
68#define HDD_IPA_UC_MAX_PENDING_EVENT 33
69
70#define HDD_IPA_UC_DEBUG_DUMMY_MEM_SIZE 32000
71#define HDD_IPA_UC_RT_DEBUG_PERIOD 300
72#define HDD_IPA_UC_RT_DEBUG_BUF_COUNT 30
73#define HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL 10000
74
75#define HDD_IPA_WLAN_HDR_DES_MAC_OFFSET 0
76#define HDD_IPA_MAX_IFACE 3
77#define HDD_IPA_MAX_SYSBAM_PIPE 4
78#define HDD_IPA_RX_PIPE HDD_IPA_MAX_IFACE
79#define HDD_IPA_ENABLE_MASK BIT(0)
80#define HDD_IPA_PRE_FILTER_ENABLE_MASK BIT(1)
81#define HDD_IPA_IPV6_ENABLE_MASK BIT(2)
82#define HDD_IPA_RM_ENABLE_MASK BIT(3)
83#define HDD_IPA_CLK_SCALING_ENABLE_MASK BIT(4)
84#define HDD_IPA_UC_ENABLE_MASK BIT(5)
85#define HDD_IPA_UC_STA_ENABLE_MASK BIT(6)
86#define HDD_IPA_REAL_TIME_DEBUGGING BIT(8)
87
Yun Parkf19e07d2015-11-20 11:34:27 -080088#define HDD_IPA_MAX_PENDING_EVENT_COUNT 20
89
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080090typedef enum {
91 HDD_IPA_UC_OPCODE_TX_SUSPEND = 0,
92 HDD_IPA_UC_OPCODE_TX_RESUME = 1,
93 HDD_IPA_UC_OPCODE_RX_SUSPEND = 2,
94 HDD_IPA_UC_OPCODE_RX_RESUME = 3,
95 HDD_IPA_UC_OPCODE_STATS = 4,
96 /* keep this last */
97 HDD_IPA_UC_OPCODE_MAX
98} hdd_ipa_uc_op_code;
99
100/**
101 * enum - Reason codes for stat query
102 *
103 * @HDD_IPA_UC_STAT_REASON_NONE: Initial value
104 * @HDD_IPA_UC_STAT_REASON_DEBUG: For debug/info
105 * @HDD_IPA_UC_STAT_REASON_BW_CAL: For bandwidth calibration
106 */
107enum {
108 HDD_IPA_UC_STAT_REASON_NONE,
109 HDD_IPA_UC_STAT_REASON_DEBUG,
110 HDD_IPA_UC_STAT_REASON_BW_CAL
111};
112
113/**
114 * enum hdd_ipa_rm_state - IPA resource manager state
115 * @HDD_IPA_RM_RELEASED: PROD pipe resource released
116 * @HDD_IPA_RM_GRANT_PENDING: PROD pipe resource requested but not granted yet
117 * @HDD_IPA_RM_GRANTED: PROD pipe resource granted
118 */
119enum hdd_ipa_rm_state {
120 HDD_IPA_RM_RELEASED,
121 HDD_IPA_RM_GRANT_PENDING,
122 HDD_IPA_RM_GRANTED,
123};
124
125struct llc_snap_hdr {
126 uint8_t dsap;
127 uint8_t ssap;
128 uint8_t resv[4];
129 __be16 eth_type;
130} __packed;
131
Leo Chang3bc8fed2015-11-13 10:59:47 -0800132/**
133 * struct hdd_ipa_tx_hdr - header type which IPA should handle to TX packet
134 * @eth: ether II header
135 * @llc_snap: LLC snap header
136 *
137 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800138struct hdd_ipa_tx_hdr {
139 struct ethhdr eth;
140 struct llc_snap_hdr llc_snap;
141} __packed;
142
Leo Chang3bc8fed2015-11-13 10:59:47 -0800143/**
144 * struct frag_header - fragment header type registered to IPA hardware
145 * @length: fragment length
146 * @reserved1: Reserved not used
147 * @reserved2: Reserved not used
148 *
149 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800150struct frag_header {
Leo Chang3bc8fed2015-11-13 10:59:47 -0800151 uint16_t length;
152 uint32_t reserved1;
153 uint32_t reserved2;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800154} __packed;
155
Leo Chang3bc8fed2015-11-13 10:59:47 -0800156/**
157 * struct ipa_header - ipa header type registered to IPA hardware
158 * @vdev_id: vdev id
159 * @reserved: Reserved not used
160 *
161 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800162struct ipa_header {
163 uint32_t
164 vdev_id:8, /* vdev_id field is LSB of IPA DESC */
165 reserved:24;
166} __packed;
167
Leo Chang3bc8fed2015-11-13 10:59:47 -0800168/**
169 * struct hdd_ipa_uc_tx_hdr - full tx header registered to IPA hardware
170 * @frag_hd: fragment header
171 * @ipa_hd: ipa header
172 * @eth: ether II header
173 *
174 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800175struct hdd_ipa_uc_tx_hdr {
176 struct frag_header frag_hd;
177 struct ipa_header ipa_hd;
178 struct ethhdr eth;
179} __packed;
180
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800181/**
182 * struct hdd_ipa_cld_hdr - IPA CLD Header
183 * @reserved: reserved fields
184 * @iface_id: interface ID
185 * @sta_id: Station ID
186 *
187 * Packed 32-bit structure
188 * +----------+----------+--------------+--------+
189 * | Reserved | QCMAP ID | interface id | STA ID |
190 * +----------+----------+--------------+--------+
191 */
192struct hdd_ipa_cld_hdr {
193 uint8_t reserved[2];
194 uint8_t iface_id;
195 uint8_t sta_id;
196} __packed;
197
198struct hdd_ipa_rx_hdr {
199 struct hdd_ipa_cld_hdr cld_hdr;
200 struct ethhdr eth;
201} __packed;
202
203struct hdd_ipa_pm_tx_cb {
204 struct hdd_ipa_iface_context *iface_context;
205 struct ipa_rx_data *ipa_tx_desc;
206};
207
208struct hdd_ipa_uc_rx_hdr {
209 struct ethhdr eth;
210} __packed;
211
212struct hdd_ipa_sys_pipe {
213 uint32_t conn_hdl;
214 uint8_t conn_hdl_valid;
215 struct ipa_sys_connect_params ipa_sys_params;
216};
217
218struct hdd_ipa_iface_stats {
219 uint64_t num_tx;
220 uint64_t num_tx_drop;
221 uint64_t num_tx_err;
222 uint64_t num_tx_cac_drop;
223 uint64_t num_rx_prefilter;
224 uint64_t num_rx_ipa_excep;
225 uint64_t num_rx_recv;
226 uint64_t num_rx_recv_mul;
227 uint64_t num_rx_send_desc_err;
228 uint64_t max_rx_mul;
229};
230
231struct hdd_ipa_priv;
232
233struct hdd_ipa_iface_context {
234 struct hdd_ipa_priv *hdd_ipa;
235 hdd_adapter_t *adapter;
236 void *tl_context;
237
238 enum ipa_client_type cons_client;
239 enum ipa_client_type prod_client;
240
241 uint8_t iface_id; /* This iface ID */
242 uint8_t sta_id; /* This iface station ID */
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530243 qdf_spinlock_t interface_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800244 uint32_t ifa_address;
245 struct hdd_ipa_iface_stats stats;
246};
247
248struct hdd_ipa_stats {
249 uint32_t event[IPA_WLAN_EVENT_MAX];
250 uint64_t num_send_msg;
251 uint64_t num_free_msg;
252
253 uint64_t num_rm_grant;
254 uint64_t num_rm_release;
255 uint64_t num_rm_grant_imm;
256 uint64_t num_cons_perf_req;
257 uint64_t num_prod_perf_req;
258
259 uint64_t num_rx_drop;
260 uint64_t num_rx_ipa_tx_dp;
261 uint64_t num_rx_ipa_splice;
262 uint64_t num_rx_ipa_loop;
263 uint64_t num_rx_ipa_tx_dp_err;
264 uint64_t num_rx_ipa_write_done;
265 uint64_t num_max_ipa_tx_mul;
266 uint64_t num_rx_ipa_hw_maxed_out;
267 uint64_t max_pend_q_cnt;
268
269 uint64_t num_tx_comp_cnt;
270 uint64_t num_tx_queued;
271 uint64_t num_tx_dequeued;
272 uint64_t num_max_pm_queue;
273
274 uint64_t num_freeq_empty;
275 uint64_t num_pri_freeq_empty;
276 uint64_t num_rx_excep;
277 uint64_t num_tx_bcmc;
278 uint64_t num_tx_bcmc_err;
279};
280
281struct ipa_uc_stas_map {
282 bool is_reserved;
283 uint8_t sta_id;
284};
285struct op_msg_type {
286 uint8_t msg_t;
287 uint8_t rsvd;
288 uint16_t op_code;
289 uint16_t len;
290 uint16_t rsvd_snd;
291};
292
293struct ipa_uc_fw_stats {
294 uint32_t tx_comp_ring_base;
295 uint32_t tx_comp_ring_size;
296 uint32_t tx_comp_ring_dbell_addr;
297 uint32_t tx_comp_ring_dbell_ind_val;
298 uint32_t tx_comp_ring_dbell_cached_val;
299 uint32_t tx_pkts_enqueued;
300 uint32_t tx_pkts_completed;
301 uint32_t tx_is_suspend;
302 uint32_t tx_reserved;
303 uint32_t rx_ind_ring_base;
304 uint32_t rx_ind_ring_size;
305 uint32_t rx_ind_ring_dbell_addr;
306 uint32_t rx_ind_ring_dbell_ind_val;
307 uint32_t rx_ind_ring_dbell_ind_cached_val;
308 uint32_t rx_ind_ring_rdidx_addr;
309 uint32_t rx_ind_ring_rd_idx_cached_val;
310 uint32_t rx_refill_idx;
311 uint32_t rx_num_pkts_indicated;
312 uint32_t rx_buf_refilled;
313 uint32_t rx_num_ind_drop_no_space;
314 uint32_t rx_num_ind_drop_no_buf;
315 uint32_t rx_is_suspend;
316 uint32_t rx_reserved;
317};
318
319struct ipa_uc_pending_event {
Anurag Chouhanffb21542016-02-17 14:33:03 +0530320 qdf_list_node_t node;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800321 hdd_adapter_t *adapter;
322 enum ipa_wlan_event type;
323 uint8_t sta_id;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530324 uint8_t mac_addr[QDF_MAC_ADDR_SIZE];
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800325};
326
327/**
328 * struct uc_rm_work_struct
329 * @work: uC RM work
330 * @event: IPA RM event
331 */
332struct uc_rm_work_struct {
333 struct work_struct work;
334 enum ipa_rm_event event;
335};
336
337/**
338 * struct uc_op_work_struct
339 * @work: uC OP work
340 * @msg: OP message
341 */
342struct uc_op_work_struct {
343 struct work_struct work;
344 struct op_msg_type *msg;
345};
346static uint8_t vdev_to_iface[CSR_ROAM_SESSION_MAX];
347
348/**
349 * struct uc_rt_debug_info
350 * @time: system time
351 * @ipa_excep_count: IPA exception packet count
352 * @rx_drop_count: IPA Rx drop packet count
353 * @net_sent_count: IPA Rx packet sent to network stack count
354 * @rx_discard_count: IPA Rx discard packet count
355 * @rx_mcbc_count: IPA Rx BCMC packet count
356 * @tx_mcbc_count: IPA Tx BCMC packet countt
357 * @tx_fwd_count: IPA Tx forward packet count
358 * @rx_destructor_call: IPA Rx packet destructor count
359 */
360struct uc_rt_debug_info {
Anurag Chouhan6d760662016-02-20 16:05:43 +0530361 unsigned long time;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800362 uint64_t ipa_excep_count;
363 uint64_t rx_drop_count;
364 uint64_t net_sent_count;
365 uint64_t rx_discard_count;
366 uint64_t rx_mcbc_count;
367 uint64_t tx_mcbc_count;
368 uint64_t tx_fwd_count;
369 uint64_t rx_destructor_call;
370};
371
372struct hdd_ipa_priv {
373 struct hdd_ipa_sys_pipe sys_pipe[HDD_IPA_MAX_SYSBAM_PIPE];
374 struct hdd_ipa_iface_context iface_context[HDD_IPA_MAX_IFACE];
375 uint8_t num_iface;
376 enum hdd_ipa_rm_state rm_state;
377 /*
Nirav Shahcbc6d722016-03-01 16:24:53 +0530378 * IPA driver can send RM notifications with IRQ disabled so using qdf
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800379 * APIs as it is taken care gracefully. Without this, kernel would throw
380 * an warning if spin_lock_bh is used while IRQ is disabled
381 */
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530382 qdf_spinlock_t rm_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800383 struct uc_rm_work_struct uc_rm_work;
384 struct uc_op_work_struct uc_op_work[HDD_IPA_UC_OPCODE_MAX];
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530385 qdf_wake_lock_t wake_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800386 struct delayed_work wake_lock_work;
387 bool wake_lock_released;
388
389 enum ipa_client_type prod_client;
390
391 atomic_t tx_ref_cnt;
Nirav Shahcbc6d722016-03-01 16:24:53 +0530392 qdf_nbuf_queue_t pm_queue_head;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800393 struct work_struct pm_work;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530394 qdf_spinlock_t pm_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800395 bool suspended;
396
397 uint32_t pending_hw_desc_cnt;
398 uint32_t hw_desc_cnt;
399 spinlock_t q_lock;
400 uint32_t freeq_cnt;
401 struct list_head free_desc_head;
402
403 uint32_t pend_q_cnt;
404 struct list_head pend_desc_head;
405
406 hdd_context_t *hdd_ctx;
407
408 struct dentry *debugfs_dir;
409 struct hdd_ipa_stats stats;
410
411 struct notifier_block ipv4_notifier;
412 uint32_t curr_prod_bw;
413 uint32_t curr_cons_bw;
414
415 uint8_t activated_fw_pipe;
416 uint8_t sap_num_connected_sta;
417 uint8_t sta_connected;
418 uint32_t tx_pipe_handle;
419 uint32_t rx_pipe_handle;
420 bool resource_loading;
421 bool resource_unloading;
422 bool pending_cons_req;
423 struct ipa_uc_stas_map assoc_stas_map[WLAN_MAX_STA_COUNT];
Anurag Chouhanffb21542016-02-17 14:33:03 +0530424 qdf_list_t pending_event;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530425 qdf_mutex_t event_lock;
Leo Change3e49442015-10-26 20:07:13 -0700426 bool ipa_pipes_down;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800427 uint32_t ipa_tx_packets_diff;
428 uint32_t ipa_rx_packets_diff;
429 uint32_t ipa_p_tx_packets;
430 uint32_t ipa_p_rx_packets;
431 uint32_t stat_req_reason;
432 uint64_t ipa_tx_forward;
433 uint64_t ipa_rx_discard;
434 uint64_t ipa_rx_net_send_count;
435 uint64_t ipa_rx_internel_drop_count;
436 uint64_t ipa_rx_destructor_count;
Anurag Chouhan210db072016-02-22 18:42:15 +0530437 qdf_mc_timer_t rt_debug_timer;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800438 struct uc_rt_debug_info rt_bug_buffer[HDD_IPA_UC_RT_DEBUG_BUF_COUNT];
439 unsigned int rt_buf_fill_index;
Anurag Chouhan210db072016-02-22 18:42:15 +0530440 qdf_mc_timer_t rt_debug_fill_timer;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530441 qdf_mutex_t rt_debug_lock;
442 qdf_mutex_t ipa_lock;
Dhanashri Atreb08959a2016-03-01 17:28:03 -0800443 struct ol_txrx_ipa_resources ipa_resource;
Leo Chang3bc8fed2015-11-13 10:59:47 -0800444 /* IPA UC doorbell registers paddr */
Anurag Chouhan6d760662016-02-20 16:05:43 +0530445 qdf_dma_addr_t tx_comp_doorbell_paddr;
446 qdf_dma_addr_t rx_ready_doorbell_paddr;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800447};
448
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800449/**
Houston Hoffman23e76f92016-02-26 12:19:11 -0800450 * FIXME: The following conversion routines are just stubs.
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800451 * They will be implemented fully by another update.
452 * The stubs will let the compile go ahead, and functionality
453 * is broken.
454 * This should be OK and IPA is not enabled yet
455 */
456void *wlan_hdd_stub_priv_to_addr(uint32_t priv)
457{
458 void *vaddr;
459 uint32_t ipa_priv = priv;
460
461 vaddr = &ipa_priv; /* just to use the var */
462 vaddr = NULL;
463 return vaddr;
464}
465
466uint32_t wlan_hdd_stub_addr_to_priv(void *ptr)
467{
468 uint32_t ipa_priv = 0;
469
470 BUG_ON(ptr == NULL);
471 return ipa_priv;
472}
Leo Changcc923e22016-06-16 15:29:03 -0700473
474#define HDD_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header)
475#define HDD_IPA_WLAN_IPA_HEADER sizeof(struct ipa_header)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800476#define HDD_IPA_WLAN_CLD_HDR_LEN sizeof(struct hdd_ipa_cld_hdr)
477#define HDD_IPA_UC_WLAN_CLD_HDR_LEN 0
478#define HDD_IPA_WLAN_TX_HDR_LEN sizeof(struct hdd_ipa_tx_hdr)
479#define HDD_IPA_UC_WLAN_TX_HDR_LEN sizeof(struct hdd_ipa_uc_tx_hdr)
480#define HDD_IPA_WLAN_RX_HDR_LEN sizeof(struct hdd_ipa_rx_hdr)
481#define HDD_IPA_UC_WLAN_RX_HDR_LEN sizeof(struct hdd_ipa_uc_rx_hdr)
Leo Changcc923e22016-06-16 15:29:03 -0700482#define HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET \
483 (HDD_IPA_WLAN_FRAG_HEADER + HDD_IPA_WLAN_IPA_HEADER)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800484
Leo Chang3bc8fed2015-11-13 10:59:47 -0800485#define HDD_IPA_FW_RX_DESC_DISCARD_M 0x1
486#define HDD_IPA_FW_RX_DESC_FORWARD_M 0x2
487
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800488#define HDD_IPA_GET_IFACE_ID(_data) \
489 (((struct hdd_ipa_cld_hdr *) (_data))->iface_id)
490
491#define HDD_IPA_LOG(LVL, fmt, args ...) \
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530492 QDF_TRACE(QDF_MODULE_ID_HDD, LVL, \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800493 "%s:%d: "fmt, __func__, __LINE__, ## args)
494
495#define HDD_IPA_DBG_DUMP(_lvl, _prefix, _buf, _len) \
496 do { \
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530497 QDF_TRACE(QDF_MODULE_ID_HDD, _lvl, "%s:", _prefix); \
498 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, _lvl, _buf, _len); \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800499 } while (0)
500
501#define HDD_IPA_IS_CONFIG_ENABLED(_hdd_ctx, _mask) \
502 (((_hdd_ctx)->config->IpaConfig & (_mask)) == (_mask))
503
504#define HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa) \
505 do { \
506 hdd_ipa->ipa_rx_internel_drop_count++; \
507 } while (0)
508#define HDD_IPA_INCREASE_NET_SEND_COUNT(hdd_ipa) \
509 do { \
510 hdd_ipa->ipa_rx_net_send_count++; \
511 } while (0)
512#define HDD_BW_GET_DIFF(_x, _y) (unsigned long)((ULONG_MAX - (_y)) + (_x) + 1)
513
Leo Chang07b28f62016-05-11 12:29:22 -0700514#if defined (QCA_WIFI_3_0) && defined (CONFIG_IPA3)
Dhanashri Atreb08959a2016-03-01 17:28:03 -0800515#define HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt) \
516do { \
517 pipe_in.u.ul.rdy_ring_rp_va = \
518 ipa_ctxt->ipa_resource.rx_proc_done_idx_vaddr; \
519 pipe_in.u.ul.rdy_comp_ring_base_pa = \
520 ipa_ctxt->ipa_resource.rx2_rdy_ring_base_paddr;\
521 pipe_in.u.ul.rdy_comp_ring_size = \
522 ipa_ctxt->ipa_resource.rx2_rdy_ring_size; \
523 pipe_in.u.ul.rdy_comp_ring_wp_pa = \
524 ipa_ctxt->ipa_resource.rx2_proc_done_idx_paddr; \
525 pipe_in.u.ul.rdy_comp_ring_wp_va = \
526 ipa_ctxt->ipa_resource.rx2_proc_done_idx_vaddr; \
Leo Chang3bc8fed2015-11-13 10:59:47 -0800527} while (0)
528#else
529/* Do nothing */
530#define HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt)
Leo Chang07b28f62016-05-11 12:29:22 -0700531#endif /* IPA3 */
Leo Chang3bc8fed2015-11-13 10:59:47 -0800532
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800533static struct hdd_ipa_adapter_2_client {
534 enum ipa_client_type cons_client;
535 enum ipa_client_type prod_client;
536} hdd_ipa_adapter_2_client[HDD_IPA_MAX_IFACE] = {
537 {
538 IPA_CLIENT_WLAN2_CONS, IPA_CLIENT_WLAN1_PROD
539 }, {
540 IPA_CLIENT_WLAN3_CONS, IPA_CLIENT_WLAN1_PROD
541 }, {
542 IPA_CLIENT_WLAN4_CONS, IPA_CLIENT_WLAN1_PROD
543 },
544};
545
546/* For Tx pipes, use Ethernet-II Header format */
547struct hdd_ipa_uc_tx_hdr ipa_uc_tx_hdr = {
548 {
Leo Chang3bc8fed2015-11-13 10:59:47 -0800549 0x0000,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800550 0x00000000,
551 0x00000000
552 },
553 {
554 0x00000000
555 },
556 {
557 {0x00, 0x03, 0x7f, 0xaa, 0xbb, 0xcc},
558 {0x00, 0x03, 0x7f, 0xdd, 0xee, 0xff},
559 0x0008
560 }
561};
562
563/* For Tx pipes, use 802.3 Header format */
564static struct hdd_ipa_tx_hdr ipa_tx_hdr = {
565 {
566 {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF},
567 {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF},
568 0x00 /* length can be zero */
569 },
570 {
571 /* LLC SNAP header 8 bytes */
572 0xaa, 0xaa,
573 {0x03, 0x00, 0x00, 0x00},
574 0x0008 /* type value(2 bytes) ,filled by wlan */
575 /* 0x0800 - IPV4, 0x86dd - IPV6 */
576 }
577};
578
579static const char *op_string[] = {
580 "TX_SUSPEND",
581 "TX_RESUME",
582 "RX_SUSPEND",
583 "RX_RESUME",
584 "STATS",
585};
586
587static struct hdd_ipa_priv *ghdd_ipa;
588
589/* Local Function Prototypes */
590static void hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt,
591 unsigned long data);
592static void hdd_ipa_w2i_cb(void *priv, enum ipa_dp_evt_type evt,
593 unsigned long data);
594
595static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context);
596
597/**
598 * hdd_ipa_is_enabled() - Is IPA enabled?
599 * @hdd_ctx: Global HDD context
600 *
601 * Return: true if IPA is enabled, false otherwise
602 */
603bool hdd_ipa_is_enabled(hdd_context_t *hdd_ctx)
604{
605 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_ENABLE_MASK);
606}
607
608/**
609 * hdd_ipa_uc_is_enabled() - Is IPA uC offload enabled?
610 * @hdd_ctx: Global HDD context
611 *
612 * Return: true if IPA uC offload is enabled, false otherwise
613 */
614bool hdd_ipa_uc_is_enabled(hdd_context_t *hdd_ctx)
615{
616 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_UC_ENABLE_MASK);
617}
618
619/**
620 * hdd_ipa_uc_sta_is_enabled() - Is STA mode IPA uC offload enabled?
621 * @hdd_ctx: Global HDD context
622 *
623 * Return: true if STA mode IPA uC offload is enabled, false otherwise
624 */
625static inline bool hdd_ipa_uc_sta_is_enabled(hdd_context_t *hdd_ctx)
626{
627 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_UC_STA_ENABLE_MASK);
628}
629
630/**
631 * hdd_ipa_is_pre_filter_enabled() - Is IPA pre-filter enabled?
632 * @hdd_ipa: Global HDD IPA context
633 *
634 * Return: true if pre-filter is enabled, otherwise false
635 */
636static inline bool hdd_ipa_is_pre_filter_enabled(hdd_context_t *hdd_ctx)
637{
638 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx,
639 HDD_IPA_PRE_FILTER_ENABLE_MASK);
640}
641
642/**
643 * hdd_ipa_is_ipv6_enabled() - Is IPA IPv6 enabled?
644 * @hdd_ipa: Global HDD IPA context
645 *
646 * Return: true if IPv6 is enabled, otherwise false
647 */
648static inline bool hdd_ipa_is_ipv6_enabled(hdd_context_t *hdd_ctx)
649{
650 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_IPV6_ENABLE_MASK);
651}
652
653/**
654 * hdd_ipa_is_rm_enabled() - Is IPA resource manager enabled?
655 * @hdd_ipa: Global HDD IPA context
656 *
657 * Return: true if resource manager is enabled, otherwise false
658 */
659static inline bool hdd_ipa_is_rm_enabled(hdd_context_t *hdd_ctx)
660{
661 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_RM_ENABLE_MASK);
662}
663
664/**
665 * hdd_ipa_is_rt_debugging_enabled() - Is IPA real-time debug enabled?
666 * @hdd_ipa: Global HDD IPA context
667 *
668 * Return: true if resource manager is enabled, otherwise false
669 */
670static inline bool hdd_ipa_is_rt_debugging_enabled(hdd_context_t *hdd_ctx)
671{
672 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_REAL_TIME_DEBUGGING);
673}
674
675/**
676 * hdd_ipa_is_clk_scaling_enabled() - Is IPA clock scaling enabled?
677 * @hdd_ipa: Global HDD IPA context
678 *
679 * Return: true if clock scaling is enabled, otherwise false
680 */
681static inline bool hdd_ipa_is_clk_scaling_enabled(hdd_context_t *hdd_ctx)
682{
683 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx,
684 HDD_IPA_CLK_SCALING_ENABLE_MASK |
685 HDD_IPA_RM_ENABLE_MASK);
686}
687
688/**
689 * hdd_ipa_uc_rt_debug_host_fill - fill rt debug buffer
690 * @ctext: pointer to hdd context.
691 *
692 * If rt debug enabled, periodically called, and fill debug buffer
693 *
694 * Return: none
695 */
696static void hdd_ipa_uc_rt_debug_host_fill(void *ctext)
697{
698 hdd_context_t *hdd_ctx = (hdd_context_t *)ctext;
699 struct hdd_ipa_priv *hdd_ipa;
700 struct uc_rt_debug_info *dump_info = NULL;
701
702 if (wlan_hdd_validate_context(hdd_ctx))
703 return;
704
705 if (!hdd_ctx->hdd_ipa || !hdd_ipa_uc_is_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530706 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800707 "%s: IPA UC is not enabled", __func__);
708 return;
709 }
710
711 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
712
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530713 qdf_mutex_acquire(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800714 dump_info = &hdd_ipa->rt_bug_buffer[
715 hdd_ipa->rt_buf_fill_index % HDD_IPA_UC_RT_DEBUG_BUF_COUNT];
716
Anurag Chouhan210db072016-02-22 18:42:15 +0530717 dump_info->time = qdf_mc_timer_get_system_time();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800718 dump_info->ipa_excep_count = hdd_ipa->stats.num_rx_excep;
719 dump_info->rx_drop_count = hdd_ipa->ipa_rx_internel_drop_count;
720 dump_info->net_sent_count = hdd_ipa->ipa_rx_net_send_count;
721 dump_info->rx_discard_count = hdd_ipa->ipa_rx_discard;
722 dump_info->tx_mcbc_count = hdd_ipa->stats.num_tx_bcmc;
723 dump_info->tx_fwd_count = hdd_ipa->ipa_tx_forward;
724 dump_info->rx_destructor_call = hdd_ipa->ipa_rx_destructor_count;
725 hdd_ipa->rt_buf_fill_index++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530726 qdf_mutex_release(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800727
Anurag Chouhan210db072016-02-22 18:42:15 +0530728 qdf_mc_timer_start(&hdd_ipa->rt_debug_fill_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800729 HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL);
730}
731
732/**
733 * hdd_ipa_uc_rt_debug_host_dump - dump rt debug buffer
734 * @hdd_ctx: pointer to hdd context.
735 *
736 * If rt debug enabled, dump debug buffer contents based on requirement
737 *
738 * Return: none
739 */
740void hdd_ipa_uc_rt_debug_host_dump(hdd_context_t *hdd_ctx)
741{
742 struct hdd_ipa_priv *hdd_ipa;
743 unsigned int dump_count;
744 unsigned int dump_index;
745 struct uc_rt_debug_info *dump_info = NULL;
746
747 if (wlan_hdd_validate_context(hdd_ctx))
748 return;
749
750 hdd_ipa = hdd_ctx->hdd_ipa;
751 if (!hdd_ipa || !hdd_ipa_uc_is_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530752 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800753 "%s: IPA UC is not enabled", __func__);
754 return;
755 }
756
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530757 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800758 "========= WLAN-IPA DEBUG BUF DUMP ==========\n");
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530759 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800760 " TM : EXEP : DROP : NETS : MCBC : TXFD : DSTR : DSCD\n");
761
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530762 qdf_mutex_acquire(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800763 for (dump_count = 0;
764 dump_count < HDD_IPA_UC_RT_DEBUG_BUF_COUNT;
765 dump_count++) {
766 dump_index = (hdd_ipa->rt_buf_fill_index + dump_count) %
767 HDD_IPA_UC_RT_DEBUG_BUF_COUNT;
768 dump_info = &hdd_ipa->rt_bug_buffer[dump_index];
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530769 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800770 "%12lu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu\n",
771 dump_info->time, dump_info->ipa_excep_count,
772 dump_info->rx_drop_count, dump_info->net_sent_count,
773 dump_info->tx_mcbc_count, dump_info->tx_fwd_count,
774 dump_info->rx_destructor_call,
775 dump_info->rx_discard_count);
776 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530777 qdf_mutex_release(&hdd_ipa->rt_debug_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530778 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800779 "======= WLAN-IPA DEBUG BUF DUMP END ========\n");
780}
781
782/**
783 * hdd_ipa_uc_rt_debug_handler - periodic memory health monitor handler
784 * @ctext: pointer to hdd context.
785 *
786 * periodically called by timer expire
787 * will try to alloc dummy memory and detect out of memory condition
788 * if out of memory detected, dump wlan-ipa stats
789 *
790 * Return: none
791 */
792static void hdd_ipa_uc_rt_debug_handler(void *ctext)
793{
794 hdd_context_t *hdd_ctx = (hdd_context_t *)ctext;
795 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
796 void *dummy_ptr = NULL;
797
798 if (wlan_hdd_validate_context(hdd_ctx))
799 return;
800
801 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530802 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800803 "%s: IPA RT debug is not enabled", __func__);
804 return;
805 }
806
807 /* Allocate dummy buffer periodically and free immediately. this will
808 * proactively detect OOM and if allocation fails dump ipa stats
809 */
810 dummy_ptr = kmalloc(HDD_IPA_UC_DEBUG_DUMMY_MEM_SIZE,
811 GFP_KERNEL | GFP_ATOMIC);
812 if (!dummy_ptr) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530813 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800814 "%s: Dummy alloc fail", __func__);
815 hdd_ipa_uc_rt_debug_host_dump(hdd_ctx);
816 hdd_ipa_uc_stat_request(
Krunal Sonibe766b02016-03-10 13:00:44 -0800817 hdd_get_adapter(hdd_ctx, QDF_SAP_MODE), 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800818 } else {
819 kfree(dummy_ptr);
820 }
821
Anurag Chouhan210db072016-02-22 18:42:15 +0530822 qdf_mc_timer_start(&hdd_ipa->rt_debug_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800823 HDD_IPA_UC_RT_DEBUG_PERIOD);
824}
825
826/**
827 * hdd_ipa_uc_rt_debug_destructor - called by data packet free
828 * @skb: packet pinter
829 *
830 * when free data packet, will be invoked by wlan client and will increase
831 * free counter
832 *
833 * Return: none
834 */
835void hdd_ipa_uc_rt_debug_destructor(struct sk_buff *skb)
836{
837 if (!ghdd_ipa) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530838 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800839 "%s: invalid hdd context", __func__);
840 return;
841 }
842
843 ghdd_ipa->ipa_rx_destructor_count++;
844}
845
846/**
847 * hdd_ipa_uc_rt_debug_deinit - remove resources to handle rt debugging
848 * @hdd_ctx: hdd main context
849 *
850 * free all rt debugging resources
851 *
852 * Return: none
853 */
854static void hdd_ipa_uc_rt_debug_deinit(hdd_context_t *hdd_ctx)
855{
856 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
857
Anurag Chouhan210db072016-02-22 18:42:15 +0530858 if (QDF_TIMER_STATE_STOPPED !=
859 qdf_mc_timer_get_current_state(&hdd_ipa->rt_debug_fill_timer)) {
860 qdf_mc_timer_stop(&hdd_ipa->rt_debug_fill_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800861 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530862 qdf_mc_timer_destroy(&hdd_ipa->rt_debug_fill_timer);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530863 qdf_mutex_destroy(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800864
865 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530866 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800867 "%s: IPA RT debug is not enabled", __func__);
868 return;
869 }
870
Anurag Chouhan210db072016-02-22 18:42:15 +0530871 if (QDF_TIMER_STATE_STOPPED !=
872 qdf_mc_timer_get_current_state(&hdd_ipa->rt_debug_timer)) {
873 qdf_mc_timer_stop(&hdd_ipa->rt_debug_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800874 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530875 qdf_mc_timer_destroy(&hdd_ipa->rt_debug_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800876}
877
878/**
879 * hdd_ipa_uc_rt_debug_init - intialize resources to handle rt debugging
880 * @hdd_ctx: hdd main context
881 *
882 * alloc and initialize all rt debugging resources
883 *
884 * Return: none
885 */
886static void hdd_ipa_uc_rt_debug_init(hdd_context_t *hdd_ctx)
887{
888 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
889
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530890 qdf_mutex_create(&hdd_ipa->rt_debug_lock);
Anurag Chouhan210db072016-02-22 18:42:15 +0530891 qdf_mc_timer_init(&hdd_ipa->rt_debug_fill_timer, QDF_TIMER_TYPE_SW,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800892 hdd_ipa_uc_rt_debug_host_fill, (void *)hdd_ctx);
893 hdd_ipa->rt_buf_fill_index = 0;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530894 qdf_mem_zero(hdd_ipa->rt_bug_buffer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800895 sizeof(struct uc_rt_debug_info) *
896 HDD_IPA_UC_RT_DEBUG_BUF_COUNT);
897 hdd_ipa->ipa_tx_forward = 0;
898 hdd_ipa->ipa_rx_discard = 0;
899 hdd_ipa->ipa_rx_net_send_count = 0;
900 hdd_ipa->ipa_rx_internel_drop_count = 0;
901 hdd_ipa->ipa_rx_destructor_count = 0;
902
Anurag Chouhan210db072016-02-22 18:42:15 +0530903 qdf_mc_timer_start(&hdd_ipa->rt_debug_fill_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800904 HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL);
905
906 /* Reatime debug enable on feature enable */
907 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530908 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800909 "%s: IPA RT debug is not enabled", __func__);
910 return;
911 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530912 qdf_mc_timer_init(&hdd_ipa->rt_debug_timer, QDF_TIMER_TYPE_SW,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800913 hdd_ipa_uc_rt_debug_handler, (void *)hdd_ctx);
Anurag Chouhan210db072016-02-22 18:42:15 +0530914 qdf_mc_timer_start(&hdd_ipa->rt_debug_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800915 HDD_IPA_UC_RT_DEBUG_PERIOD);
916
917}
918
919/**
920 * hdd_ipa_uc_stat_query() - Query the IPA stats
921 * @hdd_ctx: Global HDD context
922 * @ipa_tx_diff: tx packet count diff from previous
923 * tx packet count
924 * @ipa_rx_diff: rx packet count diff from previous
925 * rx packet count
926 *
927 * Return: true if IPA is enabled, false otherwise
928 */
929void hdd_ipa_uc_stat_query(hdd_context_t *pHddCtx,
930 uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff)
931{
932 struct hdd_ipa_priv *hdd_ipa;
933
934 hdd_ipa = (struct hdd_ipa_priv *)pHddCtx->hdd_ipa;
935 *ipa_tx_diff = 0;
936 *ipa_rx_diff = 0;
937
938 if (!hdd_ipa_is_enabled(pHddCtx) ||
939 !(hdd_ipa_uc_is_enabled(pHddCtx))) {
940 return;
941 }
942
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530943 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800944 if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) &&
945 (false == hdd_ipa->resource_loading)) {
946 *ipa_tx_diff = hdd_ipa->ipa_tx_packets_diff;
947 *ipa_rx_diff = hdd_ipa->ipa_rx_packets_diff;
948 HDD_IPA_LOG(LOG1, "STAT Query TX DIFF %d, RX DIFF %d",
949 *ipa_tx_diff, *ipa_rx_diff);
950 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530951 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800952 return;
953}
954
955/**
956 * hdd_ipa_uc_stat_request() - Get IPA stats from IPA.
957 * @adapter: network adapter
958 * @reason: STAT REQ Reason
959 *
960 * Return: None
961 */
962void hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason)
963{
964 hdd_context_t *pHddCtx;
965 struct hdd_ipa_priv *hdd_ipa;
966
967 if (!adapter) {
968 return;
969 }
970
971 pHddCtx = (hdd_context_t *)adapter->pHddCtx;
972 hdd_ipa = (struct hdd_ipa_priv *)pHddCtx->hdd_ipa;
973 if (!hdd_ipa_is_enabled(pHddCtx) ||
974 !(hdd_ipa_uc_is_enabled(pHddCtx))) {
975 return;
976 }
977
978 HDD_IPA_LOG(LOG1, "STAT REQ Reason %d", reason);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530979 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800980 if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) &&
981 (false == hdd_ipa->resource_loading)) {
982 hdd_ipa->stat_req_reason = reason;
983 wma_cli_set_command(
984 (int)adapter->sessionId,
985 (int)WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID,
986 0, VDEV_CMD);
987 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530988 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800989}
990
991/**
992 * hdd_ipa_uc_find_add_assoc_sta() - Find associated station
993 * @hdd_ipa: Global HDD IPA context
994 * @sta_add: Should station be added
995 * @sta_id: ID of the station being queried
996 *
997 * Return: true if the station was found
998 */
999static bool hdd_ipa_uc_find_add_assoc_sta(struct hdd_ipa_priv *hdd_ipa,
1000 bool sta_add, uint8_t sta_id)
1001{
1002 bool sta_found = false;
1003 uint8_t idx;
1004 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1005 if ((hdd_ipa->assoc_stas_map[idx].is_reserved) &&
1006 (hdd_ipa->assoc_stas_map[idx].sta_id == sta_id)) {
1007 sta_found = true;
1008 break;
1009 }
1010 }
1011 if (sta_add && sta_found) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301012 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001013 "%s: STA ID %d already exist, cannot add",
1014 __func__, sta_id);
1015 return sta_found;
1016 }
1017 if (sta_add) {
1018 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1019 if (!hdd_ipa->assoc_stas_map[idx].is_reserved) {
1020 hdd_ipa->assoc_stas_map[idx].is_reserved = true;
1021 hdd_ipa->assoc_stas_map[idx].sta_id = sta_id;
1022 return sta_found;
1023 }
1024 }
1025 }
1026 if (!sta_add && !sta_found) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301027 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001028 "%s: STA ID %d does not exist, cannot delete",
1029 __func__, sta_id);
1030 return sta_found;
1031 }
1032 if (!sta_add) {
1033 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1034 if ((hdd_ipa->assoc_stas_map[idx].is_reserved) &&
1035 (hdd_ipa->assoc_stas_map[idx].sta_id == sta_id)) {
1036 hdd_ipa->assoc_stas_map[idx].is_reserved =
1037 false;
1038 hdd_ipa->assoc_stas_map[idx].sta_id = 0xFF;
1039 return sta_found;
1040 }
1041 }
1042 }
1043 return sta_found;
1044}
1045
1046/**
1047 * hdd_ipa_uc_enable_pipes() - Enable IPA uC pipes
1048 * @hdd_ipa: Global HDD IPA context
1049 *
1050 * Return: 0 on success, negative errno if error
1051 */
1052static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa)
1053{
1054 int result;
1055 p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context;
1056
1057 /* ACTIVATE TX PIPE */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301058 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001059 "%s: Enable TX PIPE(tx_pipe_handle=%d)",
1060 __func__, hdd_ipa->tx_pipe_handle);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001061 result = ipa_enable_wdi_pipe(hdd_ipa->tx_pipe_handle);
1062 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301063 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001064 "%s: Enable TX PIPE fail, code %d",
1065 __func__, result);
1066 return result;
1067 }
1068 result = ipa_resume_wdi_pipe(hdd_ipa->tx_pipe_handle);
1069 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301070 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001071 "%s: Resume TX PIPE fail, code %d",
1072 __func__, result);
1073 return result;
1074 }
1075 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, true);
1076
1077 /* ACTIVATE RX PIPE */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301078 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001079 "%s: Enable RX PIPE(rx_pipe_handle=%d)",
1080 __func__, hdd_ipa->rx_pipe_handle);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001081 result = ipa_enable_wdi_pipe(hdd_ipa->rx_pipe_handle);
1082 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301083 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001084 "%s: Enable RX PIPE fail, code %d",
1085 __func__, result);
1086 return result;
1087 }
1088 result = ipa_resume_wdi_pipe(hdd_ipa->rx_pipe_handle);
1089 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301090 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001091 "%s: Resume RX PIPE fail, code %d",
1092 __func__, result);
1093 return result;
1094 }
1095 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, false);
Leo Change3e49442015-10-26 20:07:13 -07001096 hdd_ipa->ipa_pipes_down = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001097 return 0;
1098}
1099
1100/**
1101 * hdd_ipa_uc_disable_pipes() - Disable IPA uC pipes
1102 * @hdd_ipa: Global HDD IPA context
1103 *
1104 * Return: 0 on success, negative errno if error
1105 */
1106static int hdd_ipa_uc_disable_pipes(struct hdd_ipa_priv *hdd_ipa)
1107{
1108 int result;
1109
Leo Change3e49442015-10-26 20:07:13 -07001110 hdd_ipa->ipa_pipes_down = true;
1111
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301112 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable RX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001113 result = ipa_suspend_wdi_pipe(hdd_ipa->rx_pipe_handle);
1114 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301115 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001116 "%s: Suspend RX PIPE fail, code %d",
1117 __func__, result);
1118 return result;
1119 }
1120 result = ipa_disable_wdi_pipe(hdd_ipa->rx_pipe_handle);
1121 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301122 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001123 "%s: Disable RX PIPE fail, code %d",
1124 __func__, result);
1125 return result;
1126 }
1127
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301128 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable TX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001129 result = ipa_suspend_wdi_pipe(hdd_ipa->tx_pipe_handle);
1130 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301131 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001132 "%s: Suspend TX PIPE fail, code %d",
1133 __func__, result);
1134 return result;
1135 }
1136 result = ipa_disable_wdi_pipe(hdd_ipa->tx_pipe_handle);
1137 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301138 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001139 "%s: Disable TX PIPE fail, code %d",
1140 __func__, result);
1141 return result;
1142 }
1143
1144 return 0;
1145}
1146
1147/**
1148 * hdd_ipa_uc_handle_first_con() - Handle first uC IPA connection
1149 * @hdd_ipa: Global HDD IPA context
1150 *
1151 * Return: 0 on success, negative errno if error
1152 */
1153static int hdd_ipa_uc_handle_first_con(struct hdd_ipa_priv *hdd_ipa)
1154{
1155 hdd_ipa->activated_fw_pipe = 0;
1156 hdd_ipa->resource_loading = true;
Yun Park4cab6ee2015-10-27 11:43:40 -07001157
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001158 /* If RM feature enabled
1159 * Request PROD Resource first
1160 * PROD resource may return sync or async manners */
Yun Park4cab6ee2015-10-27 11:43:40 -07001161 if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) {
1162 if (!ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD)) {
1163 /* RM PROD request sync return
1164 * enable pipe immediately
1165 */
1166 if (hdd_ipa_uc_enable_pipes(hdd_ipa)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301167 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park4cab6ee2015-10-27 11:43:40 -07001168 "%s: IPA WDI Pipe activation failed",
1169 __func__);
1170 hdd_ipa->resource_loading = false;
1171 return -EBUSY;
1172 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001173 }
1174 } else {
1175 /* RM Disabled
Yun Park4cab6ee2015-10-27 11:43:40 -07001176 * Just enabled all the PIPEs
1177 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001178 if (hdd_ipa_uc_enable_pipes(hdd_ipa)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301179 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park4cab6ee2015-10-27 11:43:40 -07001180 "%s: IPA WDI Pipe activation failed",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001181 __func__);
1182 hdd_ipa->resource_loading = false;
1183 return -EBUSY;
1184 }
1185 hdd_ipa->resource_loading = false;
1186 }
Yun Park4cab6ee2015-10-27 11:43:40 -07001187
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301188 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001189 "%s: IPA WDI Pipes activated successfully", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001190 return 0;
1191}
1192
1193/**
1194 * hdd_ipa_uc_handle_last_discon() - Handle last uC IPA disconnection
1195 * @hdd_ipa: Global HDD IPA context
1196 *
1197 * Return: None
1198 */
1199static void hdd_ipa_uc_handle_last_discon(struct hdd_ipa_priv *hdd_ipa)
1200{
1201 p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context;
1202
1203 hdd_ipa->resource_unloading = true;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301204 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable FW RX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001205 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, false);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301206 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable FW TX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001207 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, true);
1208}
1209
1210/**
1211 * hdd_ipa_uc_rm_notify_handler() - IPA uC resource notification handler
1212 * @context: User context registered with TL (the IPA Global context is
1213 * registered
1214 * @rxpkt: Packet containing the notification
1215 * @staid: ID of the station associated with the packet
1216 *
1217 * Return: None
1218 */
1219static void
1220hdd_ipa_uc_rm_notify_handler(void *context, enum ipa_rm_event event)
1221{
1222 struct hdd_ipa_priv *hdd_ipa = context;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301223 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001224
1225 /*
1226 * When SSR is going on or driver is unloading, just return.
1227 */
1228 status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301229 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001230 return;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001231
1232 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1233 return;
1234
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301235 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s, event code %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001236 __func__, event);
1237
1238 switch (event) {
1239 case IPA_RM_RESOURCE_GRANTED:
1240 /* Differed RM Granted */
1241 hdd_ipa_uc_enable_pipes(hdd_ipa);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301242 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001243 if ((false == hdd_ipa->resource_unloading) &&
1244 (!hdd_ipa->activated_fw_pipe)) {
1245 hdd_ipa_uc_enable_pipes(hdd_ipa);
1246 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301247 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001248 break;
1249
1250 case IPA_RM_RESOURCE_RELEASED:
1251 /* Differed RM Released */
1252 hdd_ipa->resource_unloading = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001253 break;
1254
1255 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301256 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001257 "%s, invalid event code %d", __func__, event);
1258 break;
1259 }
1260}
1261
1262/**
1263 * hdd_ipa_uc_rm_notify_defer() - Defer IPA uC notification
1264 * @hdd_ipa: Global HDD IPA context
1265 * @event: IPA resource manager event to be deferred
1266 *
1267 * This function is called when a resource manager event is received
1268 * from firmware in interrupt context. This function will defer the
1269 * handling to the OL RX thread
1270 *
1271 * Return: None
1272 */
1273static void hdd_ipa_uc_rm_notify_defer(struct work_struct *work)
1274{
1275 enum ipa_rm_event event;
1276 struct uc_rm_work_struct *uc_rm_work = container_of(work,
1277 struct uc_rm_work_struct, work);
1278 struct hdd_ipa_priv *hdd_ipa = container_of(uc_rm_work,
1279 struct hdd_ipa_priv, uc_rm_work);
1280
1281 cds_ssr_protect(__func__);
1282 event = uc_rm_work->event;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301283 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001284 "%s, posted event %d", __func__, event);
1285
1286 hdd_ipa_uc_rm_notify_handler(hdd_ipa, event);
1287 cds_ssr_unprotect(__func__);
1288
1289 return;
1290}
1291
1292/**
1293 * hdd_ipa_uc_proc_pending_event() - Process IPA uC pending events
1294 * @hdd_ipa: Global HDD IPA context
1295 *
1296 * Return: None
1297 */
1298static void hdd_ipa_uc_proc_pending_event(struct hdd_ipa_priv *hdd_ipa)
1299{
1300 unsigned int pending_event_count;
1301 struct ipa_uc_pending_event *pending_event = NULL;
1302
Anurag Chouhanffb21542016-02-17 14:33:03 +05301303 pending_event_count = qdf_list_size(&hdd_ipa->pending_event);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301304 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001305 "%s, Pending Event Count %d", __func__, pending_event_count);
1306 if (!pending_event_count) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301307 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001308 "%s, No Pending Event", __func__);
1309 return;
1310 }
1311
Anurag Chouhanffb21542016-02-17 14:33:03 +05301312 qdf_list_remove_front(&hdd_ipa->pending_event,
1313 (qdf_list_node_t **)&pending_event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001314 while (pending_event != NULL) {
1315 hdd_ipa_wlan_evt(pending_event->adapter,
1316 pending_event->type,
1317 pending_event->sta_id,
1318 pending_event->mac_addr);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301319 qdf_mem_free(pending_event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001320 pending_event = NULL;
Anurag Chouhanffb21542016-02-17 14:33:03 +05301321 qdf_list_remove_front(&hdd_ipa->pending_event,
1322 (qdf_list_node_t **)&pending_event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001323 }
1324}
1325
1326/**
1327 * hdd_ipa_uc_op_cb() - IPA uC operation callback
1328 * @op_msg: operation message received from firmware
1329 * @usr_ctxt: user context registered with TL (we register the HDD Global
1330 * context)
1331 *
1332 * Return: None
1333 */
1334static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt)
1335{
1336 struct op_msg_type *msg = op_msg;
1337 struct ipa_uc_fw_stats *uc_fw_stat;
1338 struct IpaHwStatsWDIInfoData_t ipa_stat;
1339 struct hdd_ipa_priv *hdd_ipa;
1340 hdd_context_t *hdd_ctx;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301341 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001342
1343 if (!op_msg || !usr_ctxt) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301344 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s, INVALID ARG", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001345 return;
1346 }
1347
1348 if (HDD_IPA_UC_OPCODE_MAX <= msg->op_code) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301349 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001350 "%s, INVALID OPCODE %d", __func__, msg->op_code);
1351 return;
1352 }
1353
1354 hdd_ctx = (hdd_context_t *) usr_ctxt;
1355
1356 /*
1357 * When SSR is going on or driver is unloading, just return.
1358 */
1359 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301360 if (status) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301361 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001362 return;
1363 }
1364
1365 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1366
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301367 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001368 "%s, OPCODE %s", __func__, op_string[msg->op_code]);
1369
1370 if ((HDD_IPA_UC_OPCODE_TX_RESUME == msg->op_code) ||
1371 (HDD_IPA_UC_OPCODE_RX_RESUME == msg->op_code)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301372 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001373 hdd_ipa->activated_fw_pipe++;
1374 if (HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) {
1375 hdd_ipa->resource_loading = false;
1376 hdd_ipa_uc_proc_pending_event(hdd_ipa);
Yun Parkccc6d7a2015-12-02 14:50:13 -08001377 if (hdd_ipa->pending_cons_req)
1378 ipa_rm_notify_completion(
1379 IPA_RM_RESOURCE_GRANTED,
1380 IPA_RM_RESOURCE_WLAN_CONS);
Yun Park5b635012015-12-02 15:05:01 -08001381 hdd_ipa->pending_cons_req = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001382 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301383 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001384 }
1385
1386 if ((HDD_IPA_UC_OPCODE_TX_SUSPEND == msg->op_code) ||
1387 (HDD_IPA_UC_OPCODE_RX_SUSPEND == msg->op_code)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301388 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001389 hdd_ipa->activated_fw_pipe--;
1390 if (!hdd_ipa->activated_fw_pipe) {
1391 hdd_ipa_uc_disable_pipes(hdd_ipa);
Yun Park5b635012015-12-02 15:05:01 -08001392 if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1393 ipa_rm_release_resource(
1394 IPA_RM_RESOURCE_WLAN_PROD);
1395 /* Sync return success from IPA
1396 * Enable/resume all the PIPEs */
1397 hdd_ipa->resource_unloading = false;
1398 hdd_ipa_uc_proc_pending_event(hdd_ipa);
1399 hdd_ipa->pending_cons_req = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001400 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301401 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001402 }
1403
1404 if ((HDD_IPA_UC_OPCODE_STATS == msg->op_code) &&
1405 (HDD_IPA_UC_STAT_REASON_DEBUG == hdd_ipa->stat_req_reason)) {
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001406 struct ol_txrx_ipa_resources *res = &hdd_ipa->ipa_resource;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001407 /* STATs from host */
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301408 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001409 "==== IPA_UC WLAN_HOST CE ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001410 "CE RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001411 "CE RING SIZE: %d\n"
1412 "CE REG ADDR : 0x%llx",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001413 (unsigned long long)res->ce_sr_base_paddr,
1414 res->ce_sr_ring_size,
1415 (unsigned long long)res->ce_reg_paddr);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301416 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001417 "==== IPA_UC WLAN_HOST TX ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001418 "COMP RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001419 "COMP RING SIZE: %d\n"
1420 "NUM ALLOC BUF: %d\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001421 "COMP RING DBELL : 0x%llx",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001422 (unsigned long long)res->tx_comp_ring_base_paddr,
1423 res->tx_comp_ring_size,
1424 res->tx_num_alloc_buffer,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001425 (unsigned long long)hdd_ipa->tx_comp_doorbell_paddr);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301426 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001427 "==== IPA_UC WLAN_HOST RX ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001428 "IND RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001429 "IND RING SIZE: %d\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001430 "IND RING DBELL : 0x%llx\n"
1431 "PROC DONE IND ADDR : 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001432 "NUM EXCP PKT : %llu\n"
1433 "NUM TX BCMC : %llu\n"
1434 "NUM TX BCMC ERR : %llu",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001435 (unsigned long long)res->rx_rdy_ring_base_paddr,
1436 res->rx_rdy_ring_size,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001437 (unsigned long long)hdd_ipa->rx_ready_doorbell_paddr,
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001438 (unsigned long long)hdd_ipa->ipa_resource.
1439 rx_proc_done_idx_paddr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001440 hdd_ipa->stats.num_rx_excep,
1441 hdd_ipa->stats.num_tx_bcmc,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001442 (unsigned long long)hdd_ipa->stats.num_tx_bcmc_err);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301443 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001444 "==== IPA_UC WLAN_HOST CONTROL ====\n"
1445 "SAP NUM STAs: %d\n"
1446 "STA CONNECTED: %d\n"
1447 "TX PIPE HDL: %d\n"
1448 "RX PIPE HDL : %d\n"
1449 "RSC LOADING : %d\n"
1450 "RSC UNLOADING : %d\n"
1451 "PNDNG CNS RQT : %d",
1452 hdd_ipa->sap_num_connected_sta,
1453 hdd_ipa->sta_connected,
1454 hdd_ipa->tx_pipe_handle,
1455 hdd_ipa->rx_pipe_handle,
1456 (unsigned int)hdd_ipa->resource_loading,
1457 (unsigned int)hdd_ipa->resource_unloading,
1458 (unsigned int)hdd_ipa->pending_cons_req);
1459
1460 /* STATs from FW */
1461 uc_fw_stat = (struct ipa_uc_fw_stats *)
1462 ((uint8_t *)op_msg + sizeof(struct op_msg_type));
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301463 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001464 "==== IPA_UC WLAN_FW TX ====\n"
1465 "COMP RING BASE: 0x%x\n"
1466 "COMP RING SIZE: %d\n"
1467 "COMP RING DBELL : 0x%x\n"
1468 "COMP RING DBELL IND VAL : %d\n"
1469 "COMP RING DBELL CACHED VAL : %d\n"
1470 "COMP RING DBELL CACHED VAL : %d\n"
1471 "PKTS ENQ : %d\n"
1472 "PKTS COMP : %d\n"
1473 "IS SUSPEND : %d\n"
1474 "RSVD : 0x%x",
1475 uc_fw_stat->tx_comp_ring_base,
1476 uc_fw_stat->tx_comp_ring_size,
1477 uc_fw_stat->tx_comp_ring_dbell_addr,
1478 uc_fw_stat->tx_comp_ring_dbell_ind_val,
1479 uc_fw_stat->tx_comp_ring_dbell_cached_val,
1480 uc_fw_stat->tx_comp_ring_dbell_cached_val,
1481 uc_fw_stat->tx_pkts_enqueued,
1482 uc_fw_stat->tx_pkts_completed,
1483 uc_fw_stat->tx_is_suspend, uc_fw_stat->tx_reserved);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301484 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001485 "==== IPA_UC WLAN_FW RX ====\n"
1486 "IND RING BASE: 0x%x\n"
1487 "IND RING SIZE: %d\n"
1488 "IND RING DBELL : 0x%x\n"
1489 "IND RING DBELL IND VAL : %d\n"
1490 "IND RING DBELL CACHED VAL : %d\n"
1491 "RDY IND ADDR : 0x%x\n"
1492 "RDY IND CACHE VAL : %d\n"
1493 "RFIL IND : %d\n"
1494 "NUM PKT INDICAT : %d\n"
1495 "BUF REFIL : %d\n"
1496 "NUM DROP NO SPC : %d\n"
1497 "NUM DROP NO BUF : %d\n"
1498 "IS SUSPND : %d\n"
1499 "RSVD : 0x%x\n",
1500 uc_fw_stat->rx_ind_ring_base,
1501 uc_fw_stat->rx_ind_ring_size,
1502 uc_fw_stat->rx_ind_ring_dbell_addr,
1503 uc_fw_stat->rx_ind_ring_dbell_ind_val,
1504 uc_fw_stat->rx_ind_ring_dbell_ind_cached_val,
1505 uc_fw_stat->rx_ind_ring_rdidx_addr,
1506 uc_fw_stat->rx_ind_ring_rd_idx_cached_val,
1507 uc_fw_stat->rx_refill_idx,
1508 uc_fw_stat->rx_num_pkts_indicated,
1509 uc_fw_stat->rx_buf_refilled,
1510 uc_fw_stat->rx_num_ind_drop_no_space,
1511 uc_fw_stat->rx_num_ind_drop_no_buf,
1512 uc_fw_stat->rx_is_suspend, uc_fw_stat->rx_reserved);
1513 /* STATs from IPA */
1514 ipa_get_wdi_stats(&ipa_stat);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301515 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001516 "==== IPA_UC IPA TX ====\n"
1517 "NUM PROCD : %d\n"
1518 "CE DBELL : 0x%x\n"
1519 "NUM DBELL FIRED : %d\n"
1520 "COMP RNG FULL : %d\n"
1521 "COMP RNG EMPT : %d\n"
1522 "COMP RNG USE HGH : %d\n"
1523 "COMP RNG USE LOW : %d\n"
1524 "BAM FIFO FULL : %d\n"
1525 "BAM FIFO EMPT : %d\n"
1526 "BAM FIFO USE HGH : %d\n"
1527 "BAM FIFO USE LOW : %d\n"
1528 "NUM DBELL : %d\n"
1529 "NUM UNEXP DBELL : %d\n"
1530 "NUM BAM INT HDL : 0x%x\n"
1531 "NUM BAM INT NON-RUN : 0x%x\n"
1532 "NUM QMB INT HDL : 0x%x",
1533 ipa_stat.tx_ch_stats.num_pkts_processed,
1534 ipa_stat.tx_ch_stats.copy_engine_doorbell_value,
1535 ipa_stat.tx_ch_stats.num_db_fired,
1536 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringFull,
1537 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringEmpty,
1538 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageHigh,
1539 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageLow,
1540 ipa_stat.tx_ch_stats.bam_stats.bamFifoFull,
1541 ipa_stat.tx_ch_stats.bam_stats.bamFifoEmpty,
1542 ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageHigh,
1543 ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageLow,
1544 ipa_stat.tx_ch_stats.num_db,
1545 ipa_stat.tx_ch_stats.num_unexpected_db,
1546 ipa_stat.tx_ch_stats.num_bam_int_handled,
1547 ipa_stat.tx_ch_stats.
1548 num_bam_int_in_non_runnning_state,
1549 ipa_stat.tx_ch_stats.num_qmb_int_handled);
1550
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301551 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001552 "==== IPA_UC IPA RX ====\n"
1553 "MAX OST PKT : %d\n"
1554 "NUM PKT PRCSD : %d\n"
1555 "RNG RP : 0x%x\n"
1556 "COMP RNG FULL : %d\n"
1557 "COMP RNG EMPT : %d\n"
1558 "COMP RNG USE HGH : %d\n"
1559 "COMP RNG USE LOW : %d\n"
1560 "BAM FIFO FULL : %d\n"
1561 "BAM FIFO EMPT : %d\n"
1562 "BAM FIFO USE HGH : %d\n"
1563 "BAM FIFO USE LOW : %d\n"
1564 "NUM DB : %d\n"
1565 "NUM UNEXP DB : %d\n"
1566 "NUM BAM INT HNDL : 0x%x\n",
1567 ipa_stat.rx_ch_stats.max_outstanding_pkts,
1568 ipa_stat.rx_ch_stats.num_pkts_processed,
1569 ipa_stat.rx_ch_stats.rx_ring_rp_value,
1570 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringFull,
1571 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringEmpty,
1572 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageHigh,
1573 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageLow,
1574 ipa_stat.rx_ch_stats.bam_stats.bamFifoFull,
1575 ipa_stat.rx_ch_stats.bam_stats.bamFifoEmpty,
1576 ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageHigh,
1577 ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageLow,
1578 ipa_stat.rx_ch_stats.num_db,
1579 ipa_stat.rx_ch_stats.num_unexpected_db,
1580 ipa_stat.rx_ch_stats.num_bam_int_handled);
1581 } else if ((HDD_IPA_UC_OPCODE_STATS == msg->op_code) &&
1582 (HDD_IPA_UC_STAT_REASON_BW_CAL == hdd_ipa->stat_req_reason)) {
1583 /* STATs from FW */
1584 uc_fw_stat = (struct ipa_uc_fw_stats *)
1585 ((uint8_t *)op_msg + sizeof(struct op_msg_type));
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301586 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001587 hdd_ipa->ipa_tx_packets_diff = HDD_BW_GET_DIFF(
1588 uc_fw_stat->tx_pkts_completed,
1589 hdd_ipa->ipa_p_tx_packets);
1590 hdd_ipa->ipa_rx_packets_diff = HDD_BW_GET_DIFF(
1591 (uc_fw_stat->rx_num_ind_drop_no_space +
1592 uc_fw_stat->rx_num_ind_drop_no_buf +
1593 uc_fw_stat->rx_num_pkts_indicated),
1594 hdd_ipa->ipa_p_rx_packets);
1595
1596 hdd_ipa->ipa_p_tx_packets = uc_fw_stat->tx_pkts_completed;
1597 hdd_ipa->ipa_p_rx_packets =
1598 (uc_fw_stat->rx_num_ind_drop_no_space +
1599 uc_fw_stat->rx_num_ind_drop_no_buf +
1600 uc_fw_stat->rx_num_pkts_indicated);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301601 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001602 } else {
1603 HDD_IPA_LOG(LOGE, "INVALID REASON %d",
1604 hdd_ipa->stat_req_reason);
1605 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301606 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001607}
1608
1609
1610/**
1611 * hdd_ipa_uc_offload_enable_disable() - wdi enable/disable notify to fw
1612 * @adapter: device adapter instance
1613 * @offload_type: MCC or SCC
1614 * @enable: TX offload enable or disable
1615 *
1616 * Return: none
1617 */
1618static void hdd_ipa_uc_offload_enable_disable(hdd_adapter_t *adapter,
1619 uint32_t offload_type, uint32_t enable)
1620{
1621 struct sir_ipa_offload_enable_disable ipa_offload_enable_disable;
1622
1623 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
1624 * channel change indication. Since these indications are sent by lower
1625 * layer as SAP updates and IPA doesn't have to do anything for these
1626 * updates so ignoring!
1627 */
Krunal Sonibe766b02016-03-10 13:00:44 -08001628 if (QDF_SAP_MODE == adapter->device_mode && adapter->ipa_context)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001629 return;
1630
1631 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
1632 * channel change indication. Since these indications are sent by lower
1633 * layer as SAP updates and IPA doesn't have to do anything for these
1634 * updates so ignoring!
1635 */
1636 if (adapter->ipa_context)
1637 return;
1638
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301639 qdf_mem_zero(&ipa_offload_enable_disable,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001640 sizeof(ipa_offload_enable_disable));
1641 ipa_offload_enable_disable.offload_type = offload_type;
1642 ipa_offload_enable_disable.vdev_id = adapter->sessionId;
1643 ipa_offload_enable_disable.enable = enable;
1644
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301645 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001646 "%s: offload_type=%d, vdev_id=%d, enable=%d", __func__,
1647 ipa_offload_enable_disable.offload_type,
1648 ipa_offload_enable_disable.vdev_id,
1649 ipa_offload_enable_disable.enable);
1650
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301651 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001652 sme_ipa_offload_enable_disable(WLAN_HDD_GET_HAL_CTX(adapter),
1653 adapter->sessionId, &ipa_offload_enable_disable)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301654 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001655 "%s: Failure to enable IPA offload \
1656 (offload_type=%d, vdev_id=%d, enable=%d)", __func__,
1657 ipa_offload_enable_disable.offload_type,
1658 ipa_offload_enable_disable.vdev_id,
1659 ipa_offload_enable_disable.enable);
1660 }
1661}
1662
1663/**
1664 * hdd_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler
1665 * @work: uC OP work
1666 *
1667 * Return: None
1668 */
1669static void hdd_ipa_uc_fw_op_event_handler(struct work_struct *work)
1670{
1671 struct op_msg_type *msg;
1672 struct uc_op_work_struct *uc_op_work = container_of(work,
1673 struct uc_op_work_struct, work);
1674 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1675
1676 cds_ssr_protect(__func__);
1677
1678 msg = uc_op_work->msg;
1679 uc_op_work->msg = NULL;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301680 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001681 "%s, posted msg %d", __func__, msg->op_code);
1682
1683 hdd_ipa_uc_op_cb(msg, hdd_ipa->hdd_ctx);
1684
1685 cds_ssr_unprotect(__func__);
1686
1687 return;
1688}
1689
1690/**
1691 * hdd_ipa_uc_op_event_handler() - Adapter lookup
1692 * hdd_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler
1693 * @op_msg: operation message received from firmware
1694 * @hdd_ctx: Global HDD context
1695 *
1696 * Return: None
1697 */
1698static void hdd_ipa_uc_op_event_handler(uint8_t *op_msg, void *hdd_ctx)
1699{
1700 struct hdd_ipa_priv *hdd_ipa;
1701 struct op_msg_type *msg;
1702 struct uc_op_work_struct *uc_op_work;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301703 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001704
1705 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301706 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001707 goto end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001708
1709 msg = (struct op_msg_type *)op_msg;
1710 hdd_ipa = ((hdd_context_t *)hdd_ctx)->hdd_ipa;
1711
1712 if (unlikely(!hdd_ipa))
1713 goto end;
1714
1715 if (HDD_IPA_UC_OPCODE_MAX <= msg->op_code) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301716 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s: Invalid OP Code (%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001717 __func__, msg->op_code);
1718 goto end;
1719 }
1720
1721 uc_op_work = &hdd_ipa->uc_op_work[msg->op_code];
1722 if (uc_op_work->msg)
1723 /* When the same uC OPCODE is already pended, just return */
1724 goto end;
1725
1726 uc_op_work->msg = msg;
1727 schedule_work(&uc_op_work->work);
1728 return;
1729
1730end:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301731 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001732}
1733
1734/**
Rajeev Kumar217f2172016-01-06 18:11:55 -08001735 * hdd_ipa_init_uc_op_work - init ipa uc op work
1736 * @work: struct work_struct
1737 * @work_handler: work_handler
1738 *
1739 * Return: none
1740 */
Rajeev Kumar217f2172016-01-06 18:11:55 -08001741static void hdd_ipa_init_uc_op_work(struct work_struct *work,
1742 work_func_t work_handler)
1743{
1744 INIT_WORK(work, work_handler);
1745}
Rajeev Kumar217f2172016-01-06 18:11:55 -08001746
1747
1748/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001749 * hdd_ipa_uc_ol_init() - Initialize IPA uC offload
1750 * @hdd_ctx: Global HDD context
1751 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301752 * Return: QDF_STATUS
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001753 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301754static QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001755{
1756 struct ipa_wdi_in_params pipe_in;
1757 struct ipa_wdi_out_params pipe_out;
1758 struct hdd_ipa_priv *ipa_ctxt = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1759 p_cds_contextType cds_ctx = hdd_ctx->pcds_context;
1760 uint8_t i;
1761
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301762 qdf_mem_zero(&pipe_in, sizeof(struct ipa_wdi_in_params));
1763 qdf_mem_zero(&pipe_out, sizeof(struct ipa_wdi_out_params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001764
Anurag Chouhanffb21542016-02-17 14:33:03 +05301765 qdf_list_create(&ipa_ctxt->pending_event, 1000);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301766 qdf_mutex_create(&ipa_ctxt->event_lock);
1767 qdf_mutex_create(&ipa_ctxt->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001768
1769 /* TX PIPE */
1770 pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
1771 pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN;
1772 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
1773 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
1774 pipe_in.sys.ipa_ep_cfg.hdr.hdr_additional_const_len =
1775 HDD_IPA_UC_WLAN_8023_HDR_SIZE;
1776 pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
1777 pipe_in.sys.client = IPA_CLIENT_WLAN1_CONS;
1778 pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize;
1779 pipe_in.sys.priv = hdd_ctx->hdd_ipa;
1780 pipe_in.sys.ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
1781 pipe_in.sys.notify = hdd_ipa_i2w_cb;
1782 if (!hdd_ipa_is_rm_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301783 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001784 "%s: IPA RM DISABLED, IPA AWAKE", __func__);
1785 pipe_in.sys.keep_ipa_awake = true;
1786 }
1787
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001788 pipe_in.u.dl.comp_ring_base_pa =
1789 ipa_ctxt->ipa_resource.tx_comp_ring_base_paddr;
Leo Chang3bc8fed2015-11-13 10:59:47 -08001790 pipe_in.u.dl.comp_ring_size =
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001791 ipa_ctxt->ipa_resource.tx_comp_ring_size *
1792 sizeof(qdf_dma_addr_t);
1793 pipe_in.u.dl.ce_ring_base_pa =
1794 ipa_ctxt->ipa_resource.ce_sr_base_paddr;
1795 pipe_in.u.dl.ce_door_bell_pa = ipa_ctxt->ipa_resource.ce_reg_paddr;
1796 pipe_in.u.dl.ce_ring_size =
1797 ipa_ctxt->ipa_resource.ce_sr_ring_size;
1798 pipe_in.u.dl.num_tx_buffers =
1799 ipa_ctxt->ipa_resource.tx_num_alloc_buffer;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001800
1801 /* Connect WDI IPA PIPE */
1802 ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
1803 /* Micro Controller Doorbell register */
Leo Chang3bc8fed2015-11-13 10:59:47 -08001804 ipa_ctxt->tx_comp_doorbell_paddr = pipe_out.uc_door_bell_pa;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001805 /* WLAN TX PIPE Handle */
1806 ipa_ctxt->tx_pipe_handle = pipe_out.clnt_hdl;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301807 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001808 "TX : CRBPA 0x%x, CRS %d, CERBPA 0x%x, CEDPA 0x%x,"
1809 " CERZ %d, NB %d, CDBPAD 0x%x",
1810 (unsigned int)pipe_in.u.dl.comp_ring_base_pa,
1811 pipe_in.u.dl.comp_ring_size,
1812 (unsigned int)pipe_in.u.dl.ce_ring_base_pa,
1813 (unsigned int)pipe_in.u.dl.ce_door_bell_pa,
1814 pipe_in.u.dl.ce_ring_size,
1815 pipe_in.u.dl.num_tx_buffers,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001816 (unsigned int)ipa_ctxt->tx_comp_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001817
1818 /* RX PIPE */
1819 pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
1820 pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_RX_HDR_LEN;
1821 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 0;
1822 pipe_in.sys.ipa_ep_cfg.hdr.hdr_metadata_reg_valid = 1;
1823 pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
1824 pipe_in.sys.client = IPA_CLIENT_WLAN1_PROD;
1825 pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize +
1826 sizeof(struct sps_iovec);
1827 pipe_in.sys.notify = hdd_ipa_w2i_cb;
1828 if (!hdd_ipa_is_rm_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301829 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001830 "%s: IPA RM DISABLED, IPA AWAKE", __func__);
1831 pipe_in.sys.keep_ipa_awake = true;
1832 }
1833
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001834 pipe_in.u.ul.rdy_ring_base_pa =
1835 ipa_ctxt->ipa_resource.rx_rdy_ring_base_paddr;
1836 pipe_in.u.ul.rdy_ring_size =
1837 ipa_ctxt->ipa_resource.rx_rdy_ring_size;
1838 pipe_in.u.ul.rdy_ring_rp_pa =
1839 ipa_ctxt->ipa_resource.rx_proc_done_idx_paddr;
Leo Chang3bc8fed2015-11-13 10:59:47 -08001840 HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001841 ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
Leo Chang3bc8fed2015-11-13 10:59:47 -08001842 ipa_ctxt->rx_ready_doorbell_paddr = pipe_out.uc_door_bell_pa;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001843 ipa_ctxt->rx_pipe_handle = pipe_out.clnt_hdl;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301844 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001845 "RX : RRBPA 0x%x, RRS %d, PDIPA 0x%x, RDY_DB_PAD 0x%x",
1846 (unsigned int)pipe_in.u.ul.rdy_ring_base_pa,
1847 pipe_in.u.ul.rdy_ring_size,
1848 (unsigned int)pipe_in.u.ul.rdy_ring_rp_pa,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001849 (unsigned int)ipa_ctxt->rx_ready_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001850
1851 ol_txrx_ipa_uc_set_doorbell_paddr(cds_ctx->pdev_txrx_ctx,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001852 ipa_ctxt->tx_comp_doorbell_paddr,
1853 ipa_ctxt->rx_ready_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001854
1855 ol_txrx_ipa_uc_register_op_cb(cds_ctx->pdev_txrx_ctx,
1856 hdd_ipa_uc_op_event_handler, (void *)hdd_ctx);
1857
1858 for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) {
Rajeev Kumar217f2172016-01-06 18:11:55 -08001859 hdd_ipa_init_uc_op_work(&ipa_ctxt->uc_op_work[i].work,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001860 hdd_ipa_uc_fw_op_event_handler);
1861 ipa_ctxt->uc_op_work[i].msg = NULL;
1862 }
1863
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301864 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001865}
1866
Leo Change3e49442015-10-26 20:07:13 -07001867/**
1868 * hdd_ipa_uc_force_pipe_shutdown() - Force shutdown IPA pipe
1869 * @hdd_ctx: hdd main context
1870 *
1871 * Force shutdown IPA pipe
1872 * Independent of FW pipe status, IPA pipe shutdonw progress
1873 * in case, any STA does not leave properly, IPA HW pipe should cleaned up
1874 * independent from FW pipe status
1875 *
1876 * Return: NONE
1877 */
1878void hdd_ipa_uc_force_pipe_shutdown(hdd_context_t *hdd_ctx)
1879{
1880 struct hdd_ipa_priv *hdd_ipa;
1881
1882 if (!hdd_ipa_is_enabled(hdd_ctx) || !hdd_ctx->hdd_ipa)
1883 return;
1884
1885 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1886 if (false == hdd_ipa->ipa_pipes_down) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301887 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Leo Change3e49442015-10-26 20:07:13 -07001888 "IPA pipes are not down yet, force shutdown");
1889 hdd_ipa_uc_disable_pipes(hdd_ipa);
1890 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301891 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Leo Change3e49442015-10-26 20:07:13 -07001892 "IPA pipes are down, do nothing");
1893 }
1894
1895 return;
1896}
1897
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001898/**
1899 * hdd_ipa_uc_ssr_deinit() - handle ipa deinit for SSR
1900 *
1901 * Deinit basic IPA UC host side to be in sync reloaded FW during
1902 * SSR
1903 *
1904 * Return: 0 - Success
1905 */
1906int hdd_ipa_uc_ssr_deinit(void)
1907{
1908 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1909 int idx;
1910 struct hdd_ipa_iface_context *iface_context;
1911
Leo Chang3bc8fed2015-11-13 10:59:47 -08001912 if ((!hdd_ipa) || (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001913 return 0;
1914
1915 /* Clean up HDD IPA interfaces */
1916 for (idx = 0; (hdd_ipa->num_iface > 0) &&
1917 (idx < HDD_IPA_MAX_IFACE); idx++) {
1918 iface_context = &hdd_ipa->iface_context[idx];
1919 if (iface_context && iface_context->adapter)
1920 hdd_ipa_cleanup_iface(iface_context);
1921 }
1922
1923 /* After SSR, wlan driver reloads FW again. But we need to protect
1924 * IPA submodule during SSR transient state. So deinit basic IPA
1925 * UC host side to be in sync with reloaded FW during SSR
1926 */
Yun Parkf7dc8cd2015-11-17 15:25:12 -08001927 if (!hdd_ipa->ipa_pipes_down)
1928 hdd_ipa_uc_disable_pipes(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001929
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301930 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001931 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1932 hdd_ipa->assoc_stas_map[idx].is_reserved = false;
1933 hdd_ipa->assoc_stas_map[idx].sta_id = 0xFF;
1934 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301935 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001936
1937 /* Full IPA driver cleanup not required since wlan driver is now
1938 * unloaded and reloaded after SSR.
1939 */
1940 return 0;
1941}
1942
1943/**
1944 * hdd_ipa_uc_ssr_reinit() - handle ipa reinit after SSR
1945 *
1946 * Init basic IPA UC host side to be in sync with reloaded FW after
1947 * SSR to resume IPA UC operations
1948 *
1949 * Return: 0 - Success
1950 */
1951int hdd_ipa_uc_ssr_reinit(void)
1952{
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001953
1954 /* After SSR is complete, IPA UC can resume operation. But now wlan
1955 * driver will be unloaded and reloaded, which takes care of IPA cleanup
1956 * and initialization. This is a placeholder func if IPA has to resume
1957 * operations without driver reload.
1958 */
1959 return 0;
1960}
Leo Chang3bc8fed2015-11-13 10:59:47 -08001961
1962/**
1963 * hdd_ipa_tx_packet_ipa() - send packet to IPA
1964 * @hdd_ctx: Global HDD context
1965 * @skb: skb sent to IPA
1966 * @session_id: send packet instance session id
1967 *
1968 * Send TX packet which generated by system to IPA.
1969 * This routine only will be used for function verification
1970 *
1971 * Return: NULL packet sent to IPA properly
1972 * NULL invalid packet drop
1973 * skb packet not sent to IPA. legacy data path should handle
1974 */
1975struct sk_buff *hdd_ipa_tx_packet_ipa(hdd_context_t *hdd_ctx,
1976 struct sk_buff *skb, uint8_t session_id)
Leo Change3e49442015-10-26 20:07:13 -07001977{
Leo Chang3bc8fed2015-11-13 10:59:47 -08001978 struct ipa_header *ipa_header;
1979 struct frag_header *frag_header;
Leo Chang07b28f62016-05-11 12:29:22 -07001980 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
Leo Chang3bc8fed2015-11-13 10:59:47 -08001981
1982 if (!hdd_ipa_uc_is_enabled(hdd_ctx))
1983 return skb;
1984
Leo Chang07b28f62016-05-11 12:29:22 -07001985 if (!hdd_ipa)
1986 return skb;
1987
1988 if (HDD_IPA_UC_NUM_WDI_PIPE != hdd_ipa->activated_fw_pipe)
1989 return skb;
1990
Leo Changcc923e22016-06-16 15:29:03 -07001991 if (skb_headroom(skb) <
1992 (sizeof(struct ipa_header) + sizeof(struct frag_header)))
Leo Chang07b28f62016-05-11 12:29:22 -07001993 return skb;
1994
Leo Chang3bc8fed2015-11-13 10:59:47 -08001995 ipa_header = (struct ipa_header *) skb_push(skb,
1996 sizeof(struct ipa_header));
1997 if (!ipa_header) {
1998 /* No headroom, legacy */
1999 return skb;
2000 }
2001 memset(ipa_header, 0, sizeof(*ipa_header));
2002 ipa_header->vdev_id = 0;
2003
2004 frag_header = (struct frag_header *) skb_push(skb,
2005 sizeof(struct frag_header));
2006 if (!frag_header) {
2007 /* No headroom, drop */
2008 kfree_skb(skb);
2009 return NULL;
2010 }
2011 memset(frag_header, 0, sizeof(*frag_header));
2012 frag_header->length = skb->len - sizeof(struct frag_header)
2013 - sizeof(struct ipa_header);
2014
2015 ipa_tx_dp(IPA_CLIENT_WLAN1_CONS, skb, NULL);
2016 return NULL;
Leo Change3e49442015-10-26 20:07:13 -07002017}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002018
2019/**
2020 * hdd_ipa_wake_lock_timer_func() - Wake lock work handler
2021 * @work: scheduled work
2022 *
2023 * When IPA resources are released in hdd_ipa_rm_try_release() we do
2024 * not want to immediately release the wake lock since the system
2025 * would then potentially try to suspend when there is a healthy data
2026 * rate. Deferred work is scheduled and this function handles the
2027 * work. When this function is called, if the IPA resource is still
2028 * released then we release the wake lock.
2029 *
2030 * Return: None
2031 */
2032static void hdd_ipa_wake_lock_timer_func(struct work_struct *work)
2033{
2034 struct hdd_ipa_priv *hdd_ipa = container_of(to_delayed_work(work),
2035 struct hdd_ipa_priv,
2036 wake_lock_work);
2037
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302038 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002039
2040 if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED)
2041 goto end;
2042
2043 hdd_ipa->wake_lock_released = true;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302044 qdf_wake_lock_release(&hdd_ipa->wake_lock,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002045 WIFI_POWER_EVENT_WAKELOCK_IPA);
2046
2047end:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302048 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002049}
2050
2051/**
2052 * hdd_ipa_rm_request() - Request resource from IPA
2053 * @hdd_ipa: Global HDD IPA context
2054 *
2055 * Return: 0 on success, negative errno on error
2056 */
2057static int hdd_ipa_rm_request(struct hdd_ipa_priv *hdd_ipa)
2058{
2059 int ret = 0;
2060
2061 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2062 return 0;
2063
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302064 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002065
2066 switch (hdd_ipa->rm_state) {
2067 case HDD_IPA_RM_GRANTED:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302068 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002069 return 0;
2070 case HDD_IPA_RM_GRANT_PENDING:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302071 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002072 return -EINPROGRESS;
2073 case HDD_IPA_RM_RELEASED:
2074 hdd_ipa->rm_state = HDD_IPA_RM_GRANT_PENDING;
2075 break;
2076 }
2077
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302078 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002079
2080 ret = ipa_rm_inactivity_timer_request_resource(
2081 IPA_RM_RESOURCE_WLAN_PROD);
2082
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302083 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002084 if (ret == 0) {
2085 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
2086 hdd_ipa->stats.num_rm_grant_imm++;
2087 }
2088
2089 cancel_delayed_work(&hdd_ipa->wake_lock_work);
2090 if (hdd_ipa->wake_lock_released) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302091 qdf_wake_lock_acquire(&hdd_ipa->wake_lock,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002092 WIFI_POWER_EVENT_WAKELOCK_IPA);
2093 hdd_ipa->wake_lock_released = false;
2094 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302095 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002096
2097 return ret;
2098}
2099
2100/**
2101 * hdd_ipa_rm_try_release() - Attempt to release IPA resource
2102 * @hdd_ipa: Global HDD IPA context
2103 *
2104 * Return: 0 if resources released, negative errno otherwise
2105 */
2106static int hdd_ipa_rm_try_release(struct hdd_ipa_priv *hdd_ipa)
2107{
2108 int ret = 0;
2109
2110 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2111 return 0;
2112
2113 if (atomic_read(&hdd_ipa->tx_ref_cnt))
2114 return -EAGAIN;
2115
2116 spin_lock_bh(&hdd_ipa->q_lock);
2117 if (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
2118 (hdd_ipa->pending_hw_desc_cnt || hdd_ipa->pend_q_cnt)) {
2119 spin_unlock_bh(&hdd_ipa->q_lock);
2120 return -EAGAIN;
2121 }
2122 spin_unlock_bh(&hdd_ipa->q_lock);
2123
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302124 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002125
Nirav Shahcbc6d722016-03-01 16:24:53 +05302126 if (!qdf_nbuf_is_queue_empty(&hdd_ipa->pm_queue_head)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302127 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002128 return -EAGAIN;
2129 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302130 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002131
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302132 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002133 switch (hdd_ipa->rm_state) {
2134 case HDD_IPA_RM_GRANTED:
2135 break;
2136 case HDD_IPA_RM_GRANT_PENDING:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302137 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002138 return -EINPROGRESS;
2139 case HDD_IPA_RM_RELEASED:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302140 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002141 return 0;
2142 }
2143
2144 /* IPA driver returns immediately so set the state here to avoid any
2145 * race condition.
2146 */
2147 hdd_ipa->rm_state = HDD_IPA_RM_RELEASED;
2148 hdd_ipa->stats.num_rm_release++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302149 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002150
2151 ret =
2152 ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_WLAN_PROD);
2153
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302154 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002155 if (unlikely(ret != 0)) {
2156 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
2157 WARN_ON(1);
2158 }
2159
2160 /*
2161 * If wake_lock is released immediately, kernel would try to suspend
2162 * immediately as well, Just avoid ping-pong between suspend-resume
2163 * while there is healthy amount of data transfer going on by
2164 * releasing the wake_lock after some delay.
2165 */
2166 schedule_delayed_work(&hdd_ipa->wake_lock_work,
2167 msecs_to_jiffies
2168 (HDD_IPA_RX_INACTIVITY_MSEC_DELAY));
2169
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302170 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002171
2172 return ret;
2173}
2174
2175/**
2176 * hdd_ipa_rm_notify() - IPA resource manager notifier callback
2177 * @user_data: user data registered with IPA
2178 * @event: the IPA resource manager event that occurred
2179 * @data: the data associated with the event
2180 *
2181 * Return: None
2182 */
2183static void hdd_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
2184 unsigned long data)
2185{
2186 struct hdd_ipa_priv *hdd_ipa = user_data;
2187
2188 if (unlikely(!hdd_ipa))
2189 return;
2190
2191 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2192 return;
2193
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302194 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Evt: %d", event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002195
2196 switch (event) {
2197 case IPA_RM_RESOURCE_GRANTED:
2198 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2199 /* RM Notification comes with ISR context
2200 * it should be serialized into work queue to avoid
2201 * ISR sleep problem
2202 */
2203 hdd_ipa->uc_rm_work.event = event;
2204 schedule_work(&hdd_ipa->uc_rm_work.work);
2205 break;
2206 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302207 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002208 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302209 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002210 hdd_ipa->stats.num_rm_grant++;
2211 break;
2212
2213 case IPA_RM_RESOURCE_RELEASED:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302214 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "RM Release");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002215 hdd_ipa->resource_unloading = false;
2216 break;
2217
2218 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302219 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Unknown RM Evt: %d", event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002220 break;
2221 }
2222}
2223
2224/**
2225 * hdd_ipa_rm_cons_release() - WLAN consumer resource release handler
2226 *
2227 * Callback function registered with IPA that is called when IPA wants
2228 * to release the WLAN consumer resource
2229 *
2230 * Return: 0 if the request is granted, negative errno otherwise
2231 */
2232static int hdd_ipa_rm_cons_release(void)
2233{
2234 return 0;
2235}
2236
2237/**
2238 * hdd_ipa_rm_cons_request() - WLAN consumer resource request handler
2239 *
2240 * Callback function registered with IPA that is called when IPA wants
2241 * to access the WLAN consumer resource
2242 *
2243 * Return: 0 if the request is granted, negative errno otherwise
2244 */
2245static int hdd_ipa_rm_cons_request(void)
2246{
Yun Park4d8b60a2015-10-22 13:59:32 -07002247 int ret = 0;
2248
2249 if (ghdd_ipa->resource_loading) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302250 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Yun Park4d8b60a2015-10-22 13:59:32 -07002251 "%s: IPA resource loading in progress",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002252 __func__);
2253 ghdd_ipa->pending_cons_req = true;
Yun Park4d8b60a2015-10-22 13:59:32 -07002254 ret = -EINPROGRESS;
2255 } else if (ghdd_ipa->resource_unloading) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302256 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Yun Park4d8b60a2015-10-22 13:59:32 -07002257 "%s: IPA resource unloading in progress",
2258 __func__);
2259 ghdd_ipa->pending_cons_req = true;
2260 ret = -EPERM;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002261 }
Yun Park4d8b60a2015-10-22 13:59:32 -07002262
2263 return ret;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002264}
2265
2266/**
2267 * hdd_ipa_set_perf_level() - Set IPA performance level
2268 * @hdd_ctx: Global HDD context
2269 * @tx_packets: Number of packets transmitted in the last sample period
2270 * @rx_packets: Number of packets received in the last sample period
2271 *
2272 * Return: 0 on success, negative errno on error
2273 */
2274int hdd_ipa_set_perf_level(hdd_context_t *hdd_ctx, uint64_t tx_packets,
2275 uint64_t rx_packets)
2276{
2277 uint32_t next_cons_bw, next_prod_bw;
2278 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2279 struct ipa_rm_perf_profile profile;
2280 int ret;
2281
2282 if ((!hdd_ipa_is_enabled(hdd_ctx)) ||
2283 (!hdd_ipa_is_clk_scaling_enabled(hdd_ctx)))
2284 return 0;
2285
2286 memset(&profile, 0, sizeof(profile));
2287
2288 if (tx_packets > (hdd_ctx->config->busBandwidthHighThreshold / 2))
2289 next_cons_bw = hdd_ctx->config->IpaHighBandwidthMbps;
2290 else if (tx_packets >
2291 (hdd_ctx->config->busBandwidthMediumThreshold / 2))
2292 next_cons_bw = hdd_ctx->config->IpaMediumBandwidthMbps;
2293 else
2294 next_cons_bw = hdd_ctx->config->IpaLowBandwidthMbps;
2295
2296 if (rx_packets > (hdd_ctx->config->busBandwidthHighThreshold / 2))
2297 next_prod_bw = hdd_ctx->config->IpaHighBandwidthMbps;
2298 else if (rx_packets >
2299 (hdd_ctx->config->busBandwidthMediumThreshold / 2))
2300 next_prod_bw = hdd_ctx->config->IpaMediumBandwidthMbps;
2301 else
2302 next_prod_bw = hdd_ctx->config->IpaLowBandwidthMbps;
2303
2304 HDD_IPA_LOG(LOG1,
2305 "CONS perf curr: %d, next: %d",
2306 hdd_ipa->curr_cons_bw, next_cons_bw);
2307 HDD_IPA_LOG(LOG1,
2308 "PROD perf curr: %d, next: %d",
2309 hdd_ipa->curr_prod_bw, next_prod_bw);
2310
2311 if (hdd_ipa->curr_cons_bw != next_cons_bw) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302312 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002313 "Requesting CONS perf curr: %d, next: %d",
2314 hdd_ipa->curr_cons_bw, next_cons_bw);
2315 profile.max_supported_bandwidth_mbps = next_cons_bw;
2316 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_CONS,
2317 &profile);
2318 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302319 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002320 "RM CONS set perf profile failed: %d", ret);
2321
2322 return ret;
2323 }
2324 hdd_ipa->curr_cons_bw = next_cons_bw;
2325 hdd_ipa->stats.num_cons_perf_req++;
2326 }
2327
2328 if (hdd_ipa->curr_prod_bw != next_prod_bw) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302329 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002330 "Requesting PROD perf curr: %d, next: %d",
2331 hdd_ipa->curr_prod_bw, next_prod_bw);
2332 profile.max_supported_bandwidth_mbps = next_prod_bw;
2333 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_PROD,
2334 &profile);
2335 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302336 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002337 "RM PROD set perf profile failed: %d", ret);
2338 return ret;
2339 }
2340 hdd_ipa->curr_prod_bw = next_prod_bw;
2341 hdd_ipa->stats.num_prod_perf_req++;
2342 }
2343
2344 return 0;
2345}
2346
2347/**
Rajeev Kumar217f2172016-01-06 18:11:55 -08002348 * hdd_ipa_init_uc_rm_work - init ipa uc resource manager work
2349 * @work: struct work_struct
2350 * @work_handler: work_handler
2351 *
2352 * Return: none
2353 */
Rajeev Kumar217f2172016-01-06 18:11:55 -08002354static void hdd_ipa_init_uc_rm_work(struct work_struct *work,
2355 work_func_t work_handler)
2356{
2357 INIT_WORK(work, work_handler);
2358}
Rajeev Kumar217f2172016-01-06 18:11:55 -08002359
2360/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002361 * hdd_ipa_setup_rm() - Setup IPA resource management
2362 * @hdd_ipa: Global HDD IPA context
2363 *
2364 * Return: 0 on success, negative errno on error
2365 */
2366static int hdd_ipa_setup_rm(struct hdd_ipa_priv *hdd_ipa)
2367{
2368 struct ipa_rm_create_params create_params = { 0 };
2369 int ret;
2370
2371 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2372 return 0;
2373
Rajeev Kumar217f2172016-01-06 18:11:55 -08002374 hdd_ipa_init_uc_rm_work(&hdd_ipa->uc_rm_work.work,
2375 hdd_ipa_uc_rm_notify_defer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002376 memset(&create_params, 0, sizeof(create_params));
2377 create_params.name = IPA_RM_RESOURCE_WLAN_PROD;
2378 create_params.reg_params.user_data = hdd_ipa;
2379 create_params.reg_params.notify_cb = hdd_ipa_rm_notify;
2380 create_params.floor_voltage = IPA_VOLTAGE_SVS;
2381
2382 ret = ipa_rm_create_resource(&create_params);
2383 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302384 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002385 "Create RM resource failed: %d", ret);
2386 goto setup_rm_fail;
2387 }
2388
2389 memset(&create_params, 0, sizeof(create_params));
2390 create_params.name = IPA_RM_RESOURCE_WLAN_CONS;
2391 create_params.request_resource = hdd_ipa_rm_cons_request;
2392 create_params.release_resource = hdd_ipa_rm_cons_release;
2393 create_params.floor_voltage = IPA_VOLTAGE_SVS;
2394
2395 ret = ipa_rm_create_resource(&create_params);
2396 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302397 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002398 "Create RM CONS resource failed: %d", ret);
2399 goto delete_prod;
2400 }
2401
2402 ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD,
2403 IPA_RM_RESOURCE_APPS_CONS);
2404
2405 ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_WLAN_PROD,
2406 HDD_IPA_RX_INACTIVITY_MSEC_DELAY);
2407 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302408 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Timer init failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002409 ret);
2410 goto timer_init_failed;
2411 }
2412
2413 /* Set the lowest bandwidth to start with */
2414 ret = hdd_ipa_set_perf_level(hdd_ipa->hdd_ctx, 0, 0);
2415
2416 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302417 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002418 "Set perf level failed: %d", ret);
2419 goto set_perf_failed;
2420 }
2421
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302422 qdf_wake_lock_create(&hdd_ipa->wake_lock, "wlan_ipa");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002423 INIT_DELAYED_WORK(&hdd_ipa->wake_lock_work,
2424 hdd_ipa_wake_lock_timer_func);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302425 qdf_spinlock_create(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002426 hdd_ipa->rm_state = HDD_IPA_RM_RELEASED;
2427 hdd_ipa->wake_lock_released = true;
2428 atomic_set(&hdd_ipa->tx_ref_cnt, 0);
2429
2430 return ret;
2431
2432set_perf_failed:
2433 ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD);
2434
2435timer_init_failed:
2436 ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
2437
2438delete_prod:
2439 ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
2440
2441setup_rm_fail:
2442 return ret;
2443}
2444
2445/**
2446 * hdd_ipa_destroy_rm_resource() - Destroy IPA resources
2447 * @hdd_ipa: Global HDD IPA context
2448 *
2449 * Destroys all resources associated with the IPA resource manager
2450 *
2451 * Return: None
2452 */
2453static void hdd_ipa_destroy_rm_resource(struct hdd_ipa_priv *hdd_ipa)
2454{
2455 int ret;
2456
2457 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2458 return;
2459
2460 cancel_delayed_work_sync(&hdd_ipa->wake_lock_work);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302461 qdf_wake_lock_destroy(&hdd_ipa->wake_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002462
2463#ifdef WLAN_OPEN_SOURCE
2464 cancel_work_sync(&hdd_ipa->uc_rm_work.work);
2465#endif
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302466 qdf_spinlock_destroy(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002467
2468 ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD);
2469
2470 ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
2471 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302472 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002473 "RM PROD resource delete failed %d", ret);
2474
2475 ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
2476 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302477 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002478 "RM CONS resource delete failed %d", ret);
2479}
2480
2481/**
2482 * hdd_ipa_send_skb_to_network() - Send skb to kernel
2483 * @skb: network buffer
2484 * @adapter: network adapter
2485 *
2486 * Called when a network buffer is received which should not be routed
2487 * to the IPA module.
2488 *
2489 * Return: None
2490 */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302491static void hdd_ipa_send_skb_to_network(qdf_nbuf_t skb,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002492 hdd_adapter_t *adapter)
2493{
2494 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
2495 unsigned int cpu_index;
2496
2497 if (!adapter || adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302498 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_LOW, "Invalid adapter: 0x%p",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002499 adapter);
2500 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302501 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002502 return;
2503 }
2504
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002505 if (cds_is_driver_unloading()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002506 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302507 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002508 return;
2509 }
2510
2511 skb->destructor = hdd_ipa_uc_rt_debug_destructor;
2512 skb->dev = adapter->dev;
2513 skb->protocol = eth_type_trans(skb, skb->dev);
2514 skb->ip_summed = CHECKSUM_NONE;
2515
2516 cpu_index = wlan_hdd_get_cpu();
2517
2518 ++adapter->hdd_stats.hddTxRxStats.rxPackets[cpu_index];
2519 if (netif_rx_ni(skb) == NET_RX_SUCCESS)
2520 ++adapter->hdd_stats.hddTxRxStats.rxDelivered[cpu_index];
2521 else
2522 ++adapter->hdd_stats.hddTxRxStats.rxRefused[cpu_index];
2523
2524 HDD_IPA_INCREASE_NET_SEND_COUNT(hdd_ipa);
2525 adapter->dev->last_rx = jiffies;
2526}
2527
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002528/**
2529 * hdd_ipa_w2i_cb() - WLAN to IPA callback handler
2530 * @priv: pointer to private data registered with IPA (we register a
2531 * pointer to the global IPA context)
2532 * @evt: the IPA event which triggered the callback
2533 * @data: data associated with the event
2534 *
2535 * Return: None
2536 */
2537static void hdd_ipa_w2i_cb(void *priv, enum ipa_dp_evt_type evt,
2538 unsigned long data)
2539{
2540 struct hdd_ipa_priv *hdd_ipa = NULL;
2541 hdd_adapter_t *adapter = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302542 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002543 uint8_t iface_id;
2544 uint8_t session_id;
2545 struct hdd_ipa_iface_context *iface_context;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302546 qdf_nbuf_t copy;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002547 uint8_t fw_desc;
2548 int ret;
2549
2550 hdd_ipa = (struct hdd_ipa_priv *)priv;
2551
2552 switch (evt) {
2553 case IPA_RECEIVE:
Nirav Shahcbc6d722016-03-01 16:24:53 +05302554 skb = (qdf_nbuf_t) data;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002555 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2556 session_id = (uint8_t)skb->cb[0];
2557 iface_id = vdev_to_iface[session_id];
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302558 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002559 "IPA_RECEIVE: session_id=%u, iface_id=%u",
2560 session_id, iface_id);
2561 } else {
2562 iface_id = HDD_IPA_GET_IFACE_ID(skb->data);
2563 }
2564
2565 if (iface_id >= HDD_IPA_MAX_IFACE) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302566 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002567 "IPA_RECEIVE: Invalid iface_id: %u",
2568 iface_id);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302569 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002570 "w2i -- skb", skb->data, 8);
2571 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302572 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002573 return;
2574 }
2575
2576 iface_context = &hdd_ipa->iface_context[iface_id];
2577 adapter = iface_context->adapter;
2578
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302579 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002580 "w2i -- skb", skb->data, 8);
2581 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2582 hdd_ipa->stats.num_rx_excep++;
2583 skb_pull(skb, HDD_IPA_UC_WLAN_CLD_HDR_LEN);
2584 } else {
2585 skb_pull(skb, HDD_IPA_WLAN_CLD_HDR_LEN);
2586 }
2587
2588 iface_context->stats.num_rx_ipa_excep++;
2589
2590 /* Disable to forward Intra-BSS Rx packets when
2591 * ap_isolate=1 in hostapd.conf
2592 */
2593 if (adapter->sessionCtx.ap.apDisableIntraBssFwd) {
2594 /*
2595 * When INTRA_BSS_FWD_OFFLOAD is enabled, FW will send
2596 * all Rx packets to IPA uC, which need to be forwarded
2597 * to other interface.
2598 * And, IPA driver will send back to WLAN host driver
2599 * through exception pipe with fw_desc field set by FW.
2600 * Here we are checking fw_desc field for FORWARD bit
2601 * set, and forward to Tx. Then copy to kernel stack
2602 * only when DISCARD bit is not set.
2603 */
2604 fw_desc = (uint8_t)skb->cb[1];
2605
Leo Chang3bc8fed2015-11-13 10:59:47 -08002606 if (fw_desc & HDD_IPA_FW_RX_DESC_FORWARD_M) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002607 HDD_IPA_LOG(
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302608 QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002609 "Forward packet to Tx (fw_desc=%d)",
2610 fw_desc);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302611 copy = qdf_nbuf_copy(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002612 if (copy) {
2613 hdd_ipa->ipa_tx_forward++;
2614 ret = hdd_softap_hard_start_xmit(
2615 (struct sk_buff *)copy,
2616 adapter->dev);
2617 if (ret) {
2618 HDD_IPA_LOG(
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302619 QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002620 "Forward packet tx fail");
2621 hdd_ipa->stats.
2622 num_tx_bcmc_err++;
2623 } else {
2624 hdd_ipa->stats.num_tx_bcmc++;
2625 }
2626 }
2627 }
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002628
Leo Chang3bc8fed2015-11-13 10:59:47 -08002629 if (fw_desc & HDD_IPA_FW_RX_DESC_DISCARD_M) {
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002630 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
2631 hdd_ipa->ipa_rx_discard++;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302632 qdf_nbuf_free(skb);
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002633 break;
2634 }
2635
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002636 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302637 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002638 "Intra-BSS FWD is disabled-skip forward to Tx");
2639 }
2640
2641 hdd_ipa_send_skb_to_network(skb, adapter);
2642 break;
2643
2644 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302645 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002646 "w2i cb wrong event: 0x%x", evt);
2647 return;
2648 }
2649}
2650
2651/**
2652 * hdd_ipa_nbuf_cb() - IPA TX complete callback
2653 * @skb: packet buffer which was transmitted
2654 *
2655 * Return: None
2656 */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302657void hdd_ipa_nbuf_cb(qdf_nbuf_t skb)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002658{
2659 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
2660
Nirav Shahcbc6d722016-03-01 16:24:53 +05302661 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "%p",
2662 wlan_hdd_stub_priv_to_addr(QDF_NBUF_CB_TX_IPA_PRIV(skb)));
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002663 /* FIXME: This is broken; PRIV_DATA is now 31 bits */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302664 ipa_free_skb((struct ipa_rx_data *)
2665 wlan_hdd_stub_priv_to_addr(QDF_NBUF_CB_TX_IPA_PRIV(skb)));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002666
2667 hdd_ipa->stats.num_tx_comp_cnt++;
2668
2669 atomic_dec(&hdd_ipa->tx_ref_cnt);
2670
2671 hdd_ipa_rm_try_release(hdd_ipa);
2672}
2673
2674/**
2675 * hdd_ipa_send_pkt_to_tl() - Send an IPA packet to TL
2676 * @iface_context: interface-specific IPA context
2677 * @ipa_tx_desc: packet data descriptor
2678 *
2679 * Return: None
2680 */
2681static void hdd_ipa_send_pkt_to_tl(
2682 struct hdd_ipa_iface_context *iface_context,
2683 struct ipa_rx_data *ipa_tx_desc)
2684{
2685 struct hdd_ipa_priv *hdd_ipa = iface_context->hdd_ipa;
2686 uint8_t interface_id;
2687 hdd_adapter_t *adapter = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302688 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002689
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302690 qdf_spin_lock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002691 adapter = iface_context->adapter;
2692 if (!adapter) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302693 HDD_IPA_LOG(QDF_TRACE_LEVEL_WARN, "Interface Down");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002694 ipa_free_skb(ipa_tx_desc);
2695 iface_context->stats.num_tx_drop++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302696 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002697 hdd_ipa_rm_try_release(hdd_ipa);
2698 return;
2699 }
2700
2701 /*
2702 * During CAC period, data packets shouldn't be sent over the air so
2703 * drop all the packets here
2704 */
2705 if (WLAN_HDD_GET_AP_CTX_PTR(adapter)->dfs_cac_block_tx) {
2706 ipa_free_skb(ipa_tx_desc);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302707 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002708 iface_context->stats.num_tx_cac_drop++;
2709 hdd_ipa_rm_try_release(hdd_ipa);
2710 return;
2711 }
2712
2713 interface_id = adapter->sessionId;
2714 ++adapter->stats.tx_packets;
2715
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302716 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002717
2718 skb = ipa_tx_desc->skb;
2719
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302720 qdf_mem_set(skb->cb, sizeof(skb->cb), 0);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302721 qdf_nbuf_ipa_owned_set(skb);
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002722 /* FIXME: This is broken. No such field in cb any more:
2723 NBUF_CALLBACK_FN(skb) = hdd_ipa_nbuf_cb; */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002724 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
Nirav Shahcbc6d722016-03-01 16:24:53 +05302725 qdf_nbuf_mapped_paddr_set(skb,
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002726 ipa_tx_desc->dma_addr
2727 + HDD_IPA_WLAN_FRAG_HEADER
2728 + HDD_IPA_WLAN_IPA_HEADER);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002729 ipa_tx_desc->skb->len -=
2730 HDD_IPA_WLAN_FRAG_HEADER + HDD_IPA_WLAN_IPA_HEADER;
2731 } else
Nirav Shahcbc6d722016-03-01 16:24:53 +05302732 qdf_nbuf_mapped_paddr_set(skb, ipa_tx_desc->dma_addr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002733
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002734 /* FIXME: This is broken: priv_data is 31 bits */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302735 qdf_nbuf_ipa_priv_set(skb, wlan_hdd_stub_addr_to_priv(ipa_tx_desc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002736
2737 adapter->stats.tx_bytes += ipa_tx_desc->skb->len;
2738
2739 skb = ol_tx_send_ipa_data_frame(iface_context->tl_context,
2740 ipa_tx_desc->skb);
2741 if (skb) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302742 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "TLSHIM tx fail");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002743 ipa_free_skb(ipa_tx_desc);
2744 iface_context->stats.num_tx_err++;
2745 hdd_ipa_rm_try_release(hdd_ipa);
2746 return;
2747 }
2748
2749 atomic_inc(&hdd_ipa->tx_ref_cnt);
2750
2751 iface_context->stats.num_tx++;
2752
2753}
2754
2755/**
2756 * hdd_ipa_pm_send_pkt_to_tl() - Send queued packets to TL
2757 * @work: pointer to the scheduled work
2758 *
2759 * Called during PM resume to send packets to TL which were queued
2760 * while host was in the process of suspending.
2761 *
2762 * Return: None
2763 */
2764static void hdd_ipa_pm_send_pkt_to_tl(struct work_struct *work)
2765{
2766 struct hdd_ipa_priv *hdd_ipa = container_of(work,
2767 struct hdd_ipa_priv,
2768 pm_work);
2769 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302770 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002771 uint32_t dequeued = 0;
2772
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302773 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002774
Nirav Shahcbc6d722016-03-01 16:24:53 +05302775 while (((skb = qdf_nbuf_queue_remove(&hdd_ipa->pm_queue_head))
2776 != NULL)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302777 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002778
2779 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
2780
2781 dequeued++;
2782
2783 hdd_ipa_send_pkt_to_tl(pm_tx_cb->iface_context,
2784 pm_tx_cb->ipa_tx_desc);
2785
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302786 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002787 }
2788
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302789 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002790
2791 hdd_ipa->stats.num_tx_dequeued += dequeued;
2792 if (dequeued > hdd_ipa->stats.num_max_pm_queue)
2793 hdd_ipa->stats.num_max_pm_queue = dequeued;
2794}
2795
2796/**
2797 * hdd_ipa_i2w_cb() - IPA to WLAN callback
2798 * @priv: pointer to private data registered with IPA (we register a
2799 * pointer to the interface-specific IPA context)
2800 * @evt: the IPA event which triggered the callback
2801 * @data: data associated with the event
2802 *
2803 * Return: None
2804 */
2805static void hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt,
2806 unsigned long data)
2807{
2808 struct hdd_ipa_priv *hdd_ipa = NULL;
2809 struct ipa_rx_data *ipa_tx_desc;
2810 struct hdd_ipa_iface_context *iface_context;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302811 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002812 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302813 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002814
Mukul Sharma81661ae2015-10-30 20:26:02 +05302815 iface_context = (struct hdd_ipa_iface_context *)priv;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002816 if (evt != IPA_RECEIVE) {
Nirav Shahcbc6d722016-03-01 16:24:53 +05302817 skb = (qdf_nbuf_t) data;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002818 dev_kfree_skb_any(skb);
2819 iface_context->stats.num_tx_drop++;
2820 return;
2821 }
2822
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002823 ipa_tx_desc = (struct ipa_rx_data *)data;
2824
2825 hdd_ipa = iface_context->hdd_ipa;
2826
2827 /*
2828 * When SSR is going on or driver is unloading, just drop the packets.
2829 * During SSR, there is no use in queueing the packets as STA has to
2830 * connect back any way
2831 */
2832 status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05302833 if (status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002834 ipa_free_skb(ipa_tx_desc);
2835 iface_context->stats.num_tx_drop++;
2836 return;
2837 }
2838
2839 skb = ipa_tx_desc->skb;
2840
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302841 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_DEBUG, "i2w", skb->data, 8);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002842
2843 /*
2844 * If PROD resource is not requested here then there may be cases where
2845 * IPA hardware may be clocked down because of not having proper
2846 * dependency graph between WLAN CONS and modem PROD pipes. Adding the
2847 * workaround to request PROD resource while data is going over CONS
2848 * pipe to prevent the IPA hardware clockdown.
2849 */
2850 hdd_ipa_rm_request(hdd_ipa);
2851
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302852 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002853 /*
2854 * If host is still suspended then queue the packets and these will be
2855 * drained later when resume completes. When packet is arrived here and
2856 * host is suspended, this means that there is already resume is in
2857 * progress.
2858 */
2859 if (hdd_ipa->suspended) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302860 qdf_mem_set(skb->cb, sizeof(skb->cb), 0);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002861 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
2862 pm_tx_cb->iface_context = iface_context;
2863 pm_tx_cb->ipa_tx_desc = ipa_tx_desc;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302864 qdf_nbuf_queue_add(&hdd_ipa->pm_queue_head, skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002865 hdd_ipa->stats.num_tx_queued++;
2866
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302867 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002868 return;
2869 }
2870
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302871 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002872
2873 /*
2874 * If we are here means, host is not suspended, wait for the work queue
2875 * to finish.
2876 */
2877#ifdef WLAN_OPEN_SOURCE
2878 flush_work(&hdd_ipa->pm_work);
2879#endif
2880
2881 return hdd_ipa_send_pkt_to_tl(iface_context, ipa_tx_desc);
2882}
2883
2884/**
2885 * hdd_ipa_suspend() - Suspend IPA
2886 * @hdd_ctx: Global HDD context
2887 *
2888 * Return: 0 on success, negativer errno on error
2889 */
2890int hdd_ipa_suspend(hdd_context_t *hdd_ctx)
2891{
2892 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2893
2894 if (!hdd_ipa_is_enabled(hdd_ctx))
2895 return 0;
2896
2897 /*
2898 * Check if IPA is ready for suspend, If we are here means, there is
2899 * high chance that suspend would go through but just to avoid any race
2900 * condition after suspend started, these checks are conducted before
2901 * allowing to suspend.
2902 */
2903 if (atomic_read(&hdd_ipa->tx_ref_cnt))
2904 return -EAGAIN;
2905
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302906 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002907
2908 if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302909 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002910 return -EAGAIN;
2911 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302912 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002913
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302914 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002915 hdd_ipa->suspended = true;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302916 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002917
2918 return 0;
2919}
2920
2921/**
2922 * hdd_ipa_resume() - Resume IPA following suspend
2923 * hdd_ctx: Global HDD context
2924 *
2925 * Return: 0 on success, negative errno on error
2926 */
2927int hdd_ipa_resume(hdd_context_t *hdd_ctx)
2928{
2929 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2930
2931 if (!hdd_ipa_is_enabled(hdd_ctx))
2932 return 0;
2933
2934 schedule_work(&hdd_ipa->pm_work);
2935
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302936 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002937 hdd_ipa->suspended = false;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302938 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002939
2940 return 0;
2941}
2942
2943/**
2944 * hdd_ipa_setup_sys_pipe() - Setup all IPA Sys pipes
2945 * @hdd_ipa: Global HDD IPA context
2946 *
2947 * Return: 0 on success, negative errno on error
2948 */
2949static int hdd_ipa_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa)
2950{
2951 int i, ret = 0;
2952 struct ipa_sys_connect_params *ipa;
2953 uint32_t desc_fifo_sz;
2954
2955 /* The maximum number of descriptors that can be provided to a BAM at
2956 * once is one less than the total number of descriptors that the buffer
2957 * can contain.
2958 * If max_num_of_descriptors = (BAM_PIPE_DESCRIPTOR_FIFO_SIZE / sizeof
2959 * (SPS_DESCRIPTOR)), then (max_num_of_descriptors - 1) descriptors can
2960 * be provided at once.
2961 * Because of above requirement, one extra descriptor will be added to
2962 * make sure hardware always has one descriptor.
2963 */
2964 desc_fifo_sz = hdd_ipa->hdd_ctx->config->IpaDescSize
2965 + sizeof(struct sps_iovec);
2966
2967 /*setup TX pipes */
2968 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
2969 ipa = &hdd_ipa->sys_pipe[i].ipa_sys_params;
2970
2971 ipa->client = hdd_ipa_adapter_2_client[i].cons_client;
2972 ipa->desc_fifo_sz = desc_fifo_sz;
2973 ipa->priv = &hdd_ipa->iface_context[i];
2974 ipa->notify = hdd_ipa_i2w_cb;
2975
2976 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
2977 ipa->ipa_ep_cfg.hdr.hdr_len =
2978 HDD_IPA_UC_WLAN_TX_HDR_LEN;
2979 ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
2980 ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
2981 ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
2982 ipa->ipa_ep_cfg.hdr.hdr_additional_const_len =
2983 HDD_IPA_UC_WLAN_8023_HDR_SIZE;
2984 ipa->ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
2985 } else {
2986 ipa->ipa_ep_cfg.hdr.hdr_len = HDD_IPA_WLAN_TX_HDR_LEN;
2987 }
2988 ipa->ipa_ep_cfg.mode.mode = IPA_BASIC;
2989
2990 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2991 ipa->keep_ipa_awake = 1;
2992
2993 ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl));
2994 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302995 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed for pipe %d"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002996 " ret: %d", i, ret);
2997 goto setup_sys_pipe_fail;
2998 }
2999 hdd_ipa->sys_pipe[i].conn_hdl_valid = 1;
3000 }
3001
3002 if (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
3003 /*
3004 * Hard code it here, this can be extended if in case
3005 * PROD pipe is also per interface.
3006 * Right now there is no advantage of doing this.
3007 */
3008 hdd_ipa->prod_client = IPA_CLIENT_WLAN1_PROD;
3009
3010 ipa = &hdd_ipa->sys_pipe[HDD_IPA_RX_PIPE].ipa_sys_params;
3011
3012 ipa->client = hdd_ipa->prod_client;
3013
3014 ipa->desc_fifo_sz = desc_fifo_sz;
3015 ipa->priv = hdd_ipa;
3016 ipa->notify = hdd_ipa_w2i_cb;
3017
3018 ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
3019 ipa->ipa_ep_cfg.hdr.hdr_len = HDD_IPA_WLAN_RX_HDR_LEN;
3020 ipa->ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
3021 ipa->ipa_ep_cfg.mode.mode = IPA_BASIC;
3022
3023 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
3024 ipa->keep_ipa_awake = 1;
3025
3026 ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl));
3027 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303028 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003029 "Failed for RX pipe: %d", ret);
3030 goto setup_sys_pipe_fail;
3031 }
3032 hdd_ipa->sys_pipe[HDD_IPA_RX_PIPE].conn_hdl_valid = 1;
3033 }
3034
3035 return ret;
3036
3037setup_sys_pipe_fail:
3038
3039 while (--i >= 0) {
3040 ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].conn_hdl);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303041 qdf_mem_zero(&hdd_ipa->sys_pipe[i],
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003042 sizeof(struct hdd_ipa_sys_pipe));
3043 }
3044
3045 return ret;
3046}
3047
3048/**
3049 * hdd_ipa_teardown_sys_pipe() - Tear down all IPA Sys pipes
3050 * @hdd_ipa: Global HDD IPA context
3051 *
3052 * Return: None
3053 */
3054static void hdd_ipa_teardown_sys_pipe(struct hdd_ipa_priv *hdd_ipa)
3055{
3056 int ret = 0, i;
3057 for (i = 0; i < HDD_IPA_MAX_SYSBAM_PIPE; i++) {
3058 if (hdd_ipa->sys_pipe[i].conn_hdl_valid) {
3059 ret =
3060 ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].
3061 conn_hdl);
3062 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303063 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003064 ret);
3065
3066 hdd_ipa->sys_pipe[i].conn_hdl_valid = 0;
3067 }
3068 }
3069}
3070
3071/**
3072 * hdd_ipa_register_interface() - register IPA interface
3073 * @hdd_ipa: Global IPA context
3074 * @iface_context: Per-interface IPA context
3075 *
3076 * Return: 0 on success, negative errno on error
3077 */
3078static int hdd_ipa_register_interface(struct hdd_ipa_priv *hdd_ipa,
3079 struct hdd_ipa_iface_context
3080 *iface_context)
3081{
3082 struct ipa_tx_intf tx_intf;
3083 struct ipa_rx_intf rx_intf;
3084 struct ipa_ioc_tx_intf_prop *tx_prop = NULL;
3085 struct ipa_ioc_rx_intf_prop *rx_prop = NULL;
3086 char *ifname = iface_context->adapter->dev->name;
3087
3088 char ipv4_hdr_name[IPA_RESOURCE_NAME_MAX];
3089 char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX];
3090
3091 int num_prop = 1;
3092 int ret = 0;
3093
3094 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx))
3095 num_prop++;
3096
3097 /* Allocate TX properties for TOS categories, 1 each for IPv4 & IPv6 */
3098 tx_prop =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303099 qdf_mem_malloc(sizeof(struct ipa_ioc_tx_intf_prop) * num_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003100 if (!tx_prop) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303101 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "tx_prop allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003102 goto register_interface_fail;
3103 }
3104
3105 /* Allocate RX properties, 1 each for IPv4 & IPv6 */
3106 rx_prop =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303107 qdf_mem_malloc(sizeof(struct ipa_ioc_rx_intf_prop) * num_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003108 if (!rx_prop) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303109 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "rx_prop allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003110 goto register_interface_fail;
3111 }
3112
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303113 qdf_mem_zero(&tx_intf, sizeof(tx_intf));
3114 qdf_mem_zero(&rx_intf, sizeof(rx_intf));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003115
3116 snprintf(ipv4_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
3117 ifname, HDD_IPA_IPV4_NAME_EXT);
3118 snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
3119 ifname, HDD_IPA_IPV6_NAME_EXT);
3120
3121 rx_prop[IPA_IP_v4].ip = IPA_IP_v4;
3122 rx_prop[IPA_IP_v4].src_pipe = iface_context->prod_client;
3123 rx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3124 rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
3125
3126 /*
3127 * Interface ID is 3rd byte in the CLD header. Add the meta data and
3128 * mask to identify the interface in IPA hardware
3129 */
3130 rx_prop[IPA_IP_v4].attrib.meta_data =
3131 htonl(iface_context->adapter->sessionId << 16);
3132 rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
3133
3134 rx_intf.num_props++;
3135 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3136 rx_prop[IPA_IP_v6].ip = IPA_IP_v6;
3137 rx_prop[IPA_IP_v6].src_pipe = iface_context->prod_client;
3138 rx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3139 rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
3140 rx_prop[IPA_IP_v4].attrib.meta_data =
3141 htonl(iface_context->adapter->sessionId << 16);
3142 rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
3143
3144 rx_intf.num_props++;
3145 }
3146
3147 tx_prop[IPA_IP_v4].ip = IPA_IP_v4;
3148 tx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3149 tx_prop[IPA_IP_v4].dst_pipe = IPA_CLIENT_WLAN1_CONS;
3150 tx_prop[IPA_IP_v4].alt_dst_pipe = iface_context->cons_client;
3151 strlcpy(tx_prop[IPA_IP_v4].hdr_name, ipv4_hdr_name,
3152 IPA_RESOURCE_NAME_MAX);
3153 tx_intf.num_props++;
3154
3155 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3156 tx_prop[IPA_IP_v6].ip = IPA_IP_v6;
3157 tx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3158 tx_prop[IPA_IP_v6].dst_pipe = IPA_CLIENT_WLAN1_CONS;
3159 tx_prop[IPA_IP_v6].alt_dst_pipe = iface_context->cons_client;
3160 strlcpy(tx_prop[IPA_IP_v6].hdr_name, ipv6_hdr_name,
3161 IPA_RESOURCE_NAME_MAX);
3162 tx_intf.num_props++;
3163 }
3164
3165 tx_intf.prop = tx_prop;
3166 rx_intf.prop = rx_prop;
3167
3168 /* Call the ipa api to register interface */
3169 ret = ipa_register_intf(ifname, &tx_intf, &rx_intf);
3170
3171register_interface_fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303172 qdf_mem_free(tx_prop);
3173 qdf_mem_free(rx_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003174 return ret;
3175}
3176
3177/**
3178 * hdd_remove_ipa_header() - Remove a specific header from IPA
3179 * @name: Name of the header to be removed
3180 *
3181 * Return: None
3182 */
3183static void hdd_ipa_remove_header(char *name)
3184{
3185 struct ipa_ioc_get_hdr hdrlookup;
3186 int ret = 0, len;
3187 struct ipa_ioc_del_hdr *ipa_hdr;
3188
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303189 qdf_mem_zero(&hdrlookup, sizeof(hdrlookup));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003190 strlcpy(hdrlookup.name, name, sizeof(hdrlookup.name));
3191 ret = ipa_get_hdr(&hdrlookup);
3192 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303193 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Hdr deleted already %s, %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003194 name, ret);
3195 return;
3196 }
3197
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303198 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "hdl: 0x%x", hdrlookup.hdl);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003199 len = sizeof(struct ipa_ioc_del_hdr) + sizeof(struct ipa_hdr_del) * 1;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303200 ipa_hdr = (struct ipa_ioc_del_hdr *)qdf_mem_malloc(len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003201 if (ipa_hdr == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303202 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "ipa_hdr allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003203 return;
3204 }
3205 ipa_hdr->num_hdls = 1;
3206 ipa_hdr->commit = 0;
3207 ipa_hdr->hdl[0].hdl = hdrlookup.hdl;
3208 ipa_hdr->hdl[0].status = -1;
3209 ret = ipa_del_hdr(ipa_hdr);
3210 if (ret != 0)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303211 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Delete header failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003212 ret);
3213
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303214 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003215}
3216
3217/**
3218 * hdd_ipa_add_header_info() - Add IPA header for a given interface
3219 * @hdd_ipa: Global HDD IPA context
3220 * @iface_context: Interface-specific HDD IPA context
3221 * @mac_addr: Interface MAC address
3222 *
3223 * Return: 0 on success, negativer errno value on error
3224 */
3225static int hdd_ipa_add_header_info(struct hdd_ipa_priv *hdd_ipa,
3226 struct hdd_ipa_iface_context *iface_context,
3227 uint8_t *mac_addr)
3228{
3229 hdd_adapter_t *adapter = iface_context->adapter;
3230 char *ifname;
3231 struct ipa_ioc_add_hdr *ipa_hdr = NULL;
3232 int ret = -EINVAL;
3233 struct hdd_ipa_tx_hdr *tx_hdr = NULL;
3234 struct hdd_ipa_uc_tx_hdr *uc_tx_hdr = NULL;
3235
3236 ifname = adapter->dev->name;
3237
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303238 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Add Partial hdr: %s, %pM",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003239 ifname, mac_addr);
3240
3241 /* dynamically allocate the memory to add the hdrs */
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303242 ipa_hdr = qdf_mem_malloc(sizeof(struct ipa_ioc_add_hdr)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003243 + sizeof(struct ipa_hdr_add));
3244 if (!ipa_hdr) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303245 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003246 "%s: ipa_hdr allocation failed", ifname);
3247 ret = -ENOMEM;
3248 goto end;
3249 }
3250
3251 ipa_hdr->commit = 0;
3252 ipa_hdr->num_hdrs = 1;
3253
3254 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3255 uc_tx_hdr = (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
3256 memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN);
3257 memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
3258 uc_tx_hdr->ipa_hd.vdev_id = iface_context->adapter->sessionId;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303259 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003260 "ifname=%s, vdev_id=%d",
3261 ifname, uc_tx_hdr->ipa_hd.vdev_id);
3262 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3263 ifname, HDD_IPA_IPV4_NAME_EXT);
3264 ipa_hdr->hdr[0].hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN;
3265 ipa_hdr->hdr[0].type = IPA_HDR_L2_ETHERNET_II;
3266 ipa_hdr->hdr[0].is_partial = 1;
3267 ipa_hdr->hdr[0].hdr_hdl = 0;
3268 ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
3269 ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
3270
3271 ret = ipa_add_hdr(ipa_hdr);
3272 } else {
3273 tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr;
3274
3275 /* Set the Source MAC */
3276 memcpy(tx_hdr, &ipa_tx_hdr, HDD_IPA_WLAN_TX_HDR_LEN);
3277 memcpy(tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
3278
3279 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3280 ifname, HDD_IPA_IPV4_NAME_EXT);
3281 ipa_hdr->hdr[0].hdr_len = HDD_IPA_WLAN_TX_HDR_LEN;
3282 ipa_hdr->hdr[0].is_partial = 1;
3283 ipa_hdr->hdr[0].hdr_hdl = 0;
3284 ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
3285 ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_WLAN_HDR_DES_MAC_OFFSET;
3286
3287 /* Set the type to IPV4 in the header */
3288 tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IP);
3289
3290 ret = ipa_add_hdr(ipa_hdr);
3291 }
3292 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303293 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s IPv4 add hdr failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003294 ifname, ret);
3295 goto end;
3296 }
3297
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303298 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: IPv4 hdr_hdl: 0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003299 ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
3300
3301 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3302 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3303 ifname, HDD_IPA_IPV6_NAME_EXT);
3304
3305 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3306 uc_tx_hdr =
3307 (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
3308 uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6);
3309 } else {
3310 /* Set the type to IPV6 in the header */
3311 tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr;
3312 tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IPV6);
3313 }
3314
3315 ret = ipa_add_hdr(ipa_hdr);
3316 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303317 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003318 "%s: IPv6 add hdr failed: %d", ifname, ret);
3319 goto clean_ipv4_hdr;
3320 }
3321
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303322 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: IPv6 hdr_hdl: 0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003323 ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
3324 }
3325
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303326 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003327
3328 return ret;
3329
3330clean_ipv4_hdr:
3331 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3332 ifname, HDD_IPA_IPV4_NAME_EXT);
3333 hdd_ipa_remove_header(ipa_hdr->hdr[0].name);
3334end:
3335 if (ipa_hdr)
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303336 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003337
3338 return ret;
3339}
3340
3341/**
3342 * hdd_ipa_clean_hdr() - Cleanup IPA on a given adapter
3343 * @adapter: Adapter upon which IPA was previously configured
3344 *
3345 * Return: None
3346 */
3347static void hdd_ipa_clean_hdr(hdd_adapter_t *adapter)
3348{
3349 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
3350 int ret;
3351 char name_ipa[IPA_RESOURCE_NAME_MAX];
3352
3353 /* Remove the headers */
3354 snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
3355 adapter->dev->name, HDD_IPA_IPV4_NAME_EXT);
3356 hdd_ipa_remove_header(name_ipa);
3357
3358 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3359 snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
3360 adapter->dev->name, HDD_IPA_IPV6_NAME_EXT);
3361 hdd_ipa_remove_header(name_ipa);
3362 }
3363 /* unregister the interface with IPA */
3364 ret = ipa_deregister_intf(adapter->dev->name);
3365 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303366 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003367 "%s: ipa_deregister_intf fail: %d",
3368 adapter->dev->name, ret);
3369}
3370
3371/**
3372 * hdd_ipa_cleanup_iface() - Cleanup IPA on a given interface
3373 * @iface_context: interface-specific IPA context
3374 *
3375 * Return: None
3376 */
3377static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context)
3378{
3379 if (iface_context == NULL)
3380 return;
3381
3382 hdd_ipa_clean_hdr(iface_context->adapter);
3383
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303384 qdf_spin_lock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003385 iface_context->adapter->ipa_context = NULL;
3386 iface_context->adapter = NULL;
3387 iface_context->tl_context = NULL;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303388 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003389 iface_context->ifa_address = 0;
3390 if (!iface_context->hdd_ipa->num_iface) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303391 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003392 "NUM INTF 0, Invalid");
Anurag Chouhandf2b2682016-02-29 14:15:27 +05303393 QDF_ASSERT(0);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003394 }
3395 iface_context->hdd_ipa->num_iface--;
3396}
3397
3398/**
3399 * hdd_ipa_setup_iface() - Setup IPA on a given interface
3400 * @hdd_ipa: HDD IPA global context
3401 * @adapter: Interface upon which IPA is being setup
3402 * @sta_id: Station ID of the API instance
3403 *
3404 * Return: 0 on success, negative errno value on error
3405 */
3406static int hdd_ipa_setup_iface(struct hdd_ipa_priv *hdd_ipa,
3407 hdd_adapter_t *adapter, uint8_t sta_id)
3408{
3409 struct hdd_ipa_iface_context *iface_context = NULL;
3410 void *tl_context = NULL;
3411 int i, ret = 0;
3412
3413 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
3414 * channel change indication. Since these indications are sent by lower
3415 * layer as SAP updates and IPA doesn't have to do anything for these
3416 * updates so ignoring!
3417 */
Krunal Sonibe766b02016-03-10 13:00:44 -08003418 if (QDF_SAP_MODE == adapter->device_mode && adapter->ipa_context)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003419 return 0;
3420
3421 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
3422 if (hdd_ipa->iface_context[i].adapter == NULL) {
3423 iface_context = &(hdd_ipa->iface_context[i]);
3424 break;
3425 }
3426 }
3427
3428 if (iface_context == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303429 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003430 "All the IPA interfaces are in use");
3431 ret = -ENOMEM;
3432 goto end;
3433 }
3434
3435 adapter->ipa_context = iface_context;
3436 iface_context->adapter = adapter;
3437 iface_context->sta_id = sta_id;
3438 tl_context = ol_txrx_get_vdev_by_sta_id(sta_id);
3439
3440 if (tl_context == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303441 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003442 "Not able to get TL context sta_id: %d", sta_id);
3443 ret = -EINVAL;
3444 goto end;
3445 }
3446
3447 iface_context->tl_context = tl_context;
3448
3449 ret = hdd_ipa_add_header_info(hdd_ipa, iface_context,
3450 adapter->dev->dev_addr);
3451
3452 if (ret)
3453 goto end;
3454
3455 /* Configure the TX and RX pipes filter rules */
3456 ret = hdd_ipa_register_interface(hdd_ipa, iface_context);
3457 if (ret)
3458 goto cleanup_header;
3459
3460 hdd_ipa->num_iface++;
3461 return ret;
3462
3463cleanup_header:
3464
3465 hdd_ipa_clean_hdr(adapter);
3466end:
3467 if (iface_context)
3468 hdd_ipa_cleanup_iface(iface_context);
3469 return ret;
3470}
3471
3472/**
3473 * hdd_ipa_msg_free_fn() - Free an IPA message
3474 * @buff: pointer to the IPA message
3475 * @len: length of the IPA message
3476 * @type: type of IPA message
3477 *
3478 * Return: None
3479 */
3480static void hdd_ipa_msg_free_fn(void *buff, uint32_t len, uint32_t type)
3481{
3482 hddLog(LOG1, "msg type:%d, len:%d", type, len);
3483 ghdd_ipa->stats.num_free_msg++;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303484 qdf_mem_free(buff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003485}
3486
3487/**
3488 * hdd_ipa_send_mcc_scc_msg() - send IPA WLAN_SWITCH_TO_MCC/SCC message
3489 * @mcc_mode: 0=MCC/1=SCC
3490 *
3491 * Return: 0 on success, negative errno value on error
3492 */
3493int hdd_ipa_send_mcc_scc_msg(hdd_context_t *pHddCtx, bool mcc_mode)
3494{
3495 hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303496 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003497 hdd_adapter_t *pAdapter;
3498 struct ipa_msg_meta meta;
3499 struct ipa_wlan_msg *msg;
3500 int ret;
3501
3502 if (!hdd_ipa_uc_sta_is_enabled(pHddCtx))
3503 return -EINVAL;
3504
3505 if (!pHddCtx->mcc_mode) {
3506 /* Flush TxRx queue for each adapter before switch to SCC */
3507 status = hdd_get_front_adapter(pHddCtx, &adapter_node);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303508 while (NULL != adapter_node && QDF_STATUS_SUCCESS == status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003509 pAdapter = adapter_node->pAdapter;
Krunal Sonibe766b02016-03-10 13:00:44 -08003510 if (pAdapter->device_mode == QDF_STA_MODE ||
3511 pAdapter->device_mode == QDF_SAP_MODE) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303512 hddLog(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003513 "MCC->SCC: Flush TxRx queue(d_mode=%d)",
3514 pAdapter->device_mode);
3515 hdd_deinit_tx_rx(pAdapter);
3516 }
3517 status = hdd_get_next_adapter(
3518 pHddCtx, adapter_node, &next);
3519 adapter_node = next;
3520 }
3521 }
3522
3523 /* Send SCC/MCC Switching event to IPA */
3524 meta.msg_len = sizeof(*msg);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303525 msg = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003526 if (msg == NULL) {
3527 hddLog(LOGE, "msg allocation failed");
3528 return -ENOMEM;
3529 }
3530
3531 meta.msg_type = mcc_mode ?
3532 WLAN_SWITCH_TO_MCC : WLAN_SWITCH_TO_SCC;
3533 hddLog(LOG1, "ipa_send_msg(Evt:%d)", meta.msg_type);
3534
3535 ret = ipa_send_msg(&meta, msg, hdd_ipa_msg_free_fn);
3536
3537 if (ret) {
3538 hddLog(LOGE, "ipa_send_msg(Evt:%d) - fail=%d",
3539 meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303540 qdf_mem_free(msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003541 }
3542
3543 return ret;
3544}
3545
3546/**
3547 * hdd_ipa_wlan_event_to_str() - convert IPA WLAN event to string
3548 * @event: IPA WLAN event to be converted to a string
3549 *
3550 * Return: ASCII string representing the IPA WLAN event
3551 */
3552static inline char *hdd_ipa_wlan_event_to_str(enum ipa_wlan_event event)
3553{
3554 switch (event) {
3555 case WLAN_CLIENT_CONNECT:
3556 return "WLAN_CLIENT_CONNECT";
3557 case WLAN_CLIENT_DISCONNECT:
3558 return "WLAN_CLIENT_DISCONNECT";
3559 case WLAN_CLIENT_POWER_SAVE_MODE:
3560 return "WLAN_CLIENT_POWER_SAVE_MODE";
3561 case WLAN_CLIENT_NORMAL_MODE:
3562 return "WLAN_CLIENT_NORMAL_MODE";
3563 case SW_ROUTING_ENABLE:
3564 return "SW_ROUTING_ENABLE";
3565 case SW_ROUTING_DISABLE:
3566 return "SW_ROUTING_DISABLE";
3567 case WLAN_AP_CONNECT:
3568 return "WLAN_AP_CONNECT";
3569 case WLAN_AP_DISCONNECT:
3570 return "WLAN_AP_DISCONNECT";
3571 case WLAN_STA_CONNECT:
3572 return "WLAN_STA_CONNECT";
3573 case WLAN_STA_DISCONNECT:
3574 return "WLAN_STA_DISCONNECT";
3575 case WLAN_CLIENT_CONNECT_EX:
3576 return "WLAN_CLIENT_CONNECT_EX";
3577
3578 case IPA_WLAN_EVENT_MAX:
3579 default:
3580 return "UNKNOWN";
3581 }
3582}
3583
3584/**
3585 * hdd_ipa_wlan_evt() - IPA event handler
3586 * @adapter: adapter upon which the event was received
3587 * @sta_id: station id for the event
3588 * @type: the event type
3589 * @mac_address: MAC address associated with the event
3590 *
3591 * Return: 0 on success, negative errno value on error
3592 */
3593int hdd_ipa_wlan_evt(hdd_adapter_t *adapter, uint8_t sta_id,
3594 enum ipa_wlan_event type, uint8_t *mac_addr)
3595{
3596 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
3597 struct ipa_msg_meta meta;
3598 struct ipa_wlan_msg *msg;
3599 struct ipa_wlan_msg_ex *msg_ex = NULL;
3600 int ret;
3601
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303602 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: %s evt, MAC: %pM sta_id: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003603 adapter->dev->name, hdd_ipa_wlan_event_to_str(type),
3604 mac_addr, sta_id);
3605
3606 if (type >= IPA_WLAN_EVENT_MAX)
3607 return -EINVAL;
3608
3609 if (WARN_ON(is_zero_ether_addr(mac_addr)))
3610 return -EINVAL;
3611
3612 if (!hdd_ipa || !hdd_ipa_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303613 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "IPA OFFLOAD NOT ENABLED");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003614 return -EINVAL;
3615 }
3616
3617 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
3618 !hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
Krunal Sonibe766b02016-03-10 13:00:44 -08003619 (QDF_SAP_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003620 return 0;
3621 }
3622
3623 /*
3624 * During IPA UC resource loading/unloading new events can be issued.
3625 * Store the events separately and handle them later.
3626 */
3627 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
3628 ((hdd_ipa->resource_loading) ||
3629 (hdd_ipa->resource_unloading))) {
Yun Parkf19e07d2015-11-20 11:34:27 -08003630 unsigned int pending_event_count;
3631 struct ipa_uc_pending_event *pending_event = NULL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003632
Yun Parkf19e07d2015-11-20 11:34:27 -08003633 hdd_err("IPA resource %s inprogress",
3634 hdd_ipa->resource_loading ? "load":"unload");
3635
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303636 qdf_mutex_acquire(&hdd_ipa->event_lock);
Yun Parkf19e07d2015-11-20 11:34:27 -08003637
Anurag Chouhanffb21542016-02-17 14:33:03 +05303638 pending_event_count = qdf_list_size(&hdd_ipa->pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08003639 if (pending_event_count >= HDD_IPA_MAX_PENDING_EVENT_COUNT) {
3640 hdd_notice("Reached max pending event count");
Anurag Chouhanffb21542016-02-17 14:33:03 +05303641 qdf_list_remove_front(&hdd_ipa->pending_event,
3642 (qdf_list_node_t **)&pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08003643 } else {
3644 pending_event =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303645 (struct ipa_uc_pending_event *)qdf_mem_malloc(
Yun Parkf19e07d2015-11-20 11:34:27 -08003646 sizeof(struct ipa_uc_pending_event));
3647 }
3648
3649 if (!pending_event) {
3650 hdd_err("Pending event memory alloc fail");
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303651 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003652 return -ENOMEM;
3653 }
Yun Parkf19e07d2015-11-20 11:34:27 -08003654
3655 pending_event->adapter = adapter;
3656 pending_event->sta_id = sta_id;
3657 pending_event->type = type;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303658 qdf_mem_copy(pending_event->mac_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003659 mac_addr,
Anurag Chouhan6d760662016-02-20 16:05:43 +05303660 QDF_MAC_ADDR_SIZE);
Anurag Chouhanffb21542016-02-17 14:33:03 +05303661 qdf_list_insert_back(&hdd_ipa->pending_event,
Yun Parkf19e07d2015-11-20 11:34:27 -08003662 &pending_event->node);
3663
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303664 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003665 return 0;
3666 }
3667
3668 hdd_ipa->stats.event[type]++;
3669
Leo Chang3bc8fed2015-11-13 10:59:47 -08003670 meta.msg_type = type;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003671 switch (type) {
3672 case WLAN_STA_CONNECT:
3673 /* STA already connected and without disconnect, connect again
3674 * This is Roaming scenario
3675 */
3676 if (hdd_ipa->sta_connected)
3677 hdd_ipa_cleanup_iface(adapter->ipa_context);
3678
3679 if ((hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) &&
3680 (!hdd_ipa->sta_connected))
3681 hdd_ipa_uc_offload_enable_disable(adapter,
3682 SIR_STA_RX_DATA_OFFLOAD, 1);
3683
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303684 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003685
3686 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303687 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003688 "%s: Evt: %d, IPA UC OFFLOAD NOT ENABLED",
3689 msg_ex->name, meta.msg_type);
3690 } else if ((!hdd_ipa->sap_num_connected_sta) &&
3691 (!hdd_ipa->sta_connected)) {
3692 /* Enable IPA UC TX PIPE when STA connected */
3693 ret = hdd_ipa_uc_handle_first_con(hdd_ipa);
Yun Park4cab6ee2015-10-27 11:43:40 -07003694 if (ret) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303695 qdf_mutex_release(&hdd_ipa->event_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303696 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003697 "handle 1st con ret %d", ret);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003698 hdd_ipa_uc_offload_enable_disable(adapter,
3699 SIR_STA_RX_DATA_OFFLOAD, 0);
3700 goto end;
3701 }
3702 }
3703 ret = hdd_ipa_setup_iface(hdd_ipa, adapter, sta_id);
3704 if (ret) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303705 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003706 hdd_ipa_uc_offload_enable_disable(adapter,
3707 SIR_STA_RX_DATA_OFFLOAD, 0);
3708 goto end;
3709
3710#ifdef IPA_UC_OFFLOAD
3711 vdev_to_iface[adapter->sessionId] =
3712 ((struct hdd_ipa_iface_context *)
3713 (adapter->ipa_context))->iface_id;
3714#endif /* IPA_UC_OFFLOAD */
3715 }
3716
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303717 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003718
3719 hdd_ipa->sta_connected = 1;
3720 break;
3721
3722 case WLAN_AP_CONNECT:
3723 /* For DFS channel we get two start_bss event (before and after
3724 * CAC). Also when ACS range includes both DFS and non DFS
3725 * channels, we could possibly change channel many times due to
3726 * RADAR detection and chosen channel may not be a DFS channels.
3727 * So dont return error here. Just discard the event.
3728 */
3729 if (adapter->ipa_context)
3730 return 0;
3731
3732 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3733 hdd_ipa_uc_offload_enable_disable(adapter,
3734 SIR_AP_RX_DATA_OFFLOAD, 1);
3735 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303736 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003737 ret = hdd_ipa_setup_iface(hdd_ipa, adapter, sta_id);
3738 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303739 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003740 "%s: Evt: %d, Interface setup failed",
3741 msg_ex->name, meta.msg_type);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303742 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003743 goto end;
3744
3745#ifdef IPA_UC_OFFLOAD
3746 vdev_to_iface[adapter->sessionId] =
3747 ((struct hdd_ipa_iface_context *)
3748 (adapter->ipa_context))->iface_id;
3749#endif /* IPA_UC_OFFLOAD */
3750 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303751 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003752 break;
3753
3754 case WLAN_STA_DISCONNECT:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303755 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003756 hdd_ipa_cleanup_iface(adapter->ipa_context);
3757
3758 if (!hdd_ipa->sta_connected) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303759 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003760 "%s: Evt: %d, STA already disconnected",
3761 msg_ex->name, meta.msg_type);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303762 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003763 return -EINVAL;
3764 }
3765 hdd_ipa->sta_connected = 0;
3766 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303767 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003768 "%s: IPA UC OFFLOAD NOT ENABLED",
3769 msg_ex->name);
3770 } else {
3771 /* Disable IPA UC TX PIPE when STA disconnected */
3772 if ((!hdd_ipa->sap_num_connected_sta) ||
3773 ((!hdd_ipa->num_iface) &&
3774 (HDD_IPA_UC_NUM_WDI_PIPE ==
3775 hdd_ipa->activated_fw_pipe))) {
3776 hdd_ipa_uc_handle_last_discon(hdd_ipa);
3777 }
3778 }
3779
3780 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
3781 hdd_ipa_uc_offload_enable_disable(adapter,
3782 SIR_STA_RX_DATA_OFFLOAD, 0);
3783 vdev_to_iface[adapter->sessionId] = HDD_IPA_MAX_IFACE;
3784 }
3785
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303786 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003787 break;
3788
3789 case WLAN_AP_DISCONNECT:
3790 if (!adapter->ipa_context) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303791 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003792 "%s: Evt: %d, SAP already disconnected",
3793 msg_ex->name, meta.msg_type);
3794 return -EINVAL;
3795 }
3796
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303797 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003798 hdd_ipa_cleanup_iface(adapter->ipa_context);
3799 if ((!hdd_ipa->num_iface) &&
3800 (HDD_IPA_UC_NUM_WDI_PIPE ==
3801 hdd_ipa->activated_fw_pipe)) {
Prashanth Bhatta9e143052015-12-04 11:56:47 -08003802 if (cds_is_driver_unloading()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003803 /*
3804 * We disable WDI pipes directly here since
3805 * IPA_OPCODE_TX/RX_SUSPEND message will not be
3806 * processed when unloading WLAN driver is in
3807 * progress
3808 */
3809 hdd_ipa_uc_disable_pipes(hdd_ipa);
3810 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303811 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003812 "NO INTF left but still pipe clean up");
3813 hdd_ipa_uc_handle_last_discon(hdd_ipa);
3814 }
3815 }
3816
3817 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3818 hdd_ipa_uc_offload_enable_disable(adapter,
3819 SIR_AP_RX_DATA_OFFLOAD, 0);
3820 vdev_to_iface[adapter->sessionId] = HDD_IPA_MAX_IFACE;
3821 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303822 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003823 break;
3824
3825 case WLAN_CLIENT_CONNECT_EX:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303826 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%d %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003827 adapter->dev->ifindex, sta_id);
3828
3829 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303830 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003831 "%s: Evt: %d, IPA UC OFFLOAD NOT ENABLED",
3832 adapter->dev->name, meta.msg_type);
3833 return 0;
3834 }
3835
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303836 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003837 if (hdd_ipa_uc_find_add_assoc_sta(hdd_ipa,
3838 true, sta_id)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303839 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003840 "%s: STA ID %d found, not valid",
3841 adapter->dev->name, sta_id);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303842 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003843 return 0;
3844 }
Yun Park312f71a2015-12-08 10:22:42 -08003845
3846 /* Enable IPA UC Data PIPEs when first STA connected */
3847 if ((0 == hdd_ipa->sap_num_connected_sta) &&
3848 (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) ||
3849 !hdd_ipa->sta_connected)) {
3850 ret = hdd_ipa_uc_handle_first_con(hdd_ipa);
3851 if (ret) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303852 qdf_mutex_release(&hdd_ipa->event_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303853 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park312f71a2015-12-08 10:22:42 -08003854 "%s: handle 1st con ret %d",
3855 adapter->dev->name, ret);
3856 return ret;
3857 }
3858 }
3859
3860 hdd_ipa->sap_num_connected_sta++;
Yun Park312f71a2015-12-08 10:22:42 -08003861
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303862 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003863
3864 meta.msg_type = type;
3865 meta.msg_len = (sizeof(struct ipa_wlan_msg_ex) +
3866 sizeof(struct ipa_wlan_hdr_attrib_val));
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303867 msg_ex = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003868
3869 if (msg_ex == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303870 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003871 "msg_ex allocation failed");
3872 return -ENOMEM;
3873 }
3874 strlcpy(msg_ex->name, adapter->dev->name,
3875 IPA_RESOURCE_NAME_MAX);
3876 msg_ex->num_of_attribs = 1;
3877 msg_ex->attribs[0].attrib_type = WLAN_HDR_ATTRIB_MAC_ADDR;
3878 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3879 msg_ex->attribs[0].offset =
3880 HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
3881 } else {
3882 msg_ex->attribs[0].offset =
3883 HDD_IPA_WLAN_HDR_DES_MAC_OFFSET;
3884 }
3885 memcpy(msg_ex->attribs[0].u.mac_addr, mac_addr,
3886 IPA_MAC_ADDR_SIZE);
3887
3888 ret = ipa_send_msg(&meta, msg_ex, hdd_ipa_msg_free_fn);
3889
3890 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303891 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d : %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003892 msg_ex->name, meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303893 qdf_mem_free(msg_ex);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003894 return ret;
3895 }
3896 hdd_ipa->stats.num_send_msg++;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003897 return ret;
3898
3899 case WLAN_CLIENT_DISCONNECT:
3900 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303901 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003902 "%s: IPA UC OFFLOAD NOT ENABLED",
3903 msg_ex->name);
3904 return 0;
3905 }
3906
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303907 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003908 if (!hdd_ipa_uc_find_add_assoc_sta(hdd_ipa, false, sta_id)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303909 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003910 "%s: STA ID %d NOT found, not valid",
3911 msg_ex->name, sta_id);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303912 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003913 return 0;
3914 }
3915 hdd_ipa->sap_num_connected_sta--;
3916 /* Disable IPA UC TX PIPE when last STA disconnected */
3917 if (!hdd_ipa->sap_num_connected_sta
3918 && (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) ||
3919 !hdd_ipa->sta_connected)
3920 && (false == hdd_ipa->resource_unloading)
3921 && (HDD_IPA_UC_NUM_WDI_PIPE ==
3922 hdd_ipa->activated_fw_pipe))
3923 hdd_ipa_uc_handle_last_discon(hdd_ipa);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303924 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003925 break;
3926
3927 default:
3928 return 0;
3929 }
3930
3931 meta.msg_len = sizeof(struct ipa_wlan_msg);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303932 msg = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003933 if (msg == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303934 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "msg allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003935 return -ENOMEM;
3936 }
3937
3938 meta.msg_type = type;
3939 strlcpy(msg->name, adapter->dev->name, IPA_RESOURCE_NAME_MAX);
3940 memcpy(msg->mac_addr, mac_addr, ETH_ALEN);
3941
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303942 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003943 msg->name, meta.msg_type);
3944
3945 ret = ipa_send_msg(&meta, msg, hdd_ipa_msg_free_fn);
3946
3947 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303948 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d fail:%d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003949 msg->name, meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303950 qdf_mem_free(msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003951 return ret;
3952 }
3953
3954 hdd_ipa->stats.num_send_msg++;
3955
3956end:
3957 return ret;
3958}
3959
3960/**
3961 * hdd_ipa_rm_state_to_str() - Convert IPA RM state to string
3962 * @state: IPA RM state value
3963 *
3964 * Return: ASCII string representing the IPA RM state
3965 */
3966static inline char *hdd_ipa_rm_state_to_str(enum hdd_ipa_rm_state state)
3967{
3968 switch (state) {
3969 case HDD_IPA_RM_RELEASED:
3970 return "RELEASED";
3971 case HDD_IPA_RM_GRANT_PENDING:
3972 return "GRANT_PENDING";
3973 case HDD_IPA_RM_GRANTED:
3974 return "GRANTED";
3975 }
3976
3977 return "UNKNOWN";
3978}
3979
3980/**
3981 * hdd_ipa_init() - IPA initialization function
3982 * @hdd_ctx: HDD global context
3983 *
3984 * Allocate hdd_ipa resources, ipa pipe resource and register
3985 * wlan interface with IPA module.
3986 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303987 * Return: QDF_STATUS enumeration
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003988 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303989QDF_STATUS hdd_ipa_init(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003990{
3991 struct hdd_ipa_priv *hdd_ipa = NULL;
3992 int ret, i;
3993 struct hdd_ipa_iface_context *iface_context = NULL;
Yun Park7f171ab2016-07-29 15:44:22 -07003994 struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003995
3996 if (!hdd_ipa_is_enabled(hdd_ctx))
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303997 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003998
Yun Park7f171ab2016-07-29 15:44:22 -07003999 if (!pdev) {
4000 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, "pdev is NULL");
4001 goto fail_return;
4002 }
4003
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304004 hdd_ipa = qdf_mem_malloc(sizeof(*hdd_ipa));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004005 if (!hdd_ipa) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304006 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, "hdd_ipa allocation failed");
Leo Chang3bc8fed2015-11-13 10:59:47 -08004007 goto fail_return;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004008 }
4009
4010 hdd_ctx->hdd_ipa = hdd_ipa;
4011 ghdd_ipa = hdd_ipa;
4012 hdd_ipa->hdd_ctx = hdd_ctx;
4013 hdd_ipa->num_iface = 0;
Yun Park7f171ab2016-07-29 15:44:22 -07004014 ol_txrx_ipa_uc_get_resource(pdev, &hdd_ipa->ipa_resource);
Dhanashri Atreb08959a2016-03-01 17:28:03 -08004015 if ((0 == hdd_ipa->ipa_resource.ce_sr_base_paddr) ||
4016 (0 == hdd_ipa->ipa_resource.tx_comp_ring_base_paddr) ||
4017 (0 == hdd_ipa->ipa_resource.rx_rdy_ring_base_paddr) ||
4018 (0 == hdd_ipa->ipa_resource.rx2_rdy_ring_base_paddr)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304019 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Leo Chang3bc8fed2015-11-13 10:59:47 -08004020 "IPA UC resource alloc fail");
4021 goto fail_get_resource;
4022 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004023
4024 /* Create the interface context */
4025 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
4026 iface_context = &hdd_ipa->iface_context[i];
4027 iface_context->hdd_ipa = hdd_ipa;
4028 iface_context->cons_client =
4029 hdd_ipa_adapter_2_client[i].cons_client;
4030 iface_context->prod_client =
4031 hdd_ipa_adapter_2_client[i].prod_client;
4032 iface_context->iface_id = i;
4033 iface_context->adapter = NULL;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304034 qdf_spinlock_create(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004035 }
4036
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004037 INIT_WORK(&hdd_ipa->pm_work, hdd_ipa_pm_send_pkt_to_tl);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304038 qdf_spinlock_create(&hdd_ipa->pm_lock);
Nirav Shahcbc6d722016-03-01 16:24:53 +05304039 qdf_nbuf_queue_init(&hdd_ipa->pm_queue_head);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004040
4041 ret = hdd_ipa_setup_rm(hdd_ipa);
4042 if (ret)
4043 goto fail_setup_rm;
4044
4045 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
4046 hdd_ipa_uc_rt_debug_init(hdd_ctx);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304047 qdf_mem_zero(&hdd_ipa->stats, sizeof(hdd_ipa->stats));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004048 hdd_ipa->sap_num_connected_sta = 0;
4049 hdd_ipa->ipa_tx_packets_diff = 0;
4050 hdd_ipa->ipa_rx_packets_diff = 0;
4051 hdd_ipa->ipa_p_tx_packets = 0;
4052 hdd_ipa->ipa_p_rx_packets = 0;
4053 hdd_ipa->resource_loading = false;
4054 hdd_ipa->resource_unloading = false;
4055 hdd_ipa->sta_connected = 0;
Leo Change3e49442015-10-26 20:07:13 -07004056 hdd_ipa->ipa_pipes_down = true;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004057 /* Setup IPA sys_pipe for MCC */
4058 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
4059 ret = hdd_ipa_setup_sys_pipe(hdd_ipa);
4060 if (ret)
4061 goto fail_create_sys_pipe;
4062 }
4063 hdd_ipa_uc_ol_init(hdd_ctx);
4064 } else {
4065 ret = hdd_ipa_setup_sys_pipe(hdd_ipa);
4066 if (ret)
4067 goto fail_create_sys_pipe;
4068 }
4069
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304070 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004071
4072fail_create_sys_pipe:
4073 hdd_ipa_destroy_rm_resource(hdd_ipa);
4074fail_setup_rm:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304075 qdf_spinlock_destroy(&hdd_ipa->pm_lock);
Leo Chang3bc8fed2015-11-13 10:59:47 -08004076fail_get_resource:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304077 qdf_mem_free(hdd_ipa);
Leo Chang3bc8fed2015-11-13 10:59:47 -08004078 hdd_ctx->hdd_ipa = NULL;
4079 ghdd_ipa = NULL;
4080fail_return:
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304081 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004082}
4083
4084/**
Yun Parkf19e07d2015-11-20 11:34:27 -08004085 * hdd_ipa_cleanup_pending_event() - Cleanup IPA pending event list
4086 * @hdd_ipa: pointer to HDD IPA struct
4087 *
4088 * Return: none
4089 */
4090void hdd_ipa_cleanup_pending_event(struct hdd_ipa_priv *hdd_ipa)
4091{
4092 struct ipa_uc_pending_event *pending_event = NULL;
4093
Anurag Chouhanffb21542016-02-17 14:33:03 +05304094 while (qdf_list_remove_front(&hdd_ipa->pending_event,
4095 (qdf_list_node_t **)&pending_event) == QDF_STATUS_SUCCESS) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304096 qdf_mem_free(pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08004097 }
4098
Anurag Chouhanffb21542016-02-17 14:33:03 +05304099 qdf_list_destroy(&hdd_ipa->pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08004100}
4101
4102/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004103 * hdd_ipa_cleanup - IPA cleanup function
4104 * @hdd_ctx: HDD global context
4105 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304106 * Return: QDF_STATUS enumeration
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004107 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304108QDF_STATUS hdd_ipa_cleanup(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004109{
4110 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
4111 int i;
4112 struct hdd_ipa_iface_context *iface_context = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05304113 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004114 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
4115
4116 if (!hdd_ipa_is_enabled(hdd_ctx))
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304117 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004118
4119 if (!hdd_ipa_uc_is_enabled(hdd_ctx)) {
4120 unregister_inetaddr_notifier(&hdd_ipa->ipv4_notifier);
4121 hdd_ipa_teardown_sys_pipe(hdd_ipa);
4122 }
4123
4124 /* Teardown IPA sys_pipe for MCC */
4125 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx))
4126 hdd_ipa_teardown_sys_pipe(hdd_ipa);
4127
4128 hdd_ipa_destroy_rm_resource(hdd_ipa);
4129
4130#ifdef WLAN_OPEN_SOURCE
4131 cancel_work_sync(&hdd_ipa->pm_work);
4132#endif
4133
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304134 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004135
Nirav Shahcbc6d722016-03-01 16:24:53 +05304136 while (((skb = qdf_nbuf_queue_remove(&hdd_ipa->pm_queue_head))
4137 != NULL)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304138 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004139
4140 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
4141 ipa_free_skb(pm_tx_cb->ipa_tx_desc);
4142
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304143 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004144 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304145 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004146
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304147 qdf_spinlock_destroy(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004148
4149 /* destory the interface lock */
4150 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
4151 iface_context = &hdd_ipa->iface_context[i];
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304152 qdf_spinlock_destroy(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004153 }
4154
4155 /* This should never hit but still make sure that there are no pending
4156 * descriptor in IPA hardware
4157 */
4158 if (hdd_ipa->pending_hw_desc_cnt != 0) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304159 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004160 "IPA Pending write done: %d Waiting!",
4161 hdd_ipa->pending_hw_desc_cnt);
4162
4163 for (i = 0; hdd_ipa->pending_hw_desc_cnt != 0 && i < 10; i++) {
4164 usleep_range(100, 100);
4165 }
4166
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304167 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004168 "IPA Pending write done: desc: %d %s(%d)!",
4169 hdd_ipa->pending_hw_desc_cnt,
4170 hdd_ipa->pending_hw_desc_cnt == 0 ? "completed"
4171 : "leak", i);
4172 }
4173 if (hdd_ipa_uc_is_enabled(hdd_ctx)) {
4174 hdd_ipa_uc_rt_debug_deinit(hdd_ctx);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304175 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004176 "%s: Disconnect TX PIPE", __func__);
4177 ipa_disconnect_wdi_pipe(hdd_ipa->tx_pipe_handle);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304178 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004179 "%s: Disconnect RX PIPE", __func__);
4180 ipa_disconnect_wdi_pipe(hdd_ipa->rx_pipe_handle);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304181 qdf_mutex_destroy(&hdd_ipa->event_lock);
4182 qdf_mutex_destroy(&hdd_ipa->ipa_lock);
Yun Parkf19e07d2015-11-20 11:34:27 -08004183 hdd_ipa_cleanup_pending_event(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004184
4185#ifdef WLAN_OPEN_SOURCE
4186 for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) {
4187 cancel_work_sync(&hdd_ipa->uc_op_work[i].work);
4188 hdd_ipa->uc_op_work[i].msg = NULL;
4189 }
4190#endif
4191 }
4192
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304193 qdf_mem_free(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004194 hdd_ctx->hdd_ipa = NULL;
4195
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304196 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004197}
4198#endif /* IPA_OFFLOAD */